eleven / messenger Public

mac: connecting a server you're already on opens it instead of duplicating (#276)

add(url:) already de-duped by invite token (re-adding the same link
re-selects), but a DIFFERENT link to the same origin appended a second
SavedChat — and since the webview's keys are per-origin, the twin just
mirrored the identical session while cluttering the rail and the ⌘K
palette ("Switch to Test Server" twice). Now a matching origin re-selects
the existing entry; if that entry never got a session (its old invite may
be spent), it adopts the fresh link so the join can still succeed — the
webview only replays a join link when the origin holds no session, so a
working login is never disturbed.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 3004aa3a9aa6720e81fb7d63533ad58432eed01c parent a1c7844
2 files changed, +13 −0
  • changelog/2026-07-06-mac-dedupe-server.md +3 −0
  • ios/LChatMac/LChatMacApp.swift +10 −0
diff --git a/changelog/2026-07-06-mac-dedupe-server.md b/changelog/2026-07-06-mac-dedupe-server.md
new file mode 100644
index 0000000..101b8d6
--- /dev/null
+++ b/changelog/2026-07-06-mac-dedupe-server.md
@@ -0,0 +1,3 @@
+🤖 On the Mac app, connecting to a server you're already on now just opens the
+one you have — no more twin icons in the rail (or twin "Switch to" rows in ⌘K)
+from joining the same place twice.
diff --git a/ios/LChatMac/LChatMacApp.swift b/ios/LChatMac/LChatMacApp.swift
index d6f3de0..7d77da7 100644
--- a/ios/LChatMac/LChatMacApp.swift
+++ b/ios/LChatMac/LChatMacApp.swift
@@ -181,6 +181,16 @@ final class MacAppState: ObservableObject {
// De-dupe by token so re-adding an instance just re-selects it.
if let i = chats.firstIndex(where: { $0.token != nil && $0.token == chat.token }) {
selection = chats[i].id
+ } else if let origin = chat.origin, let i = chats.firstIndex(where: { $0.origin == origin }) {
+ // Same server, different link: you're already connected, so open what
+ // you have instead of adding a second copy — the webview's keys are
+ // per-origin, so a duplicate would just mirror the identical session
+ // (and clutter the rail + ⌘K with twins). If the saved entry never got
+ // a session (its old invite may be spent), adopt the fresh link so the
+ // join can succeed — the webview only replays a join link when the
+ // origin holds no session, so a working login is never disturbed.
+ if chats[i].session == nil { chats[i].url = url }
+ selection = chats[i].id
} else {
chats.append(chat)
selection = chat.id