import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_code_editor/flutter_code_editor.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown_latex/flutter_markdown_latex.dart'; import 'package:flutter_svg/svg.dart'; import 'package:markdown/markdown.dart' as md; import 'package:shared_preferences/shared_preferences.dart'; import 'themes/day.dart'; import 'themes/night.dart'; import 'themes/markdown.dart'; import 'codefield.dart'; class NoteEditorScreen extends StatefulWidget { final File note; final String? notesDirectoryPath; // ✅ Add this final bool isDarkMode; NoteEditorScreen( {required this.note, required this.isDarkMode, required this.notesDirectoryPath}); @override _NoteEditorScreenState createState() => _NoteEditorScreenState(); } class _NoteEditorScreenState extends State { late CodeController _controller; late String _initialContent; bool _isPreviewMode = false; bool _isLoading = true; bool _autoSaveEnabled = false; // Controlled by user toggle double _fontSize = 16.0; @override void initState() { super.initState(); _loadPreferences(); _loadNoteContent(); } Future _loadPreferences() async { final prefs = await SharedPreferences.getInstance(); setState(() { _autoSaveEnabled = prefs.getBool('auto_save_enabled') ?? false; }); } Future _setAutoSaveEnabled(bool value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool('auto_save_enabled', value); setState(() { _autoSaveEnabled = value; }); } Future _loadNoteContent() async { try { _initialContent = await widget.note.readAsString(); } catch (e) { print("❌ Failed to read file: ${widget.note.path}"); _initialContent = ""; } setState(() { _controller = CodeController( text: _initialContent, language: markdown, ); _controller.addListener(_onTextChanged); _isLoading = false; }); } void _onTextChanged() { if (_autoSaveEnabled && _controller.text != _initialContent) { _saveNote(); } } Future _saveNote() async { try { if (_controller.text == _initialContent) return; // No change, skip saving await widget.note.writeAsString(_controller.text); _initialContent = _controller.text; // Update initial state print("✅ Note saved successfully"); } catch (e) { print("❌ Error saving note: ${widget.note.path}"); } } Future _onWillPop() async { if (_controller.text == _initialContent) return true; // No changes, exit return await showDialog( context: context, builder: (context) => AlertDialog( title: Text("Save Changes?"), content: Text("You have unsaved changes. Save before exiting?"), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(true), // Discard and exit child: Text("Discard"), ), TextButton( onPressed: () async { await _saveNote(); Navigator.of(context).pop(true); // Save and exit }, child: Text("Save"), ), TextButton( onPressed: () => Navigator.of(context).pop(false), // Stay on page child: Text("Cancel"), ), ], ), ) ?? false; } void _updateFontSize(double newSize) { setState(() { _fontSize = newSize; }); } @override void dispose() { _controller.removeListener(_onTextChanged); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: _onWillPop, child: Scaffold( appBar: AppBar( title: Text(widget.note.uri.pathSegments.last.replaceAll('.md', '')), leading: IconButton( icon: Icon(Icons.arrow_back), onPressed: () async { if (await _onWillPop()) { Navigator.pop(context); } }, ), actions: [ Padding( padding: EdgeInsets.symmetric(horizontal: 15), child: PopupMenuButton( icon: SvgPicture.asset( "assets/blaster.svg", width: 30, color: Colors.orange, ), onSelected: (String value) { switch (value) { case 'Save': _saveNote(); break; case 'Toggle Autosave': _setAutoSaveEnabled(!_autoSaveEnabled); break; case 'Toggle Preview': setState(() { _isPreviewMode = !_isPreviewMode; }); break; } }, itemBuilder: (BuildContext context) => [ PopupMenuItem( value: 'Save', child: ListTile( leading: Icon(Icons.save_outlined), title: Text("Save"), ), ), PopupMenuItem( value: 'Toggle Autosave', child: ListTile( leading: Icon(_autoSaveEnabled ? Icons.alarm_off : Icons.alarm_on), title: Text(_autoSaveEnabled ? "Disable Autosave" : "Enable Autosave"), ), ), PopupMenuItem( enabled: false, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Font Size'), StatefulBuilder( builder: (context, setState) { return Slider( value: _fontSize, min: 12.0, max: 24.0, divisions: 6, label: _fontSize.toString(), onChanged: (newSize) { setState(() { _fontSize = newSize; }); _updateFontSize(newSize); }, ); }, ), ], ), ), PopupMenuItem( value: 'Toggle Preview', child: ListTile( leading: Icon(_isPreviewMode ? Icons.edit_note_outlined : Icons.visibility_outlined), title: Text(_isPreviewMode ? "Edit Mode" : "Preview Mode"), ), ), ], ), ), ], ), body: _isLoading ? Center(child: CircularProgressIndicator()) : _isPreviewMode ? Padding( padding: const EdgeInsets.all(16.0), child: SingleChildScrollView( child: MarkdownBody( builders: { 'latex': LatexElementBuilder( textStyle: TextStyle( color: widget.isDarkMode ? Colors.white : Colors.black)), }, extensionSet: md.ExtensionSet( [LatexBlockSyntax()], [LatexInlineSyntax()], ), data: _controller.text, selectable: true, styleSheet: MarkdownStyleSheet( p: TextStyle( fontSize: _fontSize, color: widget.isDarkMode ? Colors.white : Colors.black), h1: TextStyle( fontSize: _fontSize + 8, color: widget.isDarkMode ? Colors.white : Colors.black, fontWeight: FontWeight.bold), h2: TextStyle( fontSize: _fontSize + 6, color: widget.isDarkMode ? Colors.white : Colors.black, fontWeight: FontWeight.bold), h3: TextStyle( fontSize: _fontSize + 4, color: widget.isDarkMode ? Colors.white : Colors.black, fontWeight: FontWeight.bold), code: TextStyle( fontSize: _fontSize, backgroundColor: Colors.transparent), codeblockDecoration: BoxDecoration( color: Colors.transparent), ), ), ), ) : Padding( padding: const EdgeInsets.all(16.0), child: CodeTheme( data: CodeThemeData( styles: widget.isDarkMode ? nightTheme : dayTheme), child: SingleChildScrollView( child: WrappedCodeField( wrap: true, controller: _controller, textStyle: TextStyle( fontSize: _fontSize, ), ), ), ), ), ), ); } }