import SwiftUI /// The conversation screen for a revealed connection. /// /// This is where two people who met in the real world actually talk. The UI /// is deliberately calm and human — no engagement-baiting, no streaks, no /// algorithms. Just a conversation between two people who chose to meet. struct ChatView: View { @EnvironmentObject private var vm: ChatViewModel @EnvironmentObject private var revealFlow: RevealFlow @State private var input = "" let connection: Connection var body: some View { VStack(spacing: 0) { // Header HStack(spacing: 12) { Circle() .fill(Color.green.opacity(0.2)) .frame(width: 40, height: 40) .overlay(Text(String(connection.displayName.prefix(1)))) VStack(alignment: .leading, spacing: 2) { Text(connection.displayName) .font(.headline) Text("Met in person · \(connection.createdAt, format: .dateTime.month().day())") .font(.caption) .foregroundColor(.secondary) } Spacer() Image(systemName: "lock.fill") .font(.caption) .foregroundColor(.green) .accessibilityLabel("End-to-end encrypted") } .padding() .background(Color(.secondarySystemBackground)) Divider() // Messages ScrollViewReader { proxy in ScrollView { LazyVStack(spacing: 12) { ForEach(vm.messages) { message in MessageBubble(message: message, isMine: message.senderID == "me") } } .padding() } .onChange(of: vm.messages.count) { _, _ in if let last = vm.messages.last { withAnimation { proxy.scrollTo(last.id, anchor: .bottom) } } } } // Input HStack(spacing: 12) { TextField("Say hello…", text: $input, axis: .vertical) .lineLimit(1...4) .padding(10) .background(Color(.secondarySystemBackground)) .clipShape(RoundedRectangle(cornerRadius: 18)) Button { vm.draft = input input = "" Task { await vm.send() } } label: { Image(systemName: "arrow.up.circle.fill") .font(.system(size: 28)) } .disabled(input.trimmingCharacters(in: .whitespaces).isEmpty) } .padding() } .navigationTitle("") .navigationBarTitleDisplayMode(.inline) .onAppear { // Use the E2E session key established at reveal time. vm.start(with: connection, sessionKey: revealFlow.currentSessionKey()) } } } /// A single message bubble. struct MessageBubble: View { @EnvironmentObject private var vm: ChatViewModel let message: ChatMessage let isMine: Bool var body: some View { HStack { if isMine { Spacer() } Text(vm.text(for: message)) .padding(.horizontal, 14) .padding(.vertical, 10) .background( RoundedRectangle(cornerRadius: 18) .fill(isMine ? Color.green : Color(.secondarySystemBackground)) ) .foregroundColor(isMine ? .white : .primary) if !isMine { Spacer() } } } }