import Combine import Foundation import Martin import SwiftUI struct ConversationScreen: View { @EnvironmentObject var store: AppStore @State private var autoScroll = true var body: some View { ZStack { // Background color Color.Main.backgroundLight .ignoresSafeArea() // Content VStack(spacing: 0) { // Header ConversationHeader() // Msg list let messages = store.state.conversationsState.currentMessages if !messages.isEmpty { ScrollViewReader { proxy in List { ForEach(messages) { message in ConversationMessageRow(message: message) .id(message.id) .onAppear { if message.id == messages.last?.id { autoScroll = true } } .onDisappear { if message.id == messages.last?.id { autoScroll = false } } } } .listStyle(.plain) .background(Color.Main.backgroundLight) .scrollDismissesKeyboard(.immediately) .scrollIndicators(.hidden) .onChange(of: messages) { _ in if autoScroll { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { withAnimation { proxy.scrollTo(messages.last?.id, anchor: .bottom) } } } } .onChange(of: autoScroll) { new in if new { withAnimation { proxy.scrollTo(messages.last?.id, anchor: .bottom) } } } .onAppear { proxy.scrollTo(messages.last?.id, anchor: .bottom) } } } else { Spacer() } } .onTapGesture { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } // Jump to last button if !autoScroll { VStack { Spacer() HStack { Spacer() Button { autoScroll = true } label: { ZStack { Circle() .fill(Color.Main.white) Image(systemName: "arrow.down") .foregroundColor(.Tango.blueLight) } .frame(width: 40, height: 40) .shadow(color: .Main.black.opacity(0.2), radius: 4) .padding(.trailing, 8) .padding(.bottom, 8) } } } } } .safeAreaInset(edge: .bottom, spacing: 0) { ConversationTextInput() } } } // Preview #if DEBUG struct ConversationScreen_Previews: PreviewProvider { static var previews: some View { ConversationScreen() .environmentObject(pStore) } static var pStore: AppStore { let state = pState return AppStore(initialState: state, reducer: AppState.reducer, middlewares: []) } static var pState: AppState { var state = AppState() let acc = "user@test.com" let contact = "some@test.com" state.conversationsState.currentChat = Chat(id: "1", account: acc, participant: contact, type: .chat) state.conversationsState.currentMessages = [ Message( id: "1", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for test sdgdsfg dsfg dsfgdg dsfgdfgsdgsdfgdfg sdfgdsfgdfsg dsfgdsfgsdfg dsfgdfgsdg fgf fgfg sdfsdf sdfsdf sdf sdfsdf sdf sdfsdf sdfsdfsdf sdfsdf ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: true, sentError: false ), Message( id: "2", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for testsdfsdf sdfsdf sdfs sdf sdffsdf sdf sdf sdf sdf sdf sdff sdfffwwe ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false ), Message(id: "3", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: true), Message( id: "4", type: .chat, contentType: .text, from: acc, to: contact, body: "this is for test sdfkjwek jwkjfh jwerf jdfhskjdhf jsdhfjhwefh sjdhfh fsdjhfh sd ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false ), Message(id: "5", type: .chat, contentType: .text, from: contact, to: acc, body: "this is for test sdfjkkeke kekkddjw;; w;edkdjfj l kjwekrjfk wef", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), Message(id: "6", type: .chat, contentType: .text, from: acc, to: contact, body: "this is for testsdf dsdkkekkddn wejkjfj ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), Message( id: "7", type: .chat, contentType: .text, from: acc, to: contact, body: "this is for test sdgdsfg dsfg dsfgdg dsfgdfgsdgsdfgdfg sdfgdsfgdfsg dsfgdsfgsdfg dsfgdfgsdg fgf fgfg sdfsdf sdfsdf sdf sdfsdf sdf sdfsdf sdfsdfsdf sdfsdf ", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false ), Message(id: "8", type: .chat, contentType: .text, from: acc, to: contact, body: "so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), Message(id: "9", type: .chat, contentType: .text, from: contact, to: acc, body: "so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), Message(id: "10", type: .chat, contentType: .text, from: acc, to: contact, body: "so test so test so test", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false), Message(id: "11", type: .chat, contentType: .text, from: contact, to: acc, body: "xD", subject: nil, thread: nil, oobUrl: nil, date: Date(), pending: false, sentError: false) ] state.conversationsState.replyText = "> Some Text here! And if it a long and very long text sdfsadfsadfsafsadfsadfsadfsadfassadfsadfsafsafdsadfsafdsadfsadfas sdf sdf asdf sdfasdfsd sdfasdf sdfsdfdsasdfsdfa dsafsaf" return state } } #endif