mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-06-27 01:02:14 +08:00
Add the concept of Editors
* We no longer have a separate editing and browsing view - This does mean we loose the ability to quick flip between notes by swiping. However, this is more how a note editor would behave. I do later want to add that capability back. * We have 2 editors for now - Markdown and Raw. By default we use the Markdown editor which can be toggled between Preview / Edit mode. I later want to add a rich text editor and a todo editor as well.
This commit is contained in:
@ -156,9 +156,17 @@ class Note with ChangeNotifier implements Comparable<Note> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void rename(String newName) {
|
void rename(String newName) {
|
||||||
|
// Do not let the user rename it to a non-markdown file
|
||||||
|
if (!newName.toLowerCase().endsWith('.md')) {
|
||||||
|
newName += '.md';
|
||||||
|
}
|
||||||
|
|
||||||
var parentDirName = p.dirname(filePath);
|
var parentDirName = p.dirname(filePath);
|
||||||
var newFilePath = p.join(parentDirName, newName);
|
var newFilePath = p.join(parentDirName, newName);
|
||||||
File(filePath).renameSync(newFilePath);
|
if (_loadState != NoteLoadState.None) {
|
||||||
|
// for new notes
|
||||||
|
File(filePath).renameSync(newFilePath);
|
||||||
|
}
|
||||||
_filePath = newFilePath;
|
_filePath = newFilePath;
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
198
lib/editors/markdown_editor.dart
Normal file
198
lib/editors/markdown_editor.dart
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:gitjournal/core/note.dart';
|
||||||
|
import 'package:gitjournal/widgets/note_viewer.dart';
|
||||||
|
|
||||||
|
typedef NoteCallback = void Function(Note);
|
||||||
|
enum DropDownChoices { Rename }
|
||||||
|
|
||||||
|
class MarkdownEditor extends StatefulWidget {
|
||||||
|
final Note note;
|
||||||
|
final NoteCallback noteDeletionSelected;
|
||||||
|
final NoteCallback noteEditorChooserSelected;
|
||||||
|
final NoteCallback exitEditorSelected;
|
||||||
|
final NoteCallback renameNoteSelected;
|
||||||
|
final bool openEditorByDefault;
|
||||||
|
|
||||||
|
MarkdownEditor({
|
||||||
|
Key key,
|
||||||
|
@required this.note,
|
||||||
|
@required this.noteDeletionSelected,
|
||||||
|
@required this.noteEditorChooserSelected,
|
||||||
|
@required this.exitEditorSelected,
|
||||||
|
@required this.renameNoteSelected,
|
||||||
|
this.openEditorByDefault = false,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
MarkdownEditorState createState() {
|
||||||
|
return MarkdownEditorState(note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MarkdownEditorState extends State<MarkdownEditor> {
|
||||||
|
Note note;
|
||||||
|
TextEditingController _textController = TextEditingController();
|
||||||
|
TextEditingController _titleTextController = TextEditingController();
|
||||||
|
|
||||||
|
bool editingMode = false;
|
||||||
|
|
||||||
|
MarkdownEditorState(this.note) {
|
||||||
|
_textController = TextEditingController(text: note.body);
|
||||||
|
_titleTextController = TextEditingController(text: note.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
editingMode = widget.openEditorByDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_textController.dispose();
|
||||||
|
_titleTextController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var editor = Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
_NoteTitleEditor(_titleTextController),
|
||||||
|
_NoteBodyEditor(_textController),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget body = editingMode ? editor : NoteViewer(note: note);
|
||||||
|
var fab = FloatingActionButton(
|
||||||
|
child: editingMode
|
||||||
|
? const Icon(Icons.remove_red_eye)
|
||||||
|
: const Icon(Icons.edit),
|
||||||
|
onPressed: _switchMode,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
leading: IconButton(
|
||||||
|
key: const ValueKey("NewEntry"),
|
||||||
|
icon: const Icon(Icons.check),
|
||||||
|
onPressed: () {
|
||||||
|
_updateNote();
|
||||||
|
widget.exitEditorSelected(note);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: editingMode
|
||||||
|
? const Icon(Icons.remove_red_eye)
|
||||||
|
: const Icon(Icons.edit),
|
||||||
|
onPressed: _switchMode,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.library_books),
|
||||||
|
onPressed: () {
|
||||||
|
_updateNote();
|
||||||
|
widget.noteEditorChooserSelected(note);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
onPressed: () {
|
||||||
|
_updateNote();
|
||||||
|
widget.noteDeletionSelected(note);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PopupMenuButton<DropDownChoices>(
|
||||||
|
onSelected: (DropDownChoices choice) {
|
||||||
|
_updateNote();
|
||||||
|
widget.renameNoteSelected(note);
|
||||||
|
},
|
||||||
|
itemBuilder: (BuildContext context) =>
|
||||||
|
<PopupMenuEntry<DropDownChoices>>[
|
||||||
|
const PopupMenuItem<DropDownChoices>(
|
||||||
|
value: DropDownChoices.Rename,
|
||||||
|
child: Text('Edit File Name'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: body,
|
||||||
|
floatingActionButton: fab,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _switchMode() {
|
||||||
|
setState(() {
|
||||||
|
editingMode = !editingMode;
|
||||||
|
_updateNote();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateNote() {
|
||||||
|
note.title = _titleTextController.text.trim();
|
||||||
|
note.body = _textController.text.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
Note getNote() {
|
||||||
|
_updateNote();
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NoteBodyEditor extends StatelessWidget {
|
||||||
|
final TextEditingController textController;
|
||||||
|
|
||||||
|
_NoteBodyEditor(this.textController);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var style = Theme.of(context).textTheme.subhead;
|
||||||
|
|
||||||
|
return TextField(
|
||||||
|
autofocus: true,
|
||||||
|
autocorrect: false,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
maxLines: null,
|
||||||
|
style: style,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Write here',
|
||||||
|
border: InputBorder.none,
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
controller: textController,
|
||||||
|
textCapitalization: TextCapitalization.sentences,
|
||||||
|
scrollPadding: const EdgeInsets.all(0.0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NoteTitleEditor extends StatelessWidget {
|
||||||
|
final TextEditingController textController;
|
||||||
|
|
||||||
|
_NoteTitleEditor(this.textController);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var style = Theme.of(context).textTheme.title;
|
||||||
|
|
||||||
|
return TextField(
|
||||||
|
keyboardType: TextInputType.text,
|
||||||
|
maxLines: 1,
|
||||||
|
style: style,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Title',
|
||||||
|
border: InputBorder.none,
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
controller: textController,
|
||||||
|
textCapitalization: TextCapitalization.sentences,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
136
lib/editors/raw_editor.dart
Normal file
136
lib/editors/raw_editor.dart
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:gitjournal/core/note.dart';
|
||||||
|
import 'package:gitjournal/core/note_data_serializers.dart';
|
||||||
|
|
||||||
|
typedef NoteCallback = void Function(Note);
|
||||||
|
enum DropDownChoices { Rename }
|
||||||
|
|
||||||
|
class RawEditor extends StatefulWidget {
|
||||||
|
final Note note;
|
||||||
|
final NoteCallback noteDeletionSelected;
|
||||||
|
final NoteCallback noteEditorChooserSelected;
|
||||||
|
final NoteCallback exitEditorSelected;
|
||||||
|
final NoteCallback renameNoteSelected;
|
||||||
|
|
||||||
|
RawEditor({
|
||||||
|
Key key,
|
||||||
|
@required this.note,
|
||||||
|
@required this.noteDeletionSelected,
|
||||||
|
@required this.noteEditorChooserSelected,
|
||||||
|
@required this.exitEditorSelected,
|
||||||
|
@required this.renameNoteSelected,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
RawEditorState createState() {
|
||||||
|
return RawEditorState(note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RawEditorState extends State<RawEditor> {
|
||||||
|
Note note;
|
||||||
|
TextEditingController _textController = TextEditingController();
|
||||||
|
|
||||||
|
final serializer = MarkdownYAMLSerializer();
|
||||||
|
|
||||||
|
RawEditorState(this.note) {
|
||||||
|
_textController = TextEditingController(text: serializer.encode(note.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_textController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var editor = Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: _NoteEditor(_textController),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
leading: IconButton(
|
||||||
|
key: const ValueKey("NewEntry"),
|
||||||
|
icon: const Icon(Icons.check),
|
||||||
|
onPressed: () {
|
||||||
|
_updateNote();
|
||||||
|
widget.exitEditorSelected(note);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.library_books),
|
||||||
|
onPressed: () {
|
||||||
|
_updateNote();
|
||||||
|
widget.noteEditorChooserSelected(note);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
onPressed: () {
|
||||||
|
_updateNote();
|
||||||
|
widget.noteDeletionSelected(note);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PopupMenuButton<DropDownChoices>(
|
||||||
|
onSelected: (DropDownChoices choice) {
|
||||||
|
_updateNote();
|
||||||
|
widget.renameNoteSelected(note);
|
||||||
|
},
|
||||||
|
itemBuilder: (BuildContext context) =>
|
||||||
|
<PopupMenuEntry<DropDownChoices>>[
|
||||||
|
const PopupMenuItem<DropDownChoices>(
|
||||||
|
value: DropDownChoices.Rename,
|
||||||
|
child: Text('Edit File Name'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: editor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateNote() {
|
||||||
|
note.data = serializer.decode(_textController.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Note getNote() {
|
||||||
|
_updateNote();
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NoteEditor extends StatelessWidget {
|
||||||
|
final TextEditingController textController;
|
||||||
|
|
||||||
|
_NoteEditor(this.textController);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var style =
|
||||||
|
Theme.of(context).textTheme.subhead.copyWith(fontFamily: "Roboto Mono");
|
||||||
|
|
||||||
|
return TextField(
|
||||||
|
autofocus: false,
|
||||||
|
autocorrect: false,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
maxLines: null,
|
||||||
|
style: style,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Write here',
|
||||||
|
border: InputBorder.none,
|
||||||
|
isDense: true,
|
||||||
|
),
|
||||||
|
controller: textController,
|
||||||
|
textCapitalization: TextCapitalization.sentences,
|
||||||
|
scrollPadding: const EdgeInsets.all(0.0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,162 +0,0 @@
|
|||||||
import 'package:fimber/fimber.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:page_transition/page_transition.dart';
|
|
||||||
import 'package:share/share.dart';
|
|
||||||
|
|
||||||
import 'package:gitjournal/core/note.dart';
|
|
||||||
import 'package:gitjournal/state_container.dart';
|
|
||||||
import 'package:gitjournal/utils.dart';
|
|
||||||
import 'package:gitjournal/widgets/rename_dialog.dart';
|
|
||||||
import 'package:gitjournal/widgets/note_viewer.dart';
|
|
||||||
|
|
||||||
import 'journal_editor.dart';
|
|
||||||
|
|
||||||
enum NoteBrowserDropDownChoices { Rename }
|
|
||||||
|
|
||||||
class JournalBrowsingScreen extends StatefulWidget {
|
|
||||||
final List<Note> notes;
|
|
||||||
final int noteIndex;
|
|
||||||
|
|
||||||
const JournalBrowsingScreen({
|
|
||||||
@required this.notes,
|
|
||||||
@required this.noteIndex,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
JournalBrowsingScreenState createState() {
|
|
||||||
return JournalBrowsingScreenState(noteIndex: noteIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JournalBrowsingScreenState extends State<JournalBrowsingScreen> {
|
|
||||||
PageController pageController;
|
|
||||||
int currentPage;
|
|
||||||
|
|
||||||
JournalBrowsingScreenState({@required int noteIndex}) {
|
|
||||||
pageController = PageController(initialPage: noteIndex);
|
|
||||||
currentPage = noteIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var pageView = PageView.builder(
|
|
||||||
controller: pageController,
|
|
||||||
itemCount: widget.notes.length,
|
|
||||||
itemBuilder: (BuildContext context, int pos) {
|
|
||||||
var note = widget.notes[pos];
|
|
||||||
return NoteViewer(
|
|
||||||
key: ValueKey("Viewer_" + note.filePath),
|
|
||||||
note: widget.notes[pos],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onPageChanged: (int pageNum) {
|
|
||||||
setState(() {
|
|
||||||
currentPage = pageNum;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
actions: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.delete),
|
|
||||||
onPressed: () {
|
|
||||||
showDialog(context: context, builder: _buildAlertDialog);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.share),
|
|
||||||
onPressed: () {
|
|
||||||
Note note = widget.notes[_currentIndex()];
|
|
||||||
Share.share(note.body);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
PopupMenuButton<NoteBrowserDropDownChoices>(
|
|
||||||
onSelected: (NoteBrowserDropDownChoices choice) async {
|
|
||||||
var note = widget.notes[currentPage];
|
|
||||||
switch (choice) {
|
|
||||||
case NoteBrowserDropDownChoices.Rename:
|
|
||||||
var fileName = await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (_) => RenameDialog(
|
|
||||||
oldPath: note.filePath,
|
|
||||||
inputDecoration: 'File Name',
|
|
||||||
dialogTitle: "Rename File",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (fileName is String) {
|
|
||||||
final container = StateContainer.of(context);
|
|
||||||
container.renameNote(note, fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemBuilder: (BuildContext context) =>
|
|
||||||
<PopupMenuEntry<NoteBrowserDropDownChoices>>[
|
|
||||||
const PopupMenuItem<NoteBrowserDropDownChoices>(
|
|
||||||
value: NoteBrowserDropDownChoices.Rename,
|
|
||||||
child: Text('Rename File'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: pageView,
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
child: Icon(Icons.edit),
|
|
||||||
onPressed: () {
|
|
||||||
Note note = widget.notes[_currentIndex()];
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
PageTransition(
|
|
||||||
type: PageTransitionType.fade,
|
|
||||||
child: JournalEditor.fromNote(note),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int _currentIndex() {
|
|
||||||
int currentIndex = pageController.page.round();
|
|
||||||
assert(currentIndex >= 0);
|
|
||||||
assert(currentIndex < widget.notes.length);
|
|
||||||
return currentIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _deleteNote(BuildContext context) {
|
|
||||||
final stateContainer = StateContainer.of(context);
|
|
||||||
var noteIndex = _currentIndex();
|
|
||||||
Note note = widget.notes[noteIndex];
|
|
||||||
stateContainer.removeNote(note);
|
|
||||||
Navigator.pop(context);
|
|
||||||
|
|
||||||
Fimber.d("Shwoing an undo snackbar");
|
|
||||||
showUndoDeleteSnackbar(context, stateContainer, note, noteIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAlertDialog(BuildContext context) {
|
|
||||||
var title = "Are you sure you want to delete this Note?";
|
|
||||||
|
|
||||||
return AlertDialog(
|
|
||||||
content: Text(title),
|
|
||||||
actions: <Widget>[
|
|
||||||
FlatButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: const Text('Cancel'),
|
|
||||||
),
|
|
||||||
FlatButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context); // Alert box
|
|
||||||
_deleteNote(context);
|
|
||||||
},
|
|
||||||
child: const Text('Delete'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,273 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:gitjournal/core/note.dart';
|
|
||||||
import 'package:gitjournal/core/notes_folder.dart';
|
|
||||||
import 'package:gitjournal/state_container.dart';
|
|
||||||
import 'package:gitjournal/core/note_data.dart';
|
|
||||||
import 'package:gitjournal/core/note_data_serializers.dart';
|
|
||||||
import 'package:gitjournal/widgets/note_viewer.dart';
|
|
||||||
|
|
||||||
enum NoteEditorDropDownChoices { Discard, SwitchEditor }
|
|
||||||
|
|
||||||
class JournalEditor extends StatefulWidget {
|
|
||||||
final Note note;
|
|
||||||
final NotesFolder notesFolder;
|
|
||||||
|
|
||||||
JournalEditor.fromNote(this.note) : notesFolder = null;
|
|
||||||
JournalEditor.newNote(this.notesFolder) : note = null;
|
|
||||||
|
|
||||||
@override
|
|
||||||
JournalEditorState createState() {
|
|
||||||
if (note == null) {
|
|
||||||
return JournalEditorState.newNote(notesFolder);
|
|
||||||
} else {
|
|
||||||
return JournalEditorState.fromNote(note);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JournalEditorState extends State<JournalEditor> {
|
|
||||||
Note note;
|
|
||||||
final bool newNote;
|
|
||||||
TextEditingController _textController = TextEditingController();
|
|
||||||
TextEditingController _titleTextController = TextEditingController();
|
|
||||||
|
|
||||||
bool rawEditor = false;
|
|
||||||
bool editingMode = false;
|
|
||||||
final serializer = MarkdownYAMLSerializer();
|
|
||||||
|
|
||||||
JournalEditorState.newNote(NotesFolder folder) : newNote = true {
|
|
||||||
note = Note.newNote(folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
JournalEditorState.fromNote(this.note) : newNote = false {
|
|
||||||
_textController = TextEditingController(text: note.body);
|
|
||||||
_titleTextController = TextEditingController(text: note.title);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_textController.dispose();
|
|
||||||
_titleTextController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var markdownEditor = Column(
|
|
||||||
children: <Widget>[
|
|
||||||
if (!rawEditor) NoteTitleEditor(_titleTextController),
|
|
||||||
NoteMarkdownEditor(_textController, false),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
var rawEditorWidget = NoteMarkdownEditor(_textController, true);
|
|
||||||
|
|
||||||
var editorW = rawEditor ? rawEditorWidget : markdownEditor;
|
|
||||||
var editor = Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: SingleChildScrollView(child: editorW),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget body = editingMode ? editor : NoteViewer(note: note);
|
|
||||||
|
|
||||||
var newJournalScreen = Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
leading: IconButton(
|
|
||||||
key: const ValueKey("NewEntry"),
|
|
||||||
icon: const Icon(Icons.check),
|
|
||||||
onPressed: () {
|
|
||||||
_saveNote(context);
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
actions: <Widget>[
|
|
||||||
!rawEditor
|
|
||||||
? IconButton(
|
|
||||||
icon: editingMode
|
|
||||||
? Icon(Icons.remove_red_eye)
|
|
||||||
: Icon(Icons.edit),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
editingMode = !editingMode;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
PopupMenuButton<NoteEditorDropDownChoices>(
|
|
||||||
onSelected: (NoteEditorDropDownChoices choice) {
|
|
||||||
switch (choice) {
|
|
||||||
case NoteEditorDropDownChoices.Discard:
|
|
||||||
if (_noteModified()) {
|
|
||||||
showDialog(context: context, builder: _buildAlertDialog);
|
|
||||||
} else {
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NoteEditorDropDownChoices.SwitchEditor:
|
|
||||||
note.title = _titleTextController.text.trim();
|
|
||||||
setState(() {
|
|
||||||
if (rawEditor) {
|
|
||||||
rawEditor = false;
|
|
||||||
note.data = serializer.decode(_textController.text);
|
|
||||||
_textController.text = note.body;
|
|
||||||
_titleTextController.text = note.title;
|
|
||||||
} else {
|
|
||||||
rawEditor = true;
|
|
||||||
var noteData =
|
|
||||||
NoteData(_textController.text, note.data.props);
|
|
||||||
_textController.text = serializer.encode(noteData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemBuilder: (BuildContext context) =>
|
|
||||||
<PopupMenuEntry<NoteEditorDropDownChoices>>[
|
|
||||||
const PopupMenuItem<NoteEditorDropDownChoices>(
|
|
||||||
value: NoteEditorDropDownChoices.Discard,
|
|
||||||
child: Text('Discard'),
|
|
||||||
),
|
|
||||||
PopupMenuItem<NoteEditorDropDownChoices>(
|
|
||||||
value: NoteEditorDropDownChoices.SwitchEditor,
|
|
||||||
child: rawEditor
|
|
||||||
? const Text('Rich Editor')
|
|
||||||
: const Text('Raw Editor'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: body,
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
child: const Icon(Icons.check),
|
|
||||||
onPressed: () {
|
|
||||||
_saveNote(context);
|
|
||||||
Navigator.pop(context);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return WillPopScope(
|
|
||||||
onWillPop: () async {
|
|
||||||
_saveNote(context);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
child: newJournalScreen,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAlertDialog(BuildContext context) {
|
|
||||||
var title = newNote
|
|
||||||
? "Do you want to discard this?"
|
|
||||||
: "Do you want to ignore the changes?";
|
|
||||||
|
|
||||||
var editText = newNote ? "Keep Writing" : "Keep Editing";
|
|
||||||
var discardText = newNote ? "Discard" : "Discard Changes";
|
|
||||||
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(title),
|
|
||||||
actions: <Widget>[
|
|
||||||
FlatButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(false),
|
|
||||||
child: Text(editText),
|
|
||||||
),
|
|
||||||
FlatButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context); // Alert box
|
|
||||||
Navigator.pop(context); // Note Editor
|
|
||||||
},
|
|
||||||
child: Text(discardText),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool _noteModified() {
|
|
||||||
var noteContent = _textController.text.trim();
|
|
||||||
var titleContent = _titleTextController.text.trim();
|
|
||||||
if (noteContent.isEmpty && titleContent.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (note != null) {
|
|
||||||
if (rawEditor) {
|
|
||||||
return serializer.encode(note.data) != noteContent;
|
|
||||||
} else {
|
|
||||||
return noteContent != note.body || titleContent != note.title;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _saveNote(BuildContext context) {
|
|
||||||
if (!_noteModified()) return;
|
|
||||||
|
|
||||||
final stateContainer = StateContainer.of(context);
|
|
||||||
if (rawEditor == false) {
|
|
||||||
note.body = _textController.text.trim();
|
|
||||||
note.title = _titleTextController.text.trim();
|
|
||||||
} else {
|
|
||||||
note.data = serializer.decode(_textController.text);
|
|
||||||
}
|
|
||||||
if (!note.isEmpty()) {
|
|
||||||
newNote ? stateContainer.addNote(note) : stateContainer.updateNote(note);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NoteMarkdownEditor extends StatelessWidget {
|
|
||||||
final TextEditingController textController;
|
|
||||||
final bool useMonospace;
|
|
||||||
|
|
||||||
NoteMarkdownEditor(this.textController, this.useMonospace);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var style = Theme.of(context).textTheme.subhead;
|
|
||||||
if (useMonospace) {
|
|
||||||
style = style.copyWith(fontFamily: "Roboto Mono");
|
|
||||||
}
|
|
||||||
|
|
||||||
return TextField(
|
|
||||||
autofocus: false,
|
|
||||||
autocorrect: false,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
maxLines: null,
|
|
||||||
style: style,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Write here',
|
|
||||||
border: InputBorder.none,
|
|
||||||
isDense: true,
|
|
||||||
),
|
|
||||||
controller: textController,
|
|
||||||
textCapitalization: TextCapitalization.sentences,
|
|
||||||
scrollPadding: const EdgeInsets.all(0.0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NoteTitleEditor extends StatelessWidget {
|
|
||||||
final TextEditingController textController;
|
|
||||||
|
|
||||||
NoteTitleEditor(this.textController);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var style = Theme.of(context).textTheme.title;
|
|
||||||
|
|
||||||
return TextField(
|
|
||||||
autofocus: false,
|
|
||||||
keyboardType: TextInputType.text,
|
|
||||||
maxLines: 1,
|
|
||||||
style: style,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Title',
|
|
||||||
border: InputBorder.none,
|
|
||||||
isDense: true,
|
|
||||||
),
|
|
||||||
controller: textController,
|
|
||||||
textCapitalization: TextCapitalization.sentences,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:gitjournal/core/note.dart';
|
import 'package:gitjournal/core/note.dart';
|
||||||
import 'package:gitjournal/core/notes_folder.dart';
|
import 'package:gitjournal/core/notes_folder.dart';
|
||||||
import 'package:gitjournal/screens/journal_editor.dart';
|
import 'package:gitjournal/screens/note_editor.dart';
|
||||||
import 'package:gitjournal/screens/journal_browsing.dart';
|
|
||||||
import 'package:gitjournal/state_container.dart';
|
import 'package:gitjournal/state_container.dart';
|
||||||
import 'package:gitjournal/widgets/app_drawer.dart';
|
import 'package:gitjournal/widgets/app_drawer.dart';
|
||||||
import 'package:gitjournal/widgets/app_bar_menu_button.dart';
|
import 'package:gitjournal/widgets/app_bar_menu_button.dart';
|
||||||
@ -33,11 +32,9 @@ class JournalListingScreen extends StatelessWidget {
|
|||||||
Widget journalList = JournalList(
|
Widget journalList = JournalList(
|
||||||
notes: allNotes,
|
notes: allNotes,
|
||||||
noteSelectedFunction: (noteIndex) {
|
noteSelectedFunction: (noteIndex) {
|
||||||
|
var note = allNotes[noteIndex];
|
||||||
var route = MaterialPageRoute(
|
var route = MaterialPageRoute(
|
||||||
builder: (context) => JournalBrowsingScreen(
|
builder: (context) => NoteEditor.fromNote(note),
|
||||||
notes: allNotes,
|
|
||||||
noteIndex: noteIndex,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
Navigator.of(context).push(route);
|
Navigator.of(context).push(route);
|
||||||
},
|
},
|
||||||
@ -81,7 +78,7 @@ class JournalListingScreen extends StatelessWidget {
|
|||||||
|
|
||||||
void _newPost(BuildContext context) {
|
void _newPost(BuildContext context) {
|
||||||
var route = MaterialPageRoute(
|
var route = MaterialPageRoute(
|
||||||
builder: (context) => JournalEditor.newNote(notesFolder));
|
builder: (context) => NoteEditor.newNote(notesFolder));
|
||||||
Navigator.of(context).push(route);
|
Navigator.of(context).push(route);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,11 +143,9 @@ class NoteSearch extends SearchDelegate<Note> {
|
|||||||
Widget journalList = JournalList(
|
Widget journalList = JournalList(
|
||||||
notes: filteredNotes,
|
notes: filteredNotes,
|
||||||
noteSelectedFunction: (noteIndex) {
|
noteSelectedFunction: (noteIndex) {
|
||||||
|
var note = filteredNotes[noteIndex];
|
||||||
var route = MaterialPageRoute(
|
var route = MaterialPageRoute(
|
||||||
builder: (context) => JournalBrowsingScreen(
|
builder: (context) => NoteEditor.fromNote(note),
|
||||||
notes: filteredNotes,
|
|
||||||
noteIndex: noteIndex,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
Navigator.of(context).push(route);
|
Navigator.of(context).push(route);
|
||||||
},
|
},
|
||||||
|
230
lib/screens/note_editor.dart
Normal file
230
lib/screens/note_editor.dart
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fimber/fimber.dart';
|
||||||
|
|
||||||
|
import 'package:gitjournal/core/note.dart';
|
||||||
|
import 'package:gitjournal/core/notes_folder.dart';
|
||||||
|
import 'package:gitjournal/editors/markdown_editor.dart';
|
||||||
|
import 'package:gitjournal/editors/raw_editor.dart';
|
||||||
|
import 'package:gitjournal/state_container.dart';
|
||||||
|
import 'package:gitjournal/core/note_data_serializers.dart';
|
||||||
|
import 'package:gitjournal/utils.dart';
|
||||||
|
import 'package:gitjournal/widgets/rename_dialog.dart';
|
||||||
|
|
||||||
|
enum NoteEditorDropDownChoices { Discard, SwitchEditor }
|
||||||
|
|
||||||
|
class NoteEditor extends StatefulWidget {
|
||||||
|
final Note note;
|
||||||
|
final NotesFolder notesFolder;
|
||||||
|
|
||||||
|
NoteEditor.fromNote(this.note) : notesFolder = null;
|
||||||
|
NoteEditor.newNote(this.notesFolder) : note = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
NoteEditorState createState() {
|
||||||
|
if (note == null) {
|
||||||
|
return NoteEditorState.newNote(notesFolder);
|
||||||
|
} else {
|
||||||
|
return NoteEditorState.fromNote(note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EditorType { Markdown, Raw }
|
||||||
|
|
||||||
|
class NoteEditorState extends State<NoteEditor> {
|
||||||
|
Note note;
|
||||||
|
EditorType editorType = EditorType.Markdown;
|
||||||
|
String noteSerialized = "";
|
||||||
|
|
||||||
|
final _rawEditorKey = GlobalKey<RawEditorState>();
|
||||||
|
final _markdownEditorKey = GlobalKey<MarkdownEditorState>();
|
||||||
|
|
||||||
|
bool get _isNewNote {
|
||||||
|
return widget.note == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
NoteEditorState.newNote(NotesFolder folder) {
|
||||||
|
note = Note.newNote(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
NoteEditorState.fromNote(this.note) {
|
||||||
|
var serializer = MarkdownYAMLSerializer();
|
||||||
|
noteSerialized = serializer.encode(note.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () async {
|
||||||
|
_saveNote(_getNoteFromEditor());
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
child: _getEditor(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _getEditor() {
|
||||||
|
switch (editorType) {
|
||||||
|
case EditorType.Markdown:
|
||||||
|
return MarkdownEditor(
|
||||||
|
key: _markdownEditorKey,
|
||||||
|
note: note,
|
||||||
|
noteDeletionSelected: _noteDeletionSelected,
|
||||||
|
noteEditorChooserSelected: _noteEditorChooserSelected,
|
||||||
|
exitEditorSelected: _exitEditorSelected,
|
||||||
|
renameNoteSelected: _renameNoteSelected,
|
||||||
|
autofocusOnEditor: _isNewNote,
|
||||||
|
);
|
||||||
|
case EditorType.Raw:
|
||||||
|
return RawEditor(
|
||||||
|
key: _rawEditorKey,
|
||||||
|
note: note,
|
||||||
|
noteDeletionSelected: _noteDeletionSelected,
|
||||||
|
noteEditorChooserSelected: _noteEditorChooserSelected,
|
||||||
|
exitEditorSelected: _exitEditorSelected,
|
||||||
|
renameNoteSelected: _renameNoteSelected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _noteEditorChooserSelected(Note _note) async {
|
||||||
|
var newEditorType = await showDialog<EditorType>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
var children = <Widget>[
|
||||||
|
RadioListTile<EditorType>(
|
||||||
|
title: const Text("Markdown Editor"),
|
||||||
|
value: EditorType.Markdown,
|
||||||
|
groupValue: editorType,
|
||||||
|
onChanged: (EditorType et) => Navigator.of(context).pop(et),
|
||||||
|
),
|
||||||
|
RadioListTile<EditorType>(
|
||||||
|
title: const Text("Raw Editor"),
|
||||||
|
value: EditorType.Raw,
|
||||||
|
groupValue: editorType,
|
||||||
|
onChanged: (EditorType et) => Navigator.of(context).pop(et),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text("Choose Editor"),
|
||||||
|
content: Column(
|
||||||
|
children: children,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newEditorType != null) {
|
||||||
|
setState(() {
|
||||||
|
note = _note;
|
||||||
|
editorType = newEditorType;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _exitEditorSelected(Note note) {
|
||||||
|
_saveNote(note);
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _renameNoteSelected(Note _note) async {
|
||||||
|
var fileName = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => RenameDialog(
|
||||||
|
oldPath: note.filePath,
|
||||||
|
inputDecoration: 'File Name',
|
||||||
|
dialogTitle: "Rename File",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (fileName is String) {
|
||||||
|
if (_isNewNote) {
|
||||||
|
setState(() {
|
||||||
|
note = _note;
|
||||||
|
note.rename(fileName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
final container = StateContainer.of(context);
|
||||||
|
container.renameNote(note, fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _noteDeletionSelected(Note note) {
|
||||||
|
if (_isNewNote && !_noteModified(note)) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog(context: context, builder: _buildAlertDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _deleteNote(Note note) {
|
||||||
|
if (_isNewNote) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final stateContainer = StateContainer.of(context);
|
||||||
|
stateContainer.removeNote(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAlertDialog(BuildContext context) {
|
||||||
|
var title = "Do you want to delete this note?";
|
||||||
|
var editText = "Keep Writing";
|
||||||
|
var discardText = "Discard";
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(title),
|
||||||
|
actions: <Widget>[
|
||||||
|
FlatButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: Text(editText),
|
||||||
|
),
|
||||||
|
FlatButton(
|
||||||
|
onPressed: () {
|
||||||
|
_deleteNote(note);
|
||||||
|
|
||||||
|
Navigator.pop(context); // Alert box
|
||||||
|
Navigator.pop(context); // Note Editor
|
||||||
|
|
||||||
|
if (!_isNewNote) {
|
||||||
|
Fimber.d("Showing an undo snackbar");
|
||||||
|
|
||||||
|
final stateContainer = StateContainer.of(context);
|
||||||
|
showUndoDeleteSnackbar(context, stateContainer, note);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(discardText),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _noteModified(Note note) {
|
||||||
|
if (_isNewNote) {
|
||||||
|
return note.title.isNotEmpty || note.body.isNotEmpty;
|
||||||
|
}
|
||||||
|
var serializer = MarkdownYAMLSerializer();
|
||||||
|
var finalNoteSerialized = serializer.encode(note.data);
|
||||||
|
return finalNoteSerialized != noteSerialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _saveNote(Note note) {
|
||||||
|
if (!_noteModified(note)) return;
|
||||||
|
|
||||||
|
print("Note modified - saving");
|
||||||
|
final stateContainer = StateContainer.of(context);
|
||||||
|
_isNewNote ? stateContainer.addNote(note) : stateContainer.updateNote(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
Note _getNoteFromEditor() {
|
||||||
|
switch (editorType) {
|
||||||
|
case EditorType.Markdown:
|
||||||
|
return _markdownEditorKey.currentState.getNote();
|
||||||
|
case EditorType.Raw:
|
||||||
|
return _rawEditorKey.currentState.getNote();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -151,11 +151,6 @@ class StateContainerState extends State<StateContainer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void renameNote(Note note, String newFileName) async {
|
void renameNote(Note note, String newFileName) async {
|
||||||
// Do not let the user rename it to a non-markdown file
|
|
||||||
if (!newFileName.toLowerCase().endsWith('.md')) {
|
|
||||||
newFileName += '.md';
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldNotePath = note.filePath;
|
var oldNotePath = note.filePath;
|
||||||
note.rename(newFileName);
|
note.rename(newFileName);
|
||||||
|
|
||||||
@ -181,8 +176,8 @@ class StateContainerState extends State<StateContainer> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void undoRemoveNote(Note note, int index) {
|
void undoRemoveNote(Note note) {
|
||||||
note.parent.insert(index, note);
|
note.parent.insert(0, note);
|
||||||
_gitRepo.resetLastCommit().then((NoteRepoResult _) {
|
_gitRepo.resetLastCommit().then((NoteRepoResult _) {
|
||||||
syncNotes();
|
syncNotes();
|
||||||
});
|
});
|
||||||
|
@ -26,7 +26,6 @@ void showUndoDeleteSnackbar(
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
StateContainerState stateContainer,
|
StateContainerState stateContainer,
|
||||||
Note deletedNote,
|
Note deletedNote,
|
||||||
int deletedNoteIndex,
|
|
||||||
) {
|
) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ void showUndoDeleteSnackbar(
|
|||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Fimber.d("Undoing delete");
|
Fimber.d("Undoing delete");
|
||||||
stateContainer.undoRemoveNote(deletedNote, deletedNoteIndex);
|
stateContainer.undoRemoveNote(deletedNote);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).show(context);
|
).show(context);
|
||||||
|
@ -64,7 +64,7 @@ class JournalList extends StatelessWidget {
|
|||||||
final stateContainer = StateContainer.of(context);
|
final stateContainer = StateContainer.of(context);
|
||||||
stateContainer.removeNote(note);
|
stateContainer.removeNote(note);
|
||||||
|
|
||||||
showUndoDeleteSnackbar(context, stateContainer, note, i);
|
showUndoDeleteSnackbar(context, stateContainer, note);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -47,7 +47,7 @@ class _RenameDialogState extends State<RenameDialog> {
|
|||||||
FileSystemEntityType.notFound) {
|
FileSystemEntityType.notFound) {
|
||||||
return 'Already Exists';
|
return 'Already Exists';
|
||||||
}
|
}
|
||||||
return "";
|
return null;
|
||||||
},
|
},
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
|
Reference in New Issue
Block a user