ios+mac: filter picker keeps a fixed order; an empty online filter says "Nobody is online" (#552)
The funnel's view picker no longer reorders itself around the current
selection — "All chats" then "Online only", always, with the checkmark
marking where you are. And when a filter leaves the list empty, the pane
says so ("Nobody is online" for the online filter, a generic line for
the category toggles) instead of going silently blank.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2 files changed,
+40
−12
ios/LChat/Views/ThreadListView.swift+25 −6ios/LChatMac/NativeChatView.swift+15 −6
diff --git a/ios/LChat/Views/ThreadListView.swift b/ios/LChat/Views/ThreadListView.swift| index 0a7436e..65fdb92 100644 |
| --- a/ios/LChat/Views/ThreadListView.swift |
| +++ b/ios/LChat/Views/ThreadListView.swift |
| @@ -102,6 +102,14 @@ struct ThreadListView: View { |
| } |
| .overlay { if store.loading && store.threads.isEmpty { ProgressView() } } |
| .overlay { if !store.loading && store.threads.isEmpty { empty } } |
| + // The funnel can narrow the list to nothing — say so instead of |
| + // presenting a silently blank pane. |
| + .overlay { |
| + if !store.loading, !store.threads.isEmpty, filtering, visibleThreads.isEmpty, |
| + search.trimmingCharacters(in: .whitespaces).isEmpty { |
| + filteredEmpty |
| + } |
| + } |
| .navigationTitle("Chats") // also backs the chat screen's back button |
| .navigationBarTitleDisplayMode(.inline) |
| .toolbar { |
| @@ -129,13 +137,12 @@ struct ThreadListView: View { |
| // People/Bots/Apps categories. Filled glyph while narrowing. |
| ToolbarItem(placement: .topBarTrailing) { |
| Menu { |
| - // The view you're already in sits first (checked), so an |
| - // accidental tap on the top row re-picks it — a no-op — |
| - // instead of instantly narrowing the list. |
| + // A picker in a fixed order — the checkmark marks the view |
| + // you're in, and the usual accidental tap (the top row, |
| + // while unfiltered) re-picks "All chats", a no-op. |
| Picker("View", selection: $filterOnline) { |
| - ForEach(filterOnline ? [true, false] : [false, true], id: \.self) { on in |
| - Text(on ? "Online only" : "All chats").tag(on) |
| - } |
| + Text("All chats").tag(false) |
| + Text("Online only").tag(true) |
| } |
| .pickerStyle(.inline) |
| Divider() |
| @@ -300,6 +307,18 @@ struct ThreadListView: View { |
| .padding() |
| } |
| + // The funnel emptied the list. Name the online case — it's the one people |
| + // actually hit — and fall back to a generic line for the category toggles. |
| + private var filteredEmpty: some View { |
| + VStack(spacing: 8) { |
| + Image(systemName: "line.3.horizontal.decrease.circle") |
| + .font(.system(size: 36)).foregroundStyle(.secondary) |
| + Text(filterOnline ? "Nobody is online" : "No chats match your filters") |
| + .font(.callout).foregroundStyle(.secondary) |
| + } |
| + .padding() |
| + } |
| + |
| // One instance-wide socket keeps the LIST live (the Mac sidebar's trick): |
| // the hub fans every thread's frames to any member connection, so stored- |
| // message activity — including a brand-new member's first message — |
diff --git a/ios/LChatMac/NativeChatView.swift b/ios/LChatMac/NativeChatView.swift| index 9769469..5e48baf 100644 |
| --- a/ios/LChatMac/NativeChatView.swift |
| +++ b/ios/LChatMac/NativeChatView.swift |
| @@ -835,13 +835,12 @@ struct NativeChatView: View { |
| // List filters (web's funnel beside search): online-only + |
| // the People/Bots/Apps categories. Filled while narrowing. |
| Menu { |
| - // The view you're already in sits first (checked), so an |
| - // accidental click on the top row re-picks it — a no-op — |
| - // instead of instantly narrowing the list. |
| + // A picker in a fixed order — the checkmark marks the view |
| + // you're in, and the usual accidental click (the top row, |
| + // while unfiltered) re-picks "All chats", a no-op. |
| Picker("View", selection: $filterOnline) { |
| - ForEach(filterOnline ? [true, false] : [false, true], id: \.self) { on in |
| - Text(on ? "Online only" : "All chats").tag(on) |
| - } |
| + Text("All chats").tag(false) |
| + Text("Online only").tag(true) |
| } |
| .pickerStyle(.inline) |
| Divider() |
| @@ -1063,6 +1062,16 @@ struct NativeChatView: View { |
| ForEach(visibleThreads) { thread in |
| threadRow(thread) |
| } |
| + // The funnel emptied the list — say so instead of presenting |
| + // a silently blank pane. (Search's empty case is the panel's.) |
| + if visibleThreads.isEmpty, filtering, |
| + search.trimmingCharacters(in: .whitespaces).isEmpty { |
| + Text(filterOnline ? "Nobody is online" : "No chats match your filters") |
| + .font(.system(size: 12)) |
| + .foregroundStyle(.secondary) |
| + .frame(maxWidth: .infinity) |
| + .padding(.vertical, 14) |
| + } |
| } |
| .padding(6) |
| } |