Gitfed
bastien-mrq/bt-message / btmessage / Views / ChatView.swift
// ChatView.swift

import SwiftUI

struct ChatView: View {
    @EnvironmentObject var appState: AppState
    let conversationID: String
    let title: String

    @State private var inputText = ""

    var messages: [ChatMessage] {
        appState.messages(for: conversationID)
    }

    var body: some View {
        VStack(spacing: 0) {
            // Badge de sécurité
            SecurityBadge(conversationID: conversationID)
                .environmentObject(appState)

            Divider()

            // Liste des messages
            ScrollViewReader { proxy in
                ScrollView {
                    LazyVStack(spacing: 4) {
                        ForEach(messages) { msg in
                            MessageBubble(message: msg)
                                .id(msg.id)
                        }
                    }
                    .padding(.horizontal)
                    .padding(.vertical, 8)
                }
                .onAppear { scrollToBottom(proxy: proxy) }
                .onChange(of: messages.count) { _ in scrollToBottom(proxy: proxy) }
            }

            Divider()

            // Barre de saisie
            HStack(spacing: 8) {
                TextField("Message", text: $inputText, axis: .vertical)
                    .textFieldStyle(.roundedBorder)
                    .lineLimit(1...5)
                    .onSubmit { send() }

                Button(action: send) {
                    Image(systemName: "paperplane.fill")
                        .foregroundColor(inputText.isEmpty ? .gray : .blue)
                }
                .disabled(inputText.isEmpty)
            }
            .padding(.horizontal)
            .padding(.vertical, 8)
            .background(Color(.systemBackground))
        }
        .navigationTitle(title)
        .navigationBarTitleDisplayMode(.inline)
    }

    private func send() {
        let text = inputText.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !text.isEmpty else { return }
        inputText = ""
        appState.send(text: text, to: conversationID)
    }

    private func scrollToBottom(proxy: ScrollViewProxy) {
        if let last = messages.last {
            withAnimation { proxy.scrollTo(last.id, anchor: .bottom) }
        }
    }
}

// MARK: - Security Badge

private struct SecurityBadge: View {
    @EnvironmentObject var appState: AppState
    let conversationID: String

    var body: some View {
        HStack(spacing: 6) {
            if appState.isGroup(conversationID) {
                Image(systemName: "lock.fill")
                    .font(.caption)
                    .foregroundColor(.blue)
                Text("Groupe · AES-256-GCM")
                    .font(.caption2)
                    .foregroundColor(.secondary)
            } else {
                let peer = appState.peers.first(where: { $0.id == conversationID })
                Image(systemName: peer?.isKeyKnown == true ? "lock.fill" : "lock.open")
                    .font(.caption)
                    .foregroundColor(peer?.isKeyKnown == true ? .green : .orange)
                Text(peer?.isKeyKnown == true
                     ? "XWing (ML-KEM-768 + X25519) · AES-256-GCM"
                     : "Échange de clé en cours…")
                    .font(.caption2)
                    .foregroundColor(.secondary)
            }
        }
        .padding(.vertical, 4)
    }
}