eleven / messenger Public

mac: the join page's "Open in the app" lands — elevenmessenger://add pre-fills Add-from-link (#1173)

The join page has always fired elevenmessenger://add?url=<link> and the
Mac dropped it (onOpenURL accepted only the home-session sign-in
callback). Route it: validate with parseJoinLink, stash on MacAppState,
and PRE-FILL the existing Add-from-link sheet — the sheet is the
confirming click (a URL arriving from outside must never join
silently), and SpaceClaim already handles all three link shapes
natively, add-device included. The fragment secret rides encoded inside
the scheme URL's query value and never reaches any server.

First slice of web↔app handoff; the web-side "Open in the Eleven app"
for signed-in members (the "Continue on web" regret path) comes next.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 56184a024625a0c219a7025cca0eacbe7949d164 parent 932bfe1
3 files changed, +41 −2
  • ios/LChatMac/AddFromLinkSheet.swift +15 −0
  • ios/LChatMac/LChatMacApp.swift +21 −2
  • ios/LChatMac/RootView.swift +5 −0
diff --git a/ios/LChatMac/AddFromLinkSheet.swift b/ios/LChatMac/AddFromLinkSheet.swift
index fa4a39b..be2597f 100644
--- a/ios/LChatMac/AddFromLinkSheet.swift
+++ b/ios/LChatMac/AddFromLinkSheet.swift
@@ -14,6 +14,15 @@ struct AddFromLinkSheet: View {
@State private var busy = false
@State private var error: String?
+ /// The browser handoff's pre-fill (onOpenURL stashes the validated link;
+ /// see LChatMacApp): consumed once, so a later manual open starts blank.
+ private func consumePendingLink() {
+ if let pending = app.pendingAddLink {
+ link = pending
+ app.pendingAddLink = nil
+ }
+ }
+
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Add a space from a link")
@@ -52,6 +61,12 @@ struct AddFromLinkSheet: View {
}
.padding(20)
.frame(width: 440)
+ .onAppear(perform: consumePendingLink)
+ // A handoff can land while the sheet is already up (a second click
+ // in the browser) — take the newest link in place.
+ .onChange(of: app.pendingAddLink) { _, pending in
+ if pending != nil, !busy { consumePendingLink() }
+ }
}
private func add() {
diff --git a/ios/LChatMac/LChatMacApp.swift b/ios/LChatMac/LChatMacApp.swift
index b769346..d032fbf 100644
--- a/ios/LChatMac/LChatMacApp.swift
+++ b/ios/LChatMac/LChatMacApp.swift
@@ -51,10 +51,24 @@ struct LChatMacApp: App {
.onAppear { appDelegate.appState = app }
.onOpenURL { url in
// The browser sign-in handoff (elevenmessenger://
- // home-session#…) — the ONLY link the Mac app accepts now:
- // joining happens on the web, entry here is by signing in.
+ // home-session#…): claimed first, always.
if MacHomeSignIn.handleCallback(url, into: app) {
NSApp.activate(ignoringOtherApps: true)
+ return
+ }
+ // The join page's "Open in the app" (issue #1089's
+ // handoff cousin): elevenmessenger://add?url=<join or
+ // add-device link, fragment riding encoded inside the
+ // query value — the secret never reaches any server>.
+ // Validated here, then PRE-FILLS the Add-from-link sheet
+ // rather than joining — one confirming click, with the
+ // link visible, for any URL that arrived from outside.
+ if url.host == "add",
+ let link = URLComponents(url: url, resolvingAgainstBaseURL: false)?
+ .queryItems?.first(where: { $0.name == "url" })?.value,
+ parseJoinLink(link) != nil {
+ app.pendingAddLink = link
+ NSApp.activate(ignoringOtherApps: true)
}
}
}
@@ -247,6 +261,11 @@ final class MacAppState: ObservableObject {
@Published var createSpaceTick = 0
// Spaces → Add from link…: RootView owns the paste sheet.
@Published var addFromLinkTick = 0
+ // A join/add-device link handed over by the browser (the join page's
+ // "Open in the app" → elevenmessenger://add?url=…). Pre-fills the
+ // Add-from-link sheet — the sheet IS the confirming click; a URL arriving
+ // from outside the app must never join silently. Consumed on sheet appear.
+ @Published var pendingAddLink: String?
func requestCreateSpace() { createSpaceTick += 1 }
func requestAddFromLink() { addFromLinkTick += 1 }
diff --git a/ios/LChatMac/RootView.swift b/ios/LChatMac/RootView.swift
index 3dc8fbc..dc7722b 100644
--- a/ios/LChatMac/RootView.swift
+++ b/ios/LChatMac/RootView.swift
@@ -98,6 +98,11 @@ struct RootView: View {
.onChange(of: app.createSpaceTick) { _, _ in createModel = makeCreateModel() }
// Spaces ▸ Add from link… — the native paste-a-link claim.
.onChange(of: app.addFromLinkTick) { _, _ in showAddFromLink = true }
+ // …and the browser handoff pre-fills the same sheet (onOpenURL's
+ // elevenmessenger://add — the sheet is the confirming click).
+ .onChange(of: app.pendingAddLink) { _, link in
+ if link != nil { showAddFromLink = true }
+ }
.sheet(isPresented: $showAddFromLink) { AddFromLinkSheet() }
// ⌘K quick-switch palette, drawn over the whole window (above the web feed).
.overlay { if app.showPalette { CommandPalette() } }