mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-07-15 07:56:11 +08:00
215 lines
5.3 KiB
Dart
215 lines
5.3 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:gitjournal/app_settings.dart';
|
|
import 'package:gitjournal/core/note.dart';
|
|
import 'package:gitjournal/core/notes_folder.dart';
|
|
import 'package:gitjournal/editors/common.dart';
|
|
import 'package:gitjournal/editors/disposable_change_notifier.dart';
|
|
import 'package:gitjournal/editors/heuristics.dart';
|
|
import 'package:gitjournal/editors/note_body_editor.dart';
|
|
import 'package:gitjournal/editors/note_title_editor.dart';
|
|
import 'package:gitjournal/error_reporting.dart';
|
|
import 'package:gitjournal/utils/logger.dart';
|
|
import 'package:gitjournal/widgets/editor_scroll_view.dart';
|
|
import 'package:gitjournal/widgets/markdown_toolbar.dart';
|
|
|
|
class MarkdownEditor extends StatefulWidget implements Editor {
|
|
final Note note;
|
|
final NotesFolder parentFolder;
|
|
final bool noteModified;
|
|
|
|
@override
|
|
final NoteCallback noteDeletionSelected;
|
|
@override
|
|
final NoteCallback noteEditorChooserSelected;
|
|
@override
|
|
final NoteCallback exitEditorSelected;
|
|
@override
|
|
final NoteCallback renameNoteSelected;
|
|
@override
|
|
final NoteCallback editTagsSelected;
|
|
@override
|
|
final NoteCallback moveNoteToFolderSelected;
|
|
@override
|
|
final NoteCallback discardChangesSelected;
|
|
|
|
final bool editMode;
|
|
|
|
MarkdownEditor({
|
|
Key? key,
|
|
required this.note,
|
|
required this.parentFolder,
|
|
required this.noteModified,
|
|
required this.noteDeletionSelected,
|
|
required this.noteEditorChooserSelected,
|
|
required this.exitEditorSelected,
|
|
required this.renameNoteSelected,
|
|
required this.editTagsSelected,
|
|
required this.moveNoteToFolderSelected,
|
|
required this.discardChangesSelected,
|
|
required this.editMode,
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
MarkdownEditorState createState() {
|
|
return MarkdownEditorState(note);
|
|
}
|
|
}
|
|
|
|
class MarkdownEditorState extends State<MarkdownEditor>
|
|
with DisposableChangeNotifier
|
|
implements EditorState {
|
|
Note note;
|
|
late TextEditingController _textController;
|
|
late TextEditingController _titleTextController;
|
|
|
|
late EditorHeuristics _heuristics;
|
|
|
|
late bool _noteModified;
|
|
|
|
MarkdownEditorState(this.note);
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_noteModified = widget.noteModified;
|
|
|
|
_textController = TextEditingController(text: note.body);
|
|
_titleTextController = TextEditingController(text: note.title);
|
|
|
|
_heuristics = EditorHeuristics(text: note.body);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_textController.dispose();
|
|
_titleTextController.dispose();
|
|
|
|
super.disposeListenables();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
void didUpdateWidget(MarkdownEditor oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
|
|
if (oldWidget.noteModified != widget.noteModified) {
|
|
_noteModified = widget.noteModified;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
var editor = EditorScrollView(
|
|
child: Column(
|
|
children: <Widget>[
|
|
NoteTitleEditor(
|
|
_titleTextController,
|
|
_noteTitleTextChanged,
|
|
),
|
|
NoteBodyEditor(
|
|
textController: _textController,
|
|
autofocus: widget.editMode,
|
|
onChanged: _noteTextChanged,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
|
|
var settings = Provider.of<AppSettings>(context);
|
|
Widget? markdownToolbar;
|
|
if (settings.experimentalMarkdownToolbar) {
|
|
markdownToolbar = MarkdownToolBar(
|
|
textController: _textController,
|
|
);
|
|
}
|
|
|
|
return EditorScaffold(
|
|
editor: widget,
|
|
editorState: this,
|
|
noteModified: _noteModified,
|
|
editMode: widget.editMode,
|
|
parentFolder: note.parent,
|
|
body: editor,
|
|
onUndoSelected: _undo,
|
|
onRedoSelected: _redo,
|
|
undoAllowed: false,
|
|
redoAllowed: false,
|
|
extraBottomWidget: markdownToolbar,
|
|
);
|
|
}
|
|
|
|
void _updateNote() {
|
|
note.title = _titleTextController.text.trim();
|
|
note.body = _textController.text.trim();
|
|
note.type = NoteType.Unknown;
|
|
}
|
|
|
|
@override
|
|
Note getNote() {
|
|
_updateNote();
|
|
return note;
|
|
}
|
|
|
|
void _noteTextChanged() {
|
|
try {
|
|
_applyHeuristics();
|
|
} catch (e, stackTrace) {
|
|
Log.e("EditorHeuristics: $e");
|
|
logExceptionWarning(e, stackTrace);
|
|
}
|
|
if (_noteModified && !widget.editMode) return;
|
|
|
|
var newState = !(widget.editMode && _textController.text.trim().isEmpty);
|
|
if (newState != _noteModified) {
|
|
setState(() {
|
|
_noteModified = newState;
|
|
});
|
|
}
|
|
|
|
notifyListeners();
|
|
}
|
|
|
|
void _noteTitleTextChanged() {
|
|
if (_noteModified && !widget.editMode) return;
|
|
|
|
var newState =
|
|
!(widget.editMode && _titleTextController.text.trim().isEmpty);
|
|
if (newState != _noteModified) {
|
|
setState(() {
|
|
_noteModified = newState;
|
|
});
|
|
}
|
|
|
|
notifyListeners();
|
|
}
|
|
|
|
void _applyHeuristics() {
|
|
var editState = TextEditorState.fromValue(_textController.value);
|
|
var es = _heuristics.textChanged(editState);
|
|
if (es != null) {
|
|
_textController.value = es.toValue();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> addImage(File file) async {
|
|
await getNote().addImage(file);
|
|
setState(() {
|
|
_textController.text = note.body;
|
|
_noteModified = true;
|
|
});
|
|
}
|
|
|
|
@override
|
|
bool get noteModified => _noteModified;
|
|
|
|
Future<void> _undo() async {}
|
|
|
|
Future<void> _redo() async {}
|
|
}
|