feat(notes): Improve note editor change detection and UI refinements
This commit is contained in:
@@ -117,11 +117,22 @@ class _NoteEditorPageState extends ConsumerState<NoteEditorPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onContentChanged() {
|
void _onContentChanged() {
|
||||||
if (!_hasChanges && mounted) {
|
if (!mounted || _isLoading) return;
|
||||||
setState(() => _hasChanges = true);
|
|
||||||
|
// 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() {
|
void _debounceSave() {
|
||||||
_saveDebounce?.cancel();
|
_saveDebounce?.cancel();
|
||||||
@@ -378,9 +389,7 @@ class _NoteEditorPageState extends ConsumerState<NoteEditorPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _startDictation() async {
|
Future<void> _startDictation() async {
|
||||||
_voiceService ??= VoiceInputService(
|
_voiceService ??= VoiceInputService(api: ref.read(apiServiceProvider));
|
||||||
api: ref.read(apiServiceProvider),
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final ok = await _voiceService!.initialize();
|
final ok = await _voiceService!.initialize();
|
||||||
@@ -474,10 +483,12 @@ class _NoteEditorPageState extends ConsumerState<NoteEditorPage> {
|
|||||||
canPop: !_hasChanges,
|
canPop: !_hasChanges,
|
||||||
onPopInvokedWithResult: (didPop, result) async {
|
onPopInvokedWithResult: (didPop, result) async {
|
||||||
if (didPop) return; // Already popped, nothing to do
|
if (didPop) return; // Already popped, nothing to do
|
||||||
|
// Capture navigator before async gap
|
||||||
|
final navigator = Navigator.of(context);
|
||||||
// Save changes before allowing pop
|
// Save changes before allowing pop
|
||||||
await _saveNote(showFeedback: false);
|
await _saveNote(showFeedback: false);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
Navigator.of(context).pop();
|
navigator.pop();
|
||||||
},
|
},
|
||||||
child: ErrorBoundary(
|
child: ErrorBoundary(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
@@ -572,7 +583,9 @@ class _NoteEditorPageState extends ConsumerState<NoteEditorPage> {
|
|||||||
|
|
||||||
// Generate title button - aligned with other header icons
|
// Generate title button - aligned with other header icons
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
opacity: _titleFocusNode.hasFocus && !_isGeneratingTitle ? 1.0 : 0.0,
|
opacity: _titleFocusNode.hasFocus && !_isGeneratingTitle
|
||||||
|
? 1.0
|
||||||
|
: 0.0,
|
||||||
duration: const Duration(milliseconds: 150),
|
duration: const Duration(milliseconds: 150),
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
ignoring: !_titleFocusNode.hasFocus || _isGeneratingTitle,
|
ignoring: !_titleFocusNode.hasFocus || _isGeneratingTitle,
|
||||||
@@ -909,11 +922,7 @@ class _NoteEditorPageState extends ConsumerState<NoteEditorPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Icon(
|
: Icon(icon, color: color ?? theme.iconPrimary, size: IconSize.lg),
|
||||||
icon,
|
|
||||||
color: color ?? theme.iconPrimary,
|
|
||||||
size: IconSize.lg,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (showMenu) {
|
if (showMenu) {
|
||||||
|
|||||||
Reference in New Issue
Block a user