Skip to content

Chat banner dismiss, calmer scroll, real send state - #85

Open
VidurShah wants to merge 2 commits into
vidur/home-dynamic-statsfrom
vidur/chat-fine-tuning
Open

Chat banner dismiss, calmer scroll, real send state#85
VidurShah wants to merge 2 commits into
vidur/home-dynamic-statsfrom
vidur/chat-fine-tuning

Conversation

@VidurShah

@VidurShah VidurShah commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Problem

Three things the chat page did visibly wrong. Closes #76.

  • The hours banner had no dismiss path at all — no close button, no gesture, nothing. It sat above the message list permanently.
  • Entering the tab animated a flight through the whole conversation. BottomBar uses an AnimatedSwitcher with a changing ValueKey, so ChatPage is destroyed and recreated on every tab switch. _MessagesList remounts with _lastDocCount = 0, the first snapshot arrives, and _scrollToBottom runs a 200ms animated scrollTo from index 0 to the newest message. ScrollablePositionedList animates by rendering two overlapping lists and interpolating between them, which is what read as the wild scroll.
  • The composer cleared before the network call. A failed send left the patient with a snackbar and an empty text box — whatever they'd typed was gone. There was also no progress signal during the send, only the button greying out.

This is stacked on #84 (issue #72) — that one needs to merge first. The branch is cut from vidur/home-dynamic-stats and the base auto-retargets to main once #84 lands, which keeps the diff here chat-only. Both branches touch chat_page.dart, so stacking was cheaper than resolving it twice.

Changes

All in lib/pages/chat_page.dart. No new dependencies — AnimatedSize and GestureDetector ship with the framework.

  • _hoursBanner — now takes an upward fling (onVerticalDragEnd, primaryVelocity < 0, so a downward drag over it does nothing and you can't lose it while trying to read it) and collapses via AnimatedSize at the same 220ms easeOut used elsewhere in the app. Dismissible would be the more idiomatic widget, but it wants a fixed-extent child in a list and ships horizontal-swipe defaults we'd only have to switch back off. A trailing keyboard_arrow_up chevron was added as an affordance since the gesture is otherwise completely invisible.
  • _ChatPageState — added _hoursNoticeDismissed. It resets on every mount, which is exactly the acceptance criterion: because BottomBar rebuilds ChatPage from scratch on each tab switch, the banner is back the next time the user navigates to chat. Deliberately not persisted to storage or hoisted into BottomBarState — either would break that requirement.
  • _MessagesListState_scrollToBottom takes {bool animate = true}, and a _didInitialScroll flag makes the first placement Duration.zero. Messages arriving while you're on the page still animate smoothly, and scrollToMessage (the search jump) is untouched. The flag also resets in didUpdateWidget alongside _lastDocCount when the list is pointed at a different chat.
  • _ChatInputState._send_controller.clear() moved after the await. sendChatMessage is a callable that resolves once its Firestore batch commits, so awaiting it genuinely is "made it into firebase"; the bubble itself lands a beat later via the snapshot listener. The image path has no text to preserve, so it was left alone.
  • _InputButton — gained a loading flag that swaps the icon for an 18px CircularProgressIndicator, matching the pattern already in pending_verification.dart. Both buttons still grey out during a send.
  • _NotificationIconButton (separate commit) — removed. It and its state class were defined but never referenced in build(); the permission prompt is handled by _requestNotificationPermission in initState instead. That also made the firebase_messaging import unused, since AuthorizationStatus was its only consumer in this file.

Notes / follow-ups

  • The scroll fix is the minimal one, per the issue's "if the fix is really difficult dw abt it". Converting BottomBar to an IndexedStack to preserve chat state across tabs would remove the remount entirely and fix this at the root, but it would also break the banner's reappear-every-time requirement and disturb the checklist's unsaved-changes flow. Explicitly out of scope here.
  • The 120ms AnimatedSwitcher cross-fade in bottom_bar.dart still runs concurrently with the scroll. If any residual jitter shows up, that's the next thing to look at — but it means touching bottom_bar.dart, which affects every tab.
  • The chevron is easy to drop if you'd rather keep the banner bare.
  • Worth exercising during review: airplane mode should leave the typed text in the box and retryable, and search-and-tap should still animate and highlight rather than jumping instantly.

Quality-of-life fixes for three things the chat page did visibly wrong: an
undismissable warning banner, a violent scroll on entering the tab, and a
composer that threw away typed text when a send failed.

- Let the hours banner be swiped up to dismiss, collapsing it with
  AnimatedSize. The flag lives on _ChatPageState, which BottomBar rebuilds on
  every tab switch, so the notice reappears the next time the user navigates
  to the chat rather than being persisted. A trailing chevron makes the
  otherwise-invisible gesture discoverable.
- Make the initial scroll placement instant. Entering the tab used to animate
  a 200ms scrollTo from the top of the conversation to the newest message,
  which ScrollablePositionedList renders by interpolating between two
  overlapping lists. Only genuinely new messages animate now, and the search
  jump is untouched.
- Clear the composer only once the sendChatMessage callable returns rather
  than before the network call, and show a spinner in the send button while
  the message is in flight.

Closes #76
@VidurShah
VidurShah force-pushed the vidur/home-dynamic-stats branch from 3ed17b4 to 9096fba Compare August 19, 2026 20:24
@VidurShah
VidurShah force-pushed the vidur/chat-fine-tuning branch from 60d790f to 383e37e Compare August 19, 2026 20:24
@VidurShah VidurShah changed the title feat(mobile): chat banner dismiss, calmer scroll, real send state Chat banner dismiss, calmer scroll, real send state Aug 19, 2026
_NotificationIconButton and its state class were defined but never referenced
in build() — the permission prompt is handled by _requestNotificationPermission
in initState instead. Removing them also makes the firebase_messaging import
unused, since AuthorizationStatus was its only consumer in this file.
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.

1 participant