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>
2 files changed,
+13
−0
changelog/2026-07-06-mac-dedupe-server.md+3 −0ios/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 |