eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1import SwiftUI
2
3// ๐Ÿค– The native twin of the web's โ‹ฎ โ†’ "Add apps" sheet (shell.js
4// manageGroupApps): every group-capable app with an Add/Remove action. Adding
5// or removing emits a groupapps event into the group's encrypted log โ€” the
6// button state follows the fold (the echo flips it), so what you see is what
7// every member's fold agrees on. Shared by iOS and the Mac.
8public struct GroupAppsSheet: View {
9 @ObservedObject private var model: ChatFeedModel
10 // Observed in its own right: the catalogue can learn an app while the
11 // sheet is open (a member app deployed from another client โ€” the
12 // apps_changed pull), and the model only publishes when the FOLD
13 // changes, not when the registry does.
14 @ObservedObject private var registry: AppRegistry
15 @Environment(\.dismiss) private var dismiss
16 @State private var error: String?
17
18 public init(model: ChatFeedModel) {
19 self.model = model
20 self.registry = model.registry
21 }
22
23 public var body: some View {
24 VStack(spacing: 0) {
25 HStack {
26 Text("Apps")
27 .font(.headline)
28 Spacer()
29 Button("Done") { dismiss() }
30 }
31 .padding(.horizontal, 16)
32 .padding(.vertical, 12)
33 Divider()
34 List {
35 Section {
36 // Every group-capable app from the registry (places โˆ‹
37 // "chat", listed) โ€” custom and member apps included, so
38 // the sheet matches the web's, not a hand-kept subset.
39 ForEach(registry.groupApps) { app in
40 row(app)
41 }
42 } footer: {
43 Text("An app added here is shared with everyone in this chat โ€” one app, one set of data, visible to all members.")
44 .font(.footnote)
45 .foregroundStyle(.secondary)
46 }
47 if let error {
48 Section { Text(error).foregroundStyle(.red) }
49 }
50 }
51 #if os(macOS)
52 .listStyle(.inset)
53 #else
54 .listStyle(.insetGrouped)
55 #endif
56 }
57 #if os(macOS)
58 .frame(width: 340, height: 400)
59 #endif
60 }
61
62 private func row(_ app: AppManifest) -> some View {
63 let added = model.groupApps.contains { $0.id == app.name }
64 return HStack(spacing: 12) {
65 Text(app.glyph)
66 .font(.system(size: 17))
67 .frame(width: 32, height: 32)
68 .background(Circle().fill(.primary.opacity(0.06)))
69 Text(app.title)
70 Spacer()
71 Button(added ? "Remove" : "Add") {
72 error = nil
73 if !model.sendGroupAppEvent(add: !added, app: app.id) {
74 error = "You're offline โ€” try again in a moment."
75 }
76 }
77 .buttonStyle(.bordered)
78 .tint(added ? .secondary : .accentColor)
79 }
80 }
81}
82