Feature/mobile UI improvements#3748
Conversation
aaravgarg
commented
Dec 13, 2025
- 🔍 Quick search button in top bar
- 💬 Floating chat button for fast access
- 🔇 Mute button during live recordings on the transcript page.
- 📱 Cleaner, more compact conversation cards
- ⚡ Smoother animations and haptic feedback
- 🎨 Polished buttons and visual refinements
…ncements - Add search and calendar buttons to AppBar (homepage only) - Search bar visibility toggle with focus management - Floating chat button on homepage - Mute button on live transcript page with haptic feedback - Mobile-specific conversation list layout - Live transcript ellipsis on both ends - Date list styling improvements - Process Now button styling - Various UI polish and fixes
There was a problem hiding this comment.
Code Review
This pull request introduces several significant UI and UX improvements for the mobile experience, including a new mute button during recording, a floating chat button, a quick search button in the top bar, and cleaner conversation cards. The code is well-structured, and the changes effectively implement the desired features. I've identified a couple of areas for improvement related to asynchronous UI updates to enhance robustness. Specifically, I've suggested using WidgetsBinding.instance.addPostFrameCallback instead of Future.delayed for requesting focus, aligning with the rule of preferring robust helper functions, and reordering setState calls to occur after asynchronous operations complete to prevent UI state inconsistencies. Overall, this is a great set of enhancements.
| Future<void> _toggleMute(CaptureProvider provider) async { | ||
| if (_isMuted) { | ||
| // Unmute - resume recording | ||
| HapticFeedback.mediumImpact(); | ||
| setState(() { | ||
| _isMuted = false; | ||
| }); | ||
|
|
||
| if (PlatformService.isDesktop) { | ||
| // Desktop - system audio | ||
| await provider.resumeSystemAudioRecording(); | ||
| } else if (provider.havingRecordingDevice) { | ||
| // Device recording (Omi device) | ||
| await provider.resumeDeviceRecording(); | ||
| } else { | ||
| // Phone mic | ||
| await provider.streamRecording(); | ||
| MixpanelManager().phoneMicRecordingStarted(); | ||
| } | ||
| } else { | ||
| // Mute - pause recording with interesting haptic | ||
| HapticFeedback.heavyImpact(); | ||
| await Future.delayed(const Duration(milliseconds: 80)); | ||
| HapticFeedback.lightImpact(); | ||
| setState(() { | ||
| _isMuted = true; | ||
| }); | ||
|
|
||
| if (PlatformService.isDesktop) { | ||
| // Desktop - system audio | ||
| await provider.pauseSystemAudioRecording(); | ||
| } else if (provider.havingRecordingDevice) { | ||
| // Device recording (Omi device) | ||
| await provider.pauseDeviceRecording(); | ||
| } else { | ||
| // Phone mic | ||
| await provider.stopStreamRecording(); | ||
| MixpanelManager().phoneMicRecordingStopped(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
The UI state is updated via setState before the asynchronous recording operations (like resumeSystemAudioRecording or pauseDeviceRecording) are completed. If one of these async calls fails, the UI will show an incorrect state (e.g., "Muted" when the recording is still active). To ensure UI consistency, it's safer to call setState only after the asynchronous operations have successfully finished.
Future<void> _toggleMute(CaptureProvider provider) async {
if (_isMuted) {
// Unmute - resume recording
HapticFeedback.mediumImpact();
if (PlatformService.isDesktop) {
// Desktop - system audio
await provider.resumeSystemAudioRecording();
} else if (provider.havingRecordingDevice) {
// Device recording (Omi device)
await provider.resumeDeviceRecording();
} else {
// Phone mic
await provider.streamRecording();
MixpanelManager().phoneMicRecordingStarted();
}
if (mounted) {
setState(() {
_isMuted = false;
});
}
} else {
// Mute - pause recording with interesting haptic
HapticFeedback.heavyImpact();
await Future.delayed(const Duration(milliseconds: 80));
HapticFeedback.lightImpact();
if (PlatformService.isDesktop) {
// Desktop - system audio
await provider.pauseSystemAudioRecording();
} else if (provider.havingRecordingDevice) {
// Device recording (Omi device)
await provider.pauseDeviceRecording();
}
else {
// Phone mic
await provider.stopStreamRecording();
MixpanelManager().phoneMicRecordingStopped();
}
if (mounted) {
setState(() {
_isMuted = true;
});
}
}
}Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- 🔍 Quick search button in top bar - 💬 Floating chat button for fast access - 🔇 Mute button during live recordings on the transcript page. - 📱 Cleaner, more compact conversation cards - ⚡ Smoother animations and haptic feedback - 🎨 Polished buttons and visual refinements