import Combine import Foundation import Martin import SwiftUI struct ConversationScreen: View { @Environment(\.router) var router @StateObject var conversation: ConversationStore @StateObject var fileStore: FileStore @State private var autoScroll = true @State private var firstIsVisible = true var body: some View { ZStack { // Background color Color.Material.Background.light .ignoresSafeArea() // Content VStack(spacing: 0) { // Header SharedNavigationBar( leftButton: .init( image: Image(systemName: "chevron.left"), action: { router.dismissScreen() } ), centerText: .init(text: L10n.Conversation.title) ) // Msg list let messages = conversation.messages if !messages.isEmpty { ScrollViewReader { proxy in List { ForEach(messages) { message in ConversationMessageRow(message: message) .id(message.id) .onAppear { if message.id == messages.first?.id { firstIsVisible = true autoScroll = true } } .onDisappear { if message.id == messages.first?.id { firstIsVisible = false autoScroll = false } } } .rotationEffect(.degrees(180)) } .rotationEffect(.degrees(180)) .listStyle(.plain) .background(Color.Material.Background.light) .scrollDismissesKeyboard(.immediately) .scrollIndicators(.hidden) .onChange(of: autoScroll) { new in if new, !firstIsVisible { withAnimation { proxy.scrollTo(messages.first?.id, anchor: .top) } } } } } 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.Material.Shape.white) Image(systemName: "arrow.down") .foregroundColor(.Material.Elements.active) } .frame(width: 40, height: 40) .shadow(color: .black.opacity(0.2), radius: 4) .padding(.trailing, 8) .padding(.bottom, 8) } } } } } .environmentObject(conversation) .safeAreaInset(edge: .bottom, spacing: 0) { ConversationTextInput(autoScroll: $autoScroll) .environmentObject(conversation) .environmentObject(fileStore) } } }