From a13a2de7d621f420bd005926887d791b0b17afcc Mon Sep 17 00:00:00 2001 From: cogwheel0 <172976095+cogwheel0@users.noreply.github.com> Date: Sat, 6 Dec 2025 19:48:33 +0530 Subject: [PATCH] feat(notes): Improve note editor change detection and UI refinements --- .../notes/views/note_editor_page.dart | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/lib/features/notes/views/note_editor_page.dart b/lib/features/notes/views/note_editor_page.dart index aadb808..ebb1f3e 100644 --- a/lib/features/notes/views/note_editor_page.dart +++ b/lib/features/notes/views/note_editor_page.dart @@ -117,10 +117,21 @@ class _NoteEditorPageState extends ConsumerState { } void _onContentChanged() { - if (!_hasChanges && mounted) { - setState(() => _hasChanges = true); + if (!mounted || _isLoading) return; + + // Check if content actually changed from the saved note + final titleChanged = _note != null && _titleController.text != _note!.title; + final contentChanged = + _note != null && _contentController.text != _note!.markdownContent; + final hasRealChanges = titleChanged || contentChanged; + + if (hasRealChanges != _hasChanges) { + setState(() => _hasChanges = hasRealChanges); + } + + if (hasRealChanges) { + _debounceSave(); } - _debounceSave(); } void _debounceSave() { @@ -378,9 +389,7 @@ class _NoteEditorPageState extends ConsumerState { } Future _startDictation() async { - _voiceService ??= VoiceInputService( - api: ref.read(apiServiceProvider), - ); + _voiceService ??= VoiceInputService(api: ref.read(apiServiceProvider)); try { final ok = await _voiceService!.initialize(); @@ -474,10 +483,12 @@ class _NoteEditorPageState extends ConsumerState { canPop: !_hasChanges, onPopInvokedWithResult: (didPop, result) async { if (didPop) return; // Already popped, nothing to do + // Capture navigator before async gap + final navigator = Navigator.of(context); // Save changes before allowing pop await _saveNote(showFeedback: false); if (!mounted) return; - Navigator.of(context).pop(); + navigator.pop(); }, child: ErrorBoundary( child: Scaffold( @@ -572,7 +583,9 @@ class _NoteEditorPageState extends ConsumerState { // Generate title button - aligned with other header icons AnimatedOpacity( - opacity: _titleFocusNode.hasFocus && !_isGeneratingTitle ? 1.0 : 0.0, + opacity: _titleFocusNode.hasFocus && !_isGeneratingTitle + ? 1.0 + : 0.0, duration: const Duration(milliseconds: 150), child: IgnorePointer( ignoring: !_titleFocusNode.hasFocus || _isGeneratingTitle, @@ -846,11 +859,11 @@ class _NoteEditorPageState extends ConsumerState { context, icon: _isRecording ? (Platform.isIOS - ? CupertinoIcons.stop_fill - : Icons.stop_rounded) + ? CupertinoIcons.stop_fill + : Icons.stop_rounded) : (Platform.isIOS - ? CupertinoIcons.mic_fill - : Icons.mic_rounded), + ? CupertinoIcons.mic_fill + : Icons.mic_rounded), color: _isRecording ? theme.error : null, isLoading: false, tooltip: _isRecording ? l10n.stopRecording : l10n.startDictation, @@ -909,11 +922,7 @@ class _NoteEditorPageState extends ConsumerState { ), ), ) - : Icon( - icon, - color: color ?? theme.iconPrimary, - size: IconSize.lg, - ), + : Icon(icon, color: color ?? theme.iconPrimary, size: IconSize.lg), ); if (showMenu) { @@ -932,22 +941,22 @@ class _NoteEditorPageState extends ConsumerState { } }, itemBuilder: (context) => [ - PopupMenuItem( - value: 'enhance', - child: Row( - children: [ - Icon( - Platform.isIOS - ? CupertinoIcons.sparkles - : Icons.auto_fix_high_rounded, - color: theme.buttonPrimary, - size: IconSize.md, - ), - const SizedBox(width: Spacing.sm), - Text(l10n.enhanceNote), - ], + PopupMenuItem( + value: 'enhance', + child: Row( + children: [ + Icon( + Platform.isIOS + ? CupertinoIcons.sparkles + : Icons.auto_fix_high_rounded, + color: theme.buttonPrimary, + size: IconSize.md, ), - ), + const SizedBox(width: Spacing.sm), + Text(l10n.enhanceNote), + ], + ), + ), PopupMenuItem( value: 'title', child: Row(