docs: chats outside the loaded list: return paths, iOS queued count, counter drift

This commit is contained in:
Narasimha-sc committed 2026-10-19 15:52:59 +00:00
1 parent 5b0f3a8a63
commit e266ee620e
5 files changed
+18 -12

No files matched your search

+2 -2
View File
@@ -40,13 +40,13 @@ When `ChatModel.chatInitialized` transitions to `true`, the chat list appears fu
### GAP: Chat list is silently limited to 5000 chats
**Source:** [spec/client/chat-list.md](../spec/client/chat-list.md)
`apiGetChats` sends `/_get chats <userId> pcc=on` with no pagination, so the core applies its default of `PTLast maxChats` with `maxChats = 5000`. On a profile with more chats than that, the oldest are not shown, not searched (chat-list search is in-memory) and missing from the in-memory tag counts, with no indication to the user. They reappear only when they receive a message, or once a reload finds them inside the window. Such a chat is listed with an unread count of 1 for the message that brought it back, while opening it keeps the listed stats, so reading its older unread items one by one takes its count below zero until the next reload.
`apiGetChats` sends `/_get chats <userId> pcc=on` with no pagination, so the core applies its default of `PTLast maxChats` with `maxChats = 5000`. On a profile with more chats than that, the oldest are not shown, not searched (chat-list search is in-memory) and missing from the in-memory tag counts, with no indication to the user. They reappear only when an event for them arrives (a message, or a contact or group update), or once a reload finds them inside the window. Such a chat is listed with an unread count of 1 for the message that brought it back, while opening it keeps the listed stats, so reading its older unread items one by one takes its count below zero until the next reload.
**REC:** Paginate the chat list, or raise the count and surface the limit in the UI.
### GAP: Unread counters are not decremented when a chat is removed
**Source:** [spec/state.md](../spec/state.md)
`removeChat` removes preset tags and wallpaper files but decrements neither the profile unread counter nor the per-tag unread counts, so deleting a chat that had unread messages leaves both high until the next profile switch.
`removeChat` removes preset tags and wallpaper files but decrements neither the profile unread counter nor the per-tag unread counts, so deleting a chat that had unread messages leaves the profile counter high until the users list is next read (opening the user picker, a profile switch, a restart) and the tag counts until the next profile switch or restart.
**REC:** Decrement both in `removeChat`, as the read paths do.
+4 -3
View File
@@ -386,8 +386,8 @@ unread state without a matching counter update.
### Impact
The unread badge on the profile and on tags can be too high (or, in the secondary-context case, negative)
until the user switches profile or restarts. A chat that is inside the loaded window has its own count
The unread badge on the profile and on tags can be too high (in the secondary-context case the chat's own
entry in that context goes negative, which `ChatView` does not draw) until the user switches profile or restarts. A chat that is inside the loaded window has its own count
corrected by the next chat-list reload; a chat outside it (GAP-12) does not.
### Recommendation
@@ -448,7 +448,8 @@ updated.
On a profile with more than 5000 chats the oldest are not shown, not searched (chat-list search is
in-memory) and missing from the in-memory tag counts, with no indication to the user. They reappear only
when they receive a message or after the list is reloaded with them inside the window.
when an event for them arrives (a message, or a contact or group update) or after the list is reloaded
with them inside the window.
### Recommendation
@@ -73,7 +73,7 @@ Display logic:
- User tags show with emoji or label icon; long-press opens `TagsDropdownMenu` (edit, delete, change order)
- "+" button at end opens `TagListEditor` for creating new tags
- Only the most recently active 5000 chats are loaded ([GAP-12]), so a chat outside that set is absent
from the list until it receives a message. Such a chat is then added counting that message as unread,
from the list until an event for it arrives, such as a message. Such a chat is then added counting that message as unread,
so it carries an unread badge, and appears under the Unread filter on the same terms as any other chat
-- a muted chat does not, and a mentions-only group only for a mention. Its full count is shown once
the list is reloaded
+1 -1
View File
@@ -146,7 +146,7 @@ The `filteredChats` function (line ~1474) applies filters in this order:
- `UserTag`: Matches chats whose `chatTags` contain the tag ID.
- `Unread`: Matches chats with `unreadCount > 0` or `unreadChat == true`. These come from the
in-memory list only, which holds at most 5000 chats ([GAP-12]); a chat created from a received
event is counted as having that one item unread until the list is reloaded.
event is counted as having that one item unread until the chat is opened or the list is reloaded.
### Search Bar
@@ -104,7 +104,7 @@ message finds it and increments 0 to 1, so the chat becomes visible again. What
invisible is the set of chats that received exactly one message since the list was
loaded.
Any reload repairs the value, because `/_get chats` returns the database's counts, and
Any reload repairs the value of a chat it loads, because `/_get chats` returns the database's counts, and
by then the chat's `chat_ts` is recent enough to be inside the 5000. Android reloads on
`Lifecycle.Event.ON_START` when the chat is running (`SimplexApp.kt`) and iOS when the
app becomes active after being suspended (`SimpleXApp.swift`), so on mobile the wrong
@@ -187,7 +187,8 @@ separate change.
- **The 5000 limit is not addressed.** With more chats than that, the UI still silently
loads only the most recently active 5000: the rest are not shown, not searched, and
missing from the in-memory tag counts, until one of them receives a message. Recorded
missing from the in-memory tag counts, until an event for one of them arrives (a message,
or a contact or group update). Recorded
as GAP-12.
- **`addPresetChatTags` is not called** for the newly added chat, as it is not today, so
the preset tag counts stay low by one for such a chat until they are recomputed —
@@ -207,14 +208,18 @@ separate change.
chat that has unread messages leaves the profile counter and `unreadTags` high, because
`removeChat` decrements neither — it removes the chat's preset tags, but not those two;
a cleared mention can leave `unreadMentions` behind. Both are recorded as GAP-10.
Before this change such a chat contributed nothing to either counter, so the drift is
newly reachable here even though the defect is not new.
Before this change this branch added nothing to either counter, so the tag drift is
newly reachable here and the profile-counter drift grows by the arriving item, even
though the defect is not new.
- **A message that arrives while the profile or remote host is being switched can be
counted twice.** `changeActiveUser_` refills `users` from `listUsers` and only then
reloads the chat list, so during the reload every chat id misses in the stale list and
takes this branch. If the core had already committed the item before `listUsers` ran,
the `+1` is a second count. Master was inert here only because it dropped the count
entirely.
entirely. On iOS the same can happen after any reload: the `+1` queued in
`UnreadCollector` is not cleared when the list is replaced, so if the reload lands
within that second the chat comes back with the item already counted and the queued
`+1` is added on top.
- **Marking such a chat read from the list leaves the profile counter high.** The count
shown is a floor, so `markChatItemsRead` decrements by it while `apiChatRead` clears
every unread item in the chat: a chat with older unread messages, marked read from the