Files
GitJournal/lib/editors/markdown_editor.dart
2021-05-25 12:39:35 +02:00

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 {}
}