Connect the AppState to the Folders

Now when adding/editing/removing a note, it gets modified from the
directory it was present in. There is no longer a just a plain list of
all notes, but always a tree of notes, which are inside Folders.
This commit is contained in:
Vishesh Handa
2019-12-04 15:14:17 +01:00
parent 10d65cd6fb
commit 5b8fc6342a
9 changed files with 48 additions and 56 deletions

View File

@ -1,6 +1,5 @@
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:fimber/fimber.dart'; import 'package:fimber/fimber.dart';
import 'package:gitjournal/core/note.dart';
import 'package:gitjournal/core/notes_folder.dart'; import 'package:gitjournal/core/notes_folder.dart';
class AppState { class AppState {
@ -24,11 +23,10 @@ class AppState {
String gitBaseDirectory = ""; String gitBaseDirectory = "";
bool get hasJournalEntries { bool get hasJournalEntries {
return notes.isNotEmpty; return notesFolder.hasNotes;
} }
List<Note> notes = []; NotesFolder notesFolder;
NotesFolder noteFolder;
AppState(SharedPreferences pref) { AppState(SharedPreferences pref) {
localGitRepoConfigured = pref.getBool("localGitRepoConfigured") ?? false; localGitRepoConfigured = pref.getBool("localGitRepoConfigured") ?? false;

View File

@ -3,6 +3,8 @@ import 'dart:io';
import 'package:gitjournal/storage/serializers.dart'; import 'package:gitjournal/storage/serializers.dart';
import 'package:gitjournal/utils/datetime.dart'; import 'package:gitjournal/utils/datetime.dart';
import 'notes_folder.dart';
enum NoteLoadState { enum NoteLoadState {
None, None,
Loading, Loading,
@ -11,7 +13,9 @@ enum NoteLoadState {
} }
class Note implements Comparable<Note> { class Note implements Comparable<Note> {
NotesFolder parent;
String filePath = ""; String filePath = "";
DateTime _created; DateTime _created;
NoteData _data = NoteData(); NoteData _data = NoteData();
@ -20,7 +24,7 @@ class Note implements Comparable<Note> {
var _loadState = NoteLoadState.None; var _loadState = NoteLoadState.None;
var _serializer = MarkdownYAMLSerializer(); var _serializer = MarkdownYAMLSerializer();
Note([this.filePath]) { Note(this.parent, [this.filePath]) {
_created = _created ?? DateTime(0, 0, 0, 0, 0, 0, 0, 0); _created = _created ?? DateTime(0, 0, 0, 0, 0, 0, 0, 0);
} }

View File

@ -1,14 +1,11 @@
import 'note.dart'; import 'note.dart';
import 'notes_folder.dart'; import 'notes_folder.dart';
// FIXME: Maybe the parent should be a part of the Note, and the NoteFolder
// or maybe also a part of the NoteFolder
class NoteFSEntity { class NoteFSEntity {
NotesFolder parent;
NotesFolder folder; NotesFolder folder;
Note note; Note note;
NoteFSEntity(this.parent, {this.folder, this.note}) { NoteFSEntity({this.folder, this.note}) {
assert(folder != null || note != null); assert(folder != null || note != null);
} }

View File

@ -15,11 +15,11 @@ class FolderListingScreen extends StatelessWidget {
final appState = container.appState; final appState = container.appState;
var treeView = FolderTreeView( var treeView = FolderTreeView(
rootFolder: appState.noteFolder, rootFolder: appState.notesFolder,
onFolderSelected: (NotesFolder folder) { onFolderSelected: (NotesFolder folder) {
var route = MaterialPageRoute( var route = MaterialPageRoute(
builder: (context) => JournalListingScreen( builder: (context) => JournalListingScreen(
noteFolder: folder, notesFolder: folder,
), ),
); );
Navigator.of(context).push(route); Navigator.of(context).push(route);

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; 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/state_container.dart'; import 'package:gitjournal/state_container.dart';
import 'package:gitjournal/widgets/journal_editor_header.dart'; import 'package:gitjournal/widgets/journal_editor_header.dart';
import 'package:gitjournal/storage/serializers.dart'; import 'package:gitjournal/storage/serializers.dart';
@ -8,14 +9,15 @@ enum NoteEditorDropDownChoices { Discard, SwitchEditor }
class JournalEditor extends StatefulWidget { class JournalEditor extends StatefulWidget {
final Note note; final Note note;
final NotesFolder notesFolder;
JournalEditor() : note = null; JournalEditor.fromNote(this.note) : notesFolder = null;
JournalEditor.fromNote(this.note); JournalEditor.newNote(this.notesFolder) : note = null;
@override @override
JournalEditorState createState() { JournalEditorState createState() {
if (note == null) { if (note == null) {
return JournalEditorState(); return JournalEditorState.newNote(notesFolder);
} else { } else {
return JournalEditorState.fromNote(note); return JournalEditorState.fromNote(note);
} }
@ -23,13 +25,14 @@ class JournalEditor extends StatefulWidget {
} }
class JournalEditorState extends State<JournalEditor> { class JournalEditorState extends State<JournalEditor> {
Note note = Note(); Note note;
final bool newNote; final bool newNote;
TextEditingController _textController = TextEditingController(); TextEditingController _textController = TextEditingController();
bool rawEditor = false; bool rawEditor = false;
final serializer = MarkdownYAMLSerializer(); final serializer = MarkdownYAMLSerializer();
JournalEditorState() : newNote = true { JournalEditorState.newNote(NotesFolder folder) : newNote = true {
note = Note(folder);
note.created = DateTime.now(); note.created = DateTime.now();
} }

View File

@ -12,10 +12,10 @@ import 'package:gitjournal/widgets/journal_list.dart';
import 'package:gitjournal/themes.dart'; import 'package:gitjournal/themes.dart';
class JournalListingScreen extends StatelessWidget { class JournalListingScreen extends StatelessWidget {
final NotesFolder noteFolder; final NotesFolder notesFolder;
final bool recursive; final bool recursive;
JournalListingScreen({@required this.noteFolder, this.recursive = false}); JournalListingScreen({@required this.notesFolder, this.recursive = false});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -27,7 +27,9 @@ class JournalListingScreen extends StatelessWidget {
child: Icon(Icons.add), child: Icon(Icons.add),
); );
var allNotes = recursive ? noteFolder.getAllNotes() : noteFolder.getNotes(); var allNotes =
recursive ? notesFolder.getAllNotes() : notesFolder.getNotes();
allNotes.sort((a, b) => b.compareTo(a));
Widget journalList = JournalList( Widget journalList = JournalList(
notes: allNotes, notes: allNotes,
@ -76,7 +78,8 @@ class JournalListingScreen extends StatelessWidget {
} }
void _newPost(BuildContext context) { void _newPost(BuildContext context) {
var route = MaterialPageRoute(builder: (context) => JournalEditor()); var route = MaterialPageRoute(
builder: (context) => JournalEditor.newNote(notesFolder));
Navigator.of(context).push(route); Navigator.of(context).push(route);
} }
} }

View File

@ -57,7 +57,7 @@ class StateContainerState extends State<StateContainer> {
dirName: appState.localGitRepoPath, dirName: appState.localGitRepoPath,
); );
} }
appState.noteFolder = NotesFolder(noteRepo.notesBasePath); appState.notesFolder = NotesFolder(null, noteRepo.notesBasePath);
// Just a fail safe // Just a fail safe
if (!appState.remoteGitRepoConfigured) { if (!appState.remoteGitRepoConfigured) {
@ -79,25 +79,16 @@ class StateContainerState extends State<StateContainer> {
} }
} }
Future<List<Note>> _loadNotes() async { Future<void> _loadNotes() async {
await appState.noteFolder.loadRecursively(); await appState.notesFolder.loadRecursively();
var notes = appState.noteFolder.getAllNotes();
notes.sort((a, b) => b.compareTo(a));
return notes;
} }
void _loadNotesFromDisk() { void _loadNotesFromDisk() {
Fimber.d("Loading Notes From Disk"); Fimber.d("Loading Notes From Disk");
_loadNotes().then((loadedNotes) { _loadNotes().then((void _) {
setState(() { setState(() {
appState.notes = loadedNotes;
getAnalytics().logEvent( getAnalytics().logEvent(
name: "notes_loaded", name: "notes_loaded",
parameters: <String, dynamic>{
'count': loadedNotes.length,
},
); );
}); });
}).catchError((err, stack) { }).catchError((err, stack) {
@ -124,9 +115,9 @@ class StateContainerState extends State<StateContainer> {
await noteRepo.sync(); await noteRepo.sync();
try { try {
var loadedNotes = await _loadNotes(); await _loadNotes();
setState(() { setState(() {
appState.notes = loadedNotes; // TODO: Inform exactly what notes have changed?
}); });
} catch (err, stack) { } catch (err, stack) {
setState(() { setState(() {
@ -159,8 +150,8 @@ class StateContainerState extends State<StateContainer> {
void removeNote(Note note) { void removeNote(Note note) {
setState(() { setState(() {
appState.notes.remove(note); note.parent.remove(note);
noteRepo.removeNote(note).then((NoteRepoResult _) async { noteRepo.removeNote(note.filePath).then((NoteRepoResult _) async {
// FIXME: Is there a way of figuring this amount dynamically? // FIXME: Is there a way of figuring this amount dynamically?
// The '4 seconds' is taken from snack_bar.dart -> _kSnackBarDisplayDuration // The '4 seconds' is taken from snack_bar.dart -> _kSnackBarDisplayDuration
// We wait an aritfical amount of time, so that the user has a change to undo // We wait an aritfical amount of time, so that the user has a change to undo
@ -173,7 +164,7 @@ class StateContainerState extends State<StateContainer> {
void undoRemoveNote(Note note, int index) { void undoRemoveNote(Note note, int index) {
setState(() { setState(() {
appState.notes.insert(index, note); note.parent.insert(index, note);
noteRepo.resetLastCommit().then((NoteRepoResult _) { noteRepo.resetLastCommit().then((NoteRepoResult _) {
_syncNotes(); _syncNotes();
}); });
@ -181,12 +172,15 @@ class StateContainerState extends State<StateContainer> {
} }
void insertNote(int index, Note note) { void insertNote(int index, Note note) {
Fimber.d("State Container insertNote"); Fimber.d("State Container insertNote " + index.toString());
setState(() { setState(() {
if (note.filePath == null || note.filePath.isEmpty) { if (note.filePath == null || note.filePath.isEmpty) {
note.filePath = p.join(noteRepo.notesBasePath, getFileName(note)); var parentPath = note.parent != null
? note.parent.folderPath
: noteRepo.notesBasePath;
note.filePath = p.join(parentPath, getFileName(note));
} }
appState.notes.insert(index, note); note.parent.insert(index, note);
noteRepo.addNote(note).then((NoteRepoResult _) { noteRepo.addNote(note).then((NoteRepoResult _) {
_syncNotes(); _syncNotes();
}); });
@ -196,14 +190,7 @@ class StateContainerState extends State<StateContainer> {
void updateNote(Note note) { void updateNote(Note note) {
Fimber.d("State Container updateNote"); Fimber.d("State Container updateNote");
setState(() { setState(() {
// Update that specific note // Update the git repo
for (var i = 0; i < appState.notes.length; i++) {
var n = appState.notes[i];
if (n.filePath == note.filePath) {
appState.notes[i] = note;
}
}
noteRepo.updateNote(note).then((NoteRepoResult _) { noteRepo.updateNote(note).then((NoteRepoResult _) {
_syncNotes(); _syncNotes();
}); });
@ -225,7 +212,7 @@ class StateContainerState extends State<StateContainer> {
baseDirectory: appState.gitBaseDirectory, baseDirectory: appState.gitBaseDirectory,
dirName: appState.remoteGitRepoFolderName, dirName: appState.remoteGitRepoFolderName,
); );
appState.noteFolder = NotesFolder(noteRepo.notesBasePath); appState.notesFolder = NotesFolder(null, noteRepo.notesBasePath);
await _persistConfig(); await _persistConfig();
_loadNotesFromDisk(); _loadNotesFromDisk();

View File

@ -50,9 +50,9 @@ class GitNoteRepository {
return NoteRepoResult(noteFilePath: note.filePath, error: false); return NoteRepoResult(noteFilePath: note.filePath, error: false);
} }
Future<NoteRepoResult> removeNote(Note note) async { Future<NoteRepoResult> removeNote(String noteFilePath) async {
var gitDir = p.join(baseDirectory, dirName); var gitDir = p.join(baseDirectory, dirName);
var pathSpec = note.filePath.replaceFirst(gitDir, "").substring(1); var pathSpec = noteFilePath.replaceFirst(gitDir, "").substring(1);
// We are not calling note.remove() as gitRm will also remove the file // We are not calling note.remove() as gitRm will also remove the file
await _gitRepo.rm(pathSpec); await _gitRepo.rm(pathSpec);
@ -60,7 +60,7 @@ class GitNoteRepository {
message: "Removed Journal entry", message: "Removed Journal entry",
); );
return NoteRepoResult(noteFilePath: note.filePath, error: false); return NoteRepoResult(noteFilePath: noteFilePath, error: false);
} }
Future<NoteRepoResult> resetLastCommit() async { Future<NoteRepoResult> resetLastCommit() async {

View File

@ -25,11 +25,11 @@ void main() {
n1Path = p.join(tempDir.path, "1.md"); n1Path = p.join(tempDir.path, "1.md");
n2Path = p.join(tempDir.path, "2.md"); n2Path = p.join(tempDir.path, "2.md");
var n1 = Note(n1Path); var n1 = Note(null, n1Path);
n1.body = "test"; n1.body = "test";
n1.created = dt; n1.created = dt;
var n2 = Note(n2Path); var n2 = Note(null, n2Path);
n2.data = NoteData("test2", props); n2.data = NoteData("test2", props);
notes = [n1, n2]; notes = [n1, n2];
@ -49,7 +49,7 @@ void main() {
var loadedNotes = <Note>[]; var loadedNotes = <Note>[];
await Future.forEach(notes, (origNote) async { await Future.forEach(notes, (origNote) async {
var note = Note(origNote.filePath); var note = Note(null, origNote.filePath);
var r = await note.load(); var r = await note.load();
expect(r, NoteLoadState.Loaded); expect(r, NoteLoadState.Loaded);