import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:notas/data/note_body.dart'; import 'package:notas/data/note_repository.dart'; import 'package:notas/models/note.dart'; import 'package:notas/theme/app_palette.dart'; class NoteEditorScreen extends StatefulWidget { const NoteEditorScreen({ super.key, this.repository, this.saveNote, required this.note, this.embedded = false, this.onSaved, }); final NoteRepository? repository; final Future Function(Note note)? saveNote; final Note note; final bool embedded; final ValueChanged? onSaved; @override State createState() => _NoteEditorScreenState(); } class _NoteEditorScreenState extends State { static const Duration _debounceDuration = Duration(seconds: 1); late final TextEditingController _titleController; late final QuillController _bodyController; late final FocusNode _bodyFocusNode; late final ScrollController _bodyScrollController; Timer? _debounceTimer; bool _isSaving = false; bool _saveQueued = false; late Note _baselineNote; AppPalette _paletteOf(BuildContext context) { return Theme.of(context).extension() ?? AppPalette.fromBrightness(Theme.of(context).brightness); } @override void initState() { super.initState(); _baselineNote = widget.note; _titleController = TextEditingController(text: widget.note.title) ..addListener(_scheduleSave); _bodyController = QuillController( document: noteBodyToDocument(widget.note.body), selection: const TextSelection.collapsed(offset: 0), )..addListener(_scheduleSave); _bodyFocusNode = FocusNode(); _bodyScrollController = ScrollController(); } @override void dispose() { _debounceTimer?.cancel(); _titleController.dispose(); _bodyController.dispose(); _bodyFocusNode.dispose(); _bodyScrollController.dispose(); super.dispose(); } String _bodyAsJson() { return noteDocumentToStorageJson(_bodyController.document); } void _scheduleSave() { _debounceTimer?.cancel(); _debounceTimer = Timer(_debounceDuration, () { unawaited(_saveNow()); }); } Future _saveNow() async { if (!mounted) { return; } final String title = _titleController.text.trim(); final String body = _bodyAsJson(); final Note draft = _baselineNote.copyWith( title: title.isEmpty ? 'Sin título' : title, body: body, categoryId: _baselineNote.categoryId, updatedAt: DateTime.now(), isDirty: true, ); final bool hasChanges = draft.title != _baselineNote.title || draft.body != _baselineNote.body || draft.categoryId != _baselineNote.categoryId; if (!hasChanges) { return; } if (_isSaving) { _saveQueued = true; return; } _isSaving = true; try { final Note saved = widget.saveNote != null ? await widget.saveNote!(draft) : await widget.repository!.updateNote(draft); _baselineNote = saved; widget.onSaved?.call(saved); } catch (error) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('No se pudo guardar la nota: $error')), ); } } finally { _isSaving = false; if (_saveQueued) { _saveQueued = false; unawaited(_saveNow()); } } } Widget _buildEditorBody() { final AppPalette palette = _paletteOf(context); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( children: [ Expanded( child: Padding( padding: const EdgeInsets.only(left: 8), child: TextField( controller: _titleController, style: TextStyle( color: palette.textPrimary, fontSize: 26, fontWeight: FontWeight.w700, ), decoration: InputDecoration( hintText: 'Título', hintStyle: TextStyle(color: palette.textHint), border: InputBorder.none, ), ), ), ), ], ), const SizedBox(height: 10), Expanded( child: Container( padding: const EdgeInsets.all(8), child: QuillEditor.basic( controller: _bodyController, focusNode: _bodyFocusNode, scrollController: _bodyScrollController, config: QuillEditorConfig( scrollable: true, padding: EdgeInsets.zero, autoFocus: false, expands: true, placeholder: 'Escribe tu nota...', keyboardAppearance: Theme.of(context).brightness, ), ), ), ), const SizedBox(height: 8), QuillSimpleToolbar( controller: _bodyController, config: const QuillSimpleToolbarConfig( color: Colors.transparent, showBoldButton: true, showItalicButton: true, showUnderLineButton: true, showStrikeThrough: false, showInlineCode: false, showColorButton: false, showBackgroundColorButton: false, showClearFormat: false, showAlignmentButtons: false, showHeaderStyle: false, showListNumbers: true, showListBullets: true, showListCheck: true, showCodeBlock: false, showQuote: false, showIndent: false, showLink: false, showUndo: false, showRedo: false, showDividers: false, showFontFamily: false, showFontSize: false, showDirection: false, showSearchButton: false, showSubscript: false, showSuperscript: false, multiRowsDisplay: false, axis: Axis.horizontal, ), ), ], ); } @override Widget build(BuildContext context) { final AppPalette palette = _paletteOf(context); final Widget editor = Padding( padding: const EdgeInsets.all(8), child: _buildEditorBody(), ); if (widget.embedded) { return editor; } return Scaffold( backgroundColor: palette.cardBackground, appBar: AppBar(title: const Text('Editar nota')), body: SafeArea(child: editor), ); } }