cssgunc / cssgunc/CancerLINC-App
Web: chat live listener discards older paginated messages
- Dominant language
- TypeScript
- Stars
- 3
- Forks
- 1
- Avg merge
- 9d 14h
- Merged PRs (30d)
- 4
Description
> **Found by an agent** while planning the chat transcript export feature (#73). It has **not** been independently reproduced by a human — please verify before acting on it.
## Summary
In `web/app/routes/member.tsx`, the live message listener replaces the entire message list with only the newest page, discarding any older pages the user has scrolled up to load.
## Where
`web/app/routes/member.tsx` — the `onSnapshot` handler on the live query:
```ts
const liveQuery = query(
messagesRef,
orderBy("timestamp", "desc"),
limit(PAGE_SIZE) // PAGE_SIZE = 20
);
const unsubscribe = onSnapshot(liveQuery, (snapshot) => {
const mapped = sortMessagesAscending(snapshot.docs.map(mapMessageDoc));
oldestCursorRef.current = snapshot.docs.at(-1) ?? null;
setHasMoreMessages(snapshot.docs.length === PAGE_SIZE);
setMessages(mapped); // <-- overwrites, does not merge
...
});
```
`loadOlderMessages` correctly *prepends* its page:
```ts
setMessages((current) => sortMessagesAscending([...olderMessages, ...current]));
```
...but the next snapshot callback calls `setMessages(mapped)` with only the newest 20 docs, so that merged history is thrown away.
## Expected vs actual
**Expected:** loaded history stays on screen when a new message arrives.
**Actual:** the list snaps back to the newest 20 messages.
## Steps to reproduce (unverified)
1. Open a patient chat with more than 20 messages.
2. Scroll up so `loadOlderMessages` fetches at least one older page.
3. Have the patient (or another social worker) send a message — or trigger any write that the live query observes, including the `isRead` batch update that `markReceivedMessagesRead` performs.
4. The older messages disappear; the view is back to the newest 20.
Step 3 is worth noting: `markReceivedMessagesRead` writes `isRead: true` on received messages, which itself fires the listener. So this may reproduce without any new message at all.
## Possible fix direction
Merge the snapshot into existing state rather than replacing it — dedupe by message id, keep anything older than the snapshot's oldest doc, and stop overwriting `oldestCursorRef` once older pages have been loaded. Care needed so deletions/updates from the snapshot still apply. Not attempted here.
## Notes
- Not fixed in the transcript export work: that feature runs its own independent query, so it does not inherit the bug.
- A refactor extracting this logic into a `useChat` hook is planned; the intent is to move this code **unchanged** and reference this issue in a comment, so the refactor stays behavior-preserving.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by verifying the reported behavior in web/app/routes/member.tsx with a chat containing more than 20 messages, then read the onSnapshot handler alongside loadOlderMessages and markReceivedMessagesRead. Confirm how updates, deletions, message IDs, and oldestCursorRef behave after older pages load. Done means live updates preserve loaded history while keeping the list and pagination state correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100