Skip to content

Render real homepage stats - #84

Open
VidurShah wants to merge 1 commit into
mainfrom
vidur/home-dynamic-stats
Open

Render real homepage stats#84
VidurShah wants to merge 1 commit into
mainfrom
vidur/home-dynamic-stats

Conversation

@VidurShah

@VidurShah VidurShah commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Problem

The home screen's stat grid rendered hardcoded placeholders — newMessageCount = 12, completedChecklists = 10 / 12, activeReferrals = 11. Only the Calendar tile was wired to real data, so a patient saw three numbers that had nothing to do with their account. Closes #72.

The Checklists and Referrals tiles were straightforward: ChecklistService.streamCurrentUserChecklists and ReferralService.streamCurrentUserReferrals already existed and already scoped to the current user. Unread messages was the one that needed new plumbing, and it came with two constraints:

  • Filtering on both isRead and senderId server-side would need a new composite index. firestore.indexes.json only carries timestamp field overrides for the messages subcollection.
  • Counting alone gives a number that never goes down. Nothing in mobile/lib has ever written isRead — the web console is the only side implementing read receipts today, so an unread badge would have stuck at its first value forever.

Changes

  • lib/services/chat_service.dart — added streamUnreadCount(chatId), which filters server-side on isRead only. That equality-only query is covered by the automatic single-field index; the senderId check runs client-side to avoid needing a composite one. streamCurrentUserUnreadCount() wraps it for callers that don't have a chat id — the chat document id is the uid, so no lookup is needed and a chat that doesn't exist yet simply streams 0. Also added markMessagesRead(docs), which batches isRead: true onto messages the current user didn't send.
  • lib/pages/home_page.dart — dropped the hardcoded fields and added _buildChatCard, _buildChecklistCard and _buildReferralsCard, each following the shape _buildCalendarCard already used (waitingLoading..., error or null → a neutral fallback, else the value). Chat reads No new messages / N new message(s); Checklists counts unchecked items across all non-archived lists and reads All caught up / N item(s) left; Referrals counts every non-deleted referral regardless of status and reads No referrals / N referral(s). The services are held as final fields alongside the existing _eventService so the streams are created once per HomePage rather than on every rebuild.
  • lib/pages/chat_page.dart — one unawaited markMessagesRead call inside _MessagesListState.build, in the existing docs.length != _lastDocCount block that already handles the auto-scroll side effect. It reuses the docs the list has already streamed, so read receipts cost no extra reads, and it can't loop: flipping isRead changes document contents but not the count, so the guard blocks a re-fire.

firestore.rules already permits this write — each side may flip isRead to true on messages they did not send — so nothing under backend/ changed, and neither did firestore.indexes.json or pubspec.yaml.

Notes / follow-ups

  • Read receipts are deliberately best-effort. A failed markMessagesRead is debug-logged and swallowed rather than surfaced — the patient did nothing wrong, and the write retries on the next snapshot anyway.
  • The unread definition assumes the two sides never collide on isRead. The web console only marks messages read where senderId !== user.uid, i.e. patient-to-staff; mobile now marks the reverse. The sets are disjoint, so the single shared flag works — but it is a single flag, and a third participant on a chat would break that assumption.
  • Worth exercising during review: a message sent from mobile must never count toward the patient's own unread, and a referral whose status changes to completed must not change the count while soft-deleting one does.

Replace the hardcoded placeholders in the home screen's stat grid (12 new
messages, 10 of 12 checklists, 11 referrals) with the signed-in patient's own
numbers, so the tiles stop showing figures unrelated to the account.

- Wire the Chat, Checklists and Referrals tiles to StreamBuilders, following
  the shape _buildCalendarCard already used: Chat counts unread messages from
  staff, Checklists counts unchecked items across non-archived lists, and
  Referrals counts every non-deleted referral regardless of status.
- Add ChatService.streamUnreadCount, filtering server-side on isRead only.
  That equality-only query is covered by the automatic single-field index,
  whereas also filtering senderId would need a new composite index, so the
  sender check is done client-side.
- Add ChatService.markMessagesRead and call it when the message list renders,
  so opening the chat actually clears the count. firestore.rules already lets
  each side flip isRead on messages they did not send, so this needs no rules
  or backend change. The write reuses the docs the list already streamed, is
  best-effort, and sits behind the existing doc-count guard so it cannot
  re-trigger itself.

Closes #72
@VidurShah
VidurShah force-pushed the vidur/home-dynamic-stats branch from 3ed17b4 to 9096fba Compare August 19, 2026 20:24
@VidurShah VidurShah changed the title feat(mobile): render real homepage stats Render real homepage stats Aug 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mobile: Homepage updates

1 participant