eleven / ocho Public

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

Plain git — no account needed to clone.

Download

Download this file

1import LChatCore
2import SwiftUI
3
4// 🤖 The iOS receiver's confirm card (web→app handoff — the Mac
5// AddFromLinkSheet's sibling): an invite link that arrived from OUTSIDE the
6// app (the join page's "Open in the app" scheme, or a universal link on an
7// instance subdomain) pre-fills this sheet — it never auto-joins; the Add
8// tap is the confirming act for any URL that came from elsewhere.
9// Deliberately NO editable field: iOS has no paste-a-link (Paul's call —
10// joining lives on the web), so only an externally-arrived, already-validated
11// link can reach this card. SpaceClaim handles all three link shapes
12// (member/setup invites, DM invites, add-device links) with no webview; the
13// secret in the #fragment never reaches any server.
14struct AddFromLinkConfirm: View {
15 @EnvironmentObject var app: AppState
16 @Environment(\.dismiss) private var dismiss
17 let link: String
18 /// Called after the claim persisted (app.add ran) — RootView selects the
19 /// new space and drops the pending link.
20 let onAdded: (SavedInstance) -> Void
21 @State private var busy = false
22 @State private var error: String?
23
24 private var host: String {
25 parseJoinLink(link).flatMap { URL(string: $0.origin)?.host } ?? ""
26 }
27
28 var body: some View {
29 VStack(spacing: 14) {
30 Image(systemName: "person.crop.circle.badge.plus")
31 .font(.system(size: 44))
32 .foregroundStyle(.tint)
33 .accessibilityHidden(true)
34 Text("Add this space?")
35 .font(.title3.bold())
36 Text("You opened an invite for \(host). It joins this \(DeviceName.current) and syncs to your Eleven account.")
37 .font(.callout)
38 .foregroundStyle(.secondary)
39 .multilineTextAlignment(.center)
40 if let error {
41 Text(error)
42 .font(.footnote)
43 .foregroundStyle(.red)
44 .multilineTextAlignment(.center)
45 }
46 Button(action: add) {
47 HStack(spacing: 6) {
48 if busy { ProgressView().controlSize(.small) }
49 Text(busy ? "Joining…" : "Add Space")
50 }
51 .frame(maxWidth: .infinity)
52 }
53 .buttonStyle(.borderedProminent)
54 .controlSize(.large)
55 .disabled(busy)
56 Button("Cancel") { dismiss() }
57 .disabled(busy)
58 }
59 .padding(24)
60 .presentationDetents([.medium])
61 }
62
63 private func add() {
64 guard !busy else { return }
65 busy = true
66 error = nil
67 Task { @MainActor in
68 do {
69 // Carry the name you already go by; an add-device link's own
70 // "~name" (or "Me") wins inside the claim when this is empty.
71 let screenName = app.instances.first(where: { !$0.isArchived })?.screenName
72 ?? app.instances.first?.screenName ?? ""
73 let inst = try await SpaceClaim.claim(setupLink: link, screenName: screenName)
74 app.add(inst) // Keychain + list + vault push
75 onAdded(inst) // RootView: select + dismiss the pending link
76 } catch {
77 self.error = (error as? LocalizedError)?.errorDescription
78 ?? "Couldn't add that space."
79 busy = false
80 }
81 }
82 }
83}
84