Add minimizable phone call UI with home screen banner#6066
Conversation
New widgets that show call status, transcript snippet, and inline controls when the user minimizes an active phone call. ActiveCallTopBar provides a slim green bar on non-home tabs to return to the full call screen.
Replace PopScope lock with a back button that lets users minimize the call and return to the home screen. Uses popUntil to skip the intermediate PhoneCallsPage.
The ActiveCallBanner replaces this widget during calls so both don't show simultaneously.
Green bar at the top of Tasks, Memories, and Apps tabs lets users tap to return to the full call screen from any tab.
Shows a snackbar with 'A call is already in progress' if the user tries to dial while a call is in connecting/ringing/active state.
Greptile SummaryThis PR adds a minimizable phone call UI with a persistent home-screen banner ( Confidence Score: 4/5Safe to merge after fixing the tap-absorption bug in _CompactControlButton; all other changes are well-structured and correct. The feature is well-implemented overall — state management is sound, the mounted guard on auto-pop is correct, and duplicate-call prevention is properly placed. One targeted P1 fix remains: _CompactControlButton always registers a non-null GestureDetector.onTap even in the disabled state, which causes disabled buttons to silently absorb taps and block banner navigation. Once that one-liner is addressed, the PR is ready to merge. app/lib/pages/phone_calls/active_call_banner.dart — _CompactControlButton.build around line 268 Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant PhoneCallsPage
participant ActiveCallPage
participant PhoneCallProvider
participant ActiveCallBanner
participant ActiveCallTopBar
User->>PhoneCallsPage: Tap Call button
PhoneCallsPage->>PhoneCallProvider: check callState != idle
alt Call already in progress
PhoneCallProvider-->>PhoneCallsPage: callState != idle
PhoneCallsPage-->>User: Snackbar A call is already in progress
else No call in progress
PhoneCallsPage->>PhoneCallProvider: startCall(number)
PhoneCallProvider-->>PhoneCallsPage: callState = connecting
PhoneCallsPage->>ActiveCallPage: Navigator.push
User->>ActiveCallPage: Tap back arrow (minimize)
ActiveCallPage->>ActiveCallPage: Navigator.popUntil(isFirst)
Note over ActiveCallBanner: Banner appears on home tab
Note over ActiveCallTopBar: Top bar appears on other tabs
User->>ActiveCallBanner: Tap banner
ActiveCallBanner->>ActiveCallPage: Navigator.push (new instance)
PhoneCallProvider-->>ActiveCallPage: callState = ended
ActiveCallPage->>ActiveCallPage: Future.delayed(2s) pop
Note over ActiveCallBanner,ActiveCallTopBar: Banner/TopBar disappear (callState = idle)
end
Reviews (1): Last reviewed commit: "Block placing a second call while one is..." | Re-trigger Greptile |
| return GestureDetector( | ||
| onTap: () { | ||
| HapticFeedback.mediumImpact(); | ||
| onTap?.call(); |
There was a problem hiding this comment.
Disabled buttons still absorb taps and fire haptics
_CompactControlButton's GestureDetector.onTap is always set to a non-null closure (it just calls HapticFeedback.mediumImpact(); onTap?.call();), even when the outer onTap parameter is null (i.e., during the connecting state). Because Flutter gesture arena disambiguation gives the inner GestureDetector priority, tapping on the mute or speaker button while they are disabled will:
- Fire a haptic impact (misleading — the button didn't do anything).
- Consume the tap event, so the outer
ActiveCallBanner'sonTap(which navigates to the full call page) never fires.
The fix is to pass null as onTap to the GestureDetector when the button is disabled:
| return GestureDetector( | |
| onTap: () { | |
| HapticFeedback.mediumImpact(); | |
| onTap?.call(); | |
| return GestureDetector( | |
| onTap: onTap == null | |
| ? null | |
| : () { | |
| HapticFeedback.mediumImpact(); | |
| onTap!(); | |
| }, |
| class _TranscriptSnippet extends StatelessWidget { | ||
| final String text; | ||
| final bool isUser; | ||
| final String speakerLabel; | ||
|
|
||
| const _TranscriptSnippet({required this.text, required this.isUser, required this.speakerLabel}); |
There was a problem hiding this comment.
isUser field accepted but never used
_TranscriptSnippet receives an isUser flag but the widget doesn't use it — both user and remote transcripts render identically. In the full _LiveTranscriptView this flag is used to align bubbles left/right, so the banner snippet visually loses that distinction. Either use it (e.g. a subtle tint / border difference) or remove it to avoid dead parameters.
| class _TranscriptSnippet extends StatelessWidget { | |
| final String text; | |
| final bool isUser; | |
| final String speakerLabel; | |
| const _TranscriptSnippet({required this.text, required this.isUser, required this.speakerLabel}); | |
| const _TranscriptSnippet({required this.text, required this.speakerLabel}); |
…param Address Greptile review: pass null onTap to GestureDetector when button is disabled so taps propagate to the banner, and remove dead isUser field from _TranscriptSnippet.
…6066) ## Summary - **Minimizable call screen**: Added a back button to `ActiveCallPage` so users can navigate back to the home screen while a call is in progress (previously the screen was locked with `PopScope`) - **Home screen call banner**: New `ActiveCallBanner` widget shows contact info, live transcript snippet, and inline call controls (mute, speaker, end call) on the conversations page — tap to expand back to full call view - **Global call indicator**: Slim green `ActiveCallTopBar` bar on non-home tabs (Tasks, Memories, Apps) to return to the call - **UI cleanup during calls**: Hides the recording capture widget and the mic record button in the bottom nav bar while a call is active - **Duplicate call prevention**: Blocks placing a second call with a "A call is already in progress" snackbar ## Test plan - [x] Start a phone call, verify the back arrow appears in the top-left of the call screen - [x] Tap the back arrow — should go directly to home screen (not the dialer page) - [x] Verify the `ActiveCallBanner` appears at the top of the conversations list with contact name, duration, transcript, and controls - [x] Test mute/speaker/end call controls on the banner work correctly - [x] Switch to Tasks/Memories/Apps tabs — verify green top bar appears with contact name and duration - [x] Tap the banner or green bar — should navigate back to the full call screen - [x] Verify the "Continue Recording" widget and mic button are hidden during the call - [x] While on a call, tap the phone icon and try to call another number — should show "A call is already in progress" toast - [x] End the call — verify banner/top bar disappear and recording widget/mic button return 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summary
ActiveCallPageso users can navigate back to the home screen while a call is in progress (previously the screen was locked withPopScope)ActiveCallBannerwidget shows contact info, live transcript snippet, and inline call controls (mute, speaker, end call) on the conversations page — tap to expand back to full call viewActiveCallTopBarbar on non-home tabs (Tasks, Memories, Apps) to return to the callTest plan
ActiveCallBannerappears at the top of the conversations list with contact name, duration, transcript, and controls🤖 Generated with Claude Code