oficina / meet Public

Clone
git clone https://amadan.net/oficina/meet

Plain git — no account needed to clone.

Download

Download this file

1import SwiftUI
2
3/// Shared native chat history and composer. History stays visible while the
4/// signaling stream reconnects; only sending depends on current admission.
5@MainActor public struct ChatView: View {
6 @ObservedObject private var call: Call
7 @StateObject private var draft: ChatDraft
8
9 public init(call: Call, draft: ChatDraft? = nil) {
10 self.call = call
11 _draft = StateObject(wrappedValue: draft ?? ChatDraft())
12 }
13
14 public var body: some View {
15 VStack(alignment: .leading, spacing: 12) {
16 ScrollViewReader { proxy in
17 ScrollView {
18 VStack(alignment: .leading, spacing: 8) {
19 if call.chat.isEmpty {
20 Text("Messages sent during this call appear here.")
21 .foregroundStyle(.secondary)
22 }
23 ForEach(Array(call.chat.enumerated()), id: \.offset) { _, line in
24 Text(line).textSelection(.enabled)
25 .padding(10).frame(maxWidth: .infinity, alignment: .leading)
26 .background(.quaternary.opacity(0.5), in: RoundedRectangle(cornerRadius: 10))
27 }
28 Color.clear.frame(height: 1).id("chat-bottom")
29 }.frame(maxWidth: .infinity, alignment: .leading)
30 }
31 .accessibilityIdentifier("chatHistory")
32 .defaultScrollAnchor(.bottom)
33 .onAppear { proxy.scrollTo("chat-bottom", anchor: .bottom) }
34 .onChange(of: call.chat) { _, _ in
35 proxy.scrollTo("chat-bottom", anchor: .bottom)
36 }
37 }
38 if call.reconnecting {
39 Text("Reconnecting. You can send again when the call reconnects.")
40 .font(.footnote).foregroundStyle(.secondary)
41 }
42 HStack {
43 TextField("Message to everyone", text: $draft.text)
44 .textFieldStyle(.roundedBorder)
45 .accessibilityIdentifier("chatComposer")
46 .onSubmit(send)
47 Button("Send", systemImage: "arrow.up.circle.fill", action: send)
48 .buttonStyle(.borderedProminent)
49 .disabled(draft.text.isEmpty || call.state?.phase != "admitted")
50 }
51 }
52 }
53
54 private func send() {
55 guard call.state?.phase == "admitted" else { return }
56 if call.sendChat(draft.text) { draft.text = "" }
57 }
58}
59