eleven / messenger Public

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

Plain git — no account needed to clone.

Download

Download this file

1import Foundation
2
3// 🤖 Resolves a tapped notification's thread id to the space that owns it.
4// The tap route used to hang everything on one source — the SharedThreadKeys
5// entry the ThreadStore folds park — and bailed SILENTLY when it was missing
6// or its origin no longer matched a saved instance (stale park, re-added
7// space, wiped keychain): the app cold-launched to the chat list and the tap
8// just… did nothing (the 2026-07-28 report). Resolution now falls back down
9// the same ladder the data actually lives in, and the caller can always tell
10// why a tap still couldn't route.
11public enum NotificationRoute {
12 /// Where a resolution came from — the caller logs it so a stranded tap in
13 /// the field names its failing rung instead of vanishing.
14 public enum Resolution {
15 case parkedKey(SavedInstance) // SharedThreadKeys entry matched
16 case snapshot(SavedInstance) // found in a space's thread snapshot
17 case onlySpace(SavedInstance) // one signed-in space — must be it
18 case unresolved
19 }
20
21 /// The production ladder. `entryOrigin` and `snapshotHasThread` are seams
22 /// so the ladder's order and edge cases stay unit-testable without a
23 /// keychain (ThreadStoreTests pattern).
24 public static func resolve(threadID: String,
25 instances: [SavedInstance],
26 entryOrigin: (String) -> String? = {
27 SharedThreadKeys.lookup(threadID: $0)?.origin
28 },
29 snapshotHasThread: (String, String) -> Bool = { origin, thread in
30 ThreadListSnapshot.load(origin: origin)?
31 .rows.contains { $0.id == thread } ?? false
32 }) -> Resolution {
33 if let origin = entryOrigin(threadID),
34 let inst = instances.first(where: { $0.origin == origin }) {
35 return .parkedKey(inst)
36 }
37 // Stale or missing park: each space's last folded thread list also
38 // knows its thread ids (and survives the park being wiped).
39 if let inst = instances.first(where: { snapshotHasThread($0.origin, threadID) }) {
40 return .snapshot(inst)
41 }
42 // One signed-in space — the thread can only be there. A genuinely new
43 // thread resolves inside the chat screen's own hydrate + refresh once
44 // pushed; that beats dropping the tap on the floor.
45 let active = instances.filter { !$0.isArchived }
46 if active.count == 1 { return .onlySpace(active[0]) }
47 if instances.count == 1 { return .onlySpace(instances[0]) }
48 return .unresolved
49 }
50}
51