diff --git a/lib/gitapp.dart b/lib/gitapp.dart index c3794124..844b2132 100644 --- a/lib/gitapp.dart +++ b/lib/gitapp.dart @@ -26,23 +26,37 @@ buildGitButtons() { ), RaisedButton( child: Text("Git Clone"), - onPressed: gitClone, + onPressed: () async { + gitClone("root@bcn.vhanda.in:git/test", "journal"); + }, ), RaisedButton( child: Text("Git Pull"), - onPressed: gitPull, + onPressed: () async { + gitPull("journal"); + }, ), RaisedButton( child: Text("Git Add"), - onPressed: gitAdd, + onPressed: () async { + await gitAdd("journal", "1"); + }, ), RaisedButton( child: Text("Git Push"), - onPressed: gitPush, + onPressed: () async { + gitPush("journal"); + }, ), RaisedButton( - child: Text("Git Commit"), - onPressed: gitCommit, - ), + child: Text("Git Commit"), + onPressed: () async { + gitCommit( + gitFolder: "journal", + authorEmail: "noemail@example.com", + authorName: "Vishesh Handa", + message: "Default message from GitJournal", + ); + }), ]; } diff --git a/lib/main.dart b/lib/main.dart index a41bc0e3..00941d89 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,24 +5,9 @@ import 'package:journal/app.dart'; import 'package:journal/gitapp.dart'; import 'package:journal/state_container.dart'; -/* -import 'note.dart'; - -Future> fetchNotes() async { - final response = await http.get('http://192.168.1.132:8000/notes'); - final responseJson = json.decode(response.body); - - var notes = []; - for (var postJson in responseJson) { - notes.add(new Note.fromJson(postJson)); - } - - return notes; -} -*/ - void main() { runApp(new StateContainer( - child: GitApp(), + child: JournalApp(), + //child: GitApp(), )); } diff --git a/lib/note.dart b/lib/note.dart index b988b3be..59aac733 100644 --- a/lib/note.dart +++ b/lib/note.dart @@ -49,21 +49,34 @@ class Note implements Comparable { @override String toString() { - return 'Note{id: $id, body: $body, createdAt: $created}'; + return 'Note{id: $id, body: $body, created: $created}'; } @override - int compareTo(other) => created.compareTo(other.created); + int compareTo(other) { + if (other == null) { + return -1; + } + return created.compareTo(other.created); + } } class AppState { - bool isLoading; + bool isLoadingFromDisk; + bool localStateModified; + + bool isLoadingRemoteState; + bool remoteStateModified; + List notes; AppState({ - this.isLoading = false, + this.isLoadingFromDisk = false, + this.localStateModified = false, + this.isLoadingRemoteState = false, + this.remoteStateModified = false, this.notes = const [], }); - factory AppState.loading() => AppState(isLoading: true); + //factory AppState.loading() => AppState(isLoading: true); } diff --git a/lib/state_container.dart b/lib/state_container.dart index 606a2183..7d39a00f 100644 --- a/lib/state_container.dart +++ b/lib/state_container.dart @@ -9,12 +9,12 @@ import 'package:uuid/uuid.dart'; import 'package:journal/note.dart'; import 'package:journal/storage/serializers.dart'; import 'package:journal/storage/notes_repository.dart'; -import 'package:journal/storage/file_storage.dart'; +import 'package:journal/storage/git_storage.dart'; import 'package:journal/storage/git.dart'; Future getNotesDir() async { var appDir = await getGitBaseDirectory(); - var dir = new Directory(p.join(appDir.path, "notes")); + var dir = new Directory(p.join(appDir.path, "journal")); await dir.create(); return dir; @@ -40,56 +40,80 @@ class StateContainer extends StatefulWidget { } class StateContainerState extends State { - AppState appState = AppState.loading(); - NoteRepository noteRepo = new FileStorage( + AppState appState = AppState(); + + NoteRepository noteRepo = new GitNoteRepository( getDirectory: getNotesDir, - noteSerializer: new MarkdownYAMLSerializer(), - fileNameGenerator: (Note note) => note.id, + dirName: "journal", + gitCloneUrl: "root@bcn.vhanda.in:git/test", ); @override void initState() { super.initState(); + _loadNotesFromDisk(); + _syncNotes(); + } + + void _loadNotesFromDisk() { + print("Loading Notes From Disk"); + appState.isLoadingFromDisk = true; noteRepo.listNotes().then((loadedNotes) { setState(() { - appState = AppState(notes: loadedNotes); + appState.isLoadingFromDisk = false; + appState.notes = loadedNotes; }); - }).catchError((err) { + }).catchError((err, stack) { setState(() { - print("Load Notes Error:"); - print(err); - appState.isLoading = false; + print("Load Notes From Disk Error: " + err.toString()); + print(stack.toString()); + appState.isLoadingFromDisk = false; }); }); } - void addNote(Note note) { - setState(() { - note.id = new Uuid().v4(); - appState.notes.insert(0, note); - noteRepo.addNote(note); + void _syncNotes() { + print("Starting to syncNOtes"); + this.noteRepo.sync().then((loaded) { + print("NotesRepo Synced: " + loaded.toString()); + _loadNotesFromDisk(); + }).catchError((err) { + print("NotesRepo Sync: " + err.toString()); }); } + void addNote(Note note) { + insertNote(0, note); + } + void removeNote(Note note) { setState(() { appState.notes.remove(note); - noteRepo.removeNote(note); + noteRepo.removeNote(note).then((NoteRepoResult _) { + _syncNotes(); + }); }); } void insertNote(int index, Note note) { setState(() { + print("insertNote: " + note.toString()); + if (note.id == null || note.id.isEmpty) { + note.id = new Uuid().v4(); + } appState.notes.insert(index, note); - noteRepo.addNote(note); + noteRepo.addNote(note).then((NoteRepoResult _) { + _syncNotes(); + }); }); } - // FIXME: Implement this! void updateNote(Note note) { setState(() { - noteRepo.updateNote(note); + noteRepo.updateNote(note).then((NoteRepoResult _) { + _syncNotes(); + }); }); } diff --git a/lib/storage/file_storage.dart b/lib/storage/file_storage.dart index da9c0e84..bb3230bf 100644 --- a/lib/storage/file_storage.dart +++ b/lib/storage/file_storage.dart @@ -29,6 +29,13 @@ class FileStorage implements NoteRepository { var lister = dir.list(recursive: false); await for (var fileEntity in lister) { Note note = await _loadNote(fileEntity); + if (note == null) { + continue; + } + if (note.id == null) { + String filename = p.basename(fileEntity.path); + note.id = filename; + } notes.add(note); } @@ -47,30 +54,33 @@ class FileStorage implements NoteRepository { } @override - Future addNote(Note note) async { + Future addNote(Note note) async { final dir = await getDirectory(); var filePath = p.join(dir.path, fileNameGenerator(note)); var file = new File(filePath); + if (file == null) { + return NoteRepoResult(error: true); + } var contents = noteSerializer.encode(note); await file.writeAsString(contents); - return true; + return NoteRepoResult(noteFilePath: filePath, error: false); } @override - Future removeNote(Note note) async { + Future removeNote(Note note) async { final dir = await getDirectory(); var filePath = p.join(dir.path, fileNameGenerator(note)); var file = new File(filePath); await file.delete(); - return true; + return NoteRepoResult(noteFilePath: filePath, error: false); } @override - Future updateNote(Note note) async { + Future updateNote(Note note) async { return addNote(note); } @@ -81,8 +91,8 @@ class FileStorage implements NoteRepository { Future saveNotes(List notes) async { final dir = await getDirectory(); - await dir.delete(recursive: true); - await dir.create(); + // FIXME: Why do we need to delete everything? + // await dir.delete(recursive: true); for (var note in notes) { var filePath = p.join(dir.path, fileNameGenerator(note)); diff --git a/lib/storage/git.dart b/lib/storage/git.dart index 2c428714..fdc504bb 100644 --- a/lib/storage/git.dart +++ b/lib/storage/git.dart @@ -13,12 +13,12 @@ Future getGitBaseDirectory() async { return new Directory(path); } -Future gitClone() async { +Future gitClone(String cloneUrl, String folderName) async { print("Going to git clone"); try { await _platform.invokeMethod('gitClone', { - 'cloneUrl': "root@bcn.vhanda.in:git/test", - 'folderName': "journal", + 'cloneUrl': cloneUrl, + 'folderName': folderName, }); print("Done"); } on PlatformException catch (e) { @@ -36,11 +36,11 @@ Future generateSSHKeys() async { } } -Future gitPull() async { +Future gitPull(String folderName) async { print("Going to git pull"); try { await _platform.invokeMethod('gitPull', { - 'folderName': "journal", + 'folderName': folderName, }); print("Done"); } on PlatformException catch (e) { @@ -48,12 +48,12 @@ Future gitPull() async { } } -Future gitAdd() async { - print("Going to git add"); +Future gitAdd(String gitFolder, String filePattern) async { + print("Going to git add: " + filePattern); try { await _platform.invokeMethod('gitAdd', { - 'folderName': "journal", - 'filePattern': ".", + 'folderName': gitFolder, + 'filePattern': filePattern, }); print("Done"); } on PlatformException catch (e) { @@ -61,12 +61,12 @@ Future gitAdd() async { } } -Future gitRm() async { +Future gitRm(String gitFolder, String filePattern) async { print("Going to git rm"); try { await _platform.invokeMethod('gitRm', { - 'folderName': "journal", - 'filePattern': "1", + 'folderName': gitFolder, + 'filePattern': filePattern, }); print("Done"); } on PlatformException catch (e) { @@ -74,11 +74,11 @@ Future gitRm() async { } } -Future gitPush() async { +Future gitPush(String folderName) async { print("Going to git push"); try { await _platform.invokeMethod('gitPush', { - 'folderName': "journal", + 'folderName': folderName, }); print("Done"); } on PlatformException catch (e) { @@ -86,14 +86,19 @@ Future gitPush() async { } } -Future gitCommit() async { +Future gitCommit({ + String gitFolder, + String authorName, + String authorEmail, + String message, +}) async { print("Going to git commit"); try { await _platform.invokeMethod('gitCommit', { - 'folderName': "journal", - 'authorName': "Vishesh Handa", - 'authorEmail': "noemail@example.com", - 'message': "Default message from GitJournal", + 'folderName': gitFolder, + 'authorName': authorName, + 'authorEmail': authorEmail, + 'message': message, }); print("Done"); } on PlatformException catch (e) { diff --git a/lib/storage/git_storage.dart b/lib/storage/git_storage.dart new file mode 100644 index 00000000..8e2b342c --- /dev/null +++ b/lib/storage/git_storage.dart @@ -0,0 +1,108 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:path/path.dart' as p; + +import 'package:journal/note.dart'; +import 'package:journal/storage/git.dart'; +import 'package:journal/storage/serializers.dart'; +import 'package:journal/storage/file_storage.dart'; +import 'package:journal/storage/notes_repository.dart'; + +class GitNoteRepository implements NoteRepository { + final FileStorage _fileStorage; + final String gitCloneUrl; + final String dirName; + + bool cloned = false; + bool checkForCloned = false; + + final Future Function() getDirectory; + + GitNoteRepository({ + @required this.gitCloneUrl, + @required this.dirName, + @required this.getDirectory, + }) : _fileStorage = FileStorage( + noteSerializer: new MarkdownYAMLSerializer(), + fileNameGenerator: (Note note) => note.id, + getDirectory: getDirectory, + ) { + // FIXME: This isn't correct. The gitUrl might not be cloned at this point! + } + + @override + Future addNote(Note note) async { + print("Calling gitStorage addNote"); + var result = await _fileStorage.addNote(note); + if (result.error) { + return result; + } + + var baseDir = await this.getDirectory(); + var filePath = result.noteFilePath.replaceFirst(baseDir.path + "/", ""); + + await gitAdd(this.dirName, filePath); + await gitCommit( + gitFolder: this.dirName, + authorEmail: "noemail@example.com", + authorName: "Vishesh Handa", + message: "Added Journal entry", + ); + + return result; + } + + @override + Future removeNote(Note note) async { + var result = await _fileStorage.addNote(note); + if (result.error) { + return result; + } + + var baseDir = await this.getDirectory(); + var filePath = result.noteFilePath.replaceFirst(baseDir.path + "/", ""); + + await gitRm(this.dirName, filePath); + await gitCommit( + gitFolder: this.dirName, + authorEmail: "noemail@example.com", + authorName: "Vishesh Handa", + message: "Added Journal entry", + ); + + return result; + } + + @override + Future updateNote(Note note) async { + return this.addNote(note); + } + + @override + Future> listNotes() { + return _fileStorage.listNotes(); + } + + @override + Future sync() async { + print("Starting Sync"); + if (!checkForCloned) { + var baseDir = await this.getDirectory(); + var dotGitDir = new Directory(p.join(baseDir.path, ".git")); + cloned = await dotGitDir.exists(); + checkForCloned = true; + } + if (!cloned) { + await gitClone(this.gitCloneUrl, this.dirName); + cloned = true; + return true; + } + + await gitPull(this.dirName); + await gitPush(this.dirName); + + return true; + } +} diff --git a/lib/storage/notes_repository.dart b/lib/storage/notes_repository.dart index b8de586e..675d273c 100644 --- a/lib/storage/notes_repository.dart +++ b/lib/storage/notes_repository.dart @@ -1,13 +1,25 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; + import 'package:journal/note.dart'; +class NoteRepoResult { + bool error; + String noteFilePath; + + NoteRepoResult({ + @required this.error, + this.noteFilePath, + }); +} + abstract class NoteRepository { // TODO: Better error message! Future sync(); - Future addNote(Note note); - Future updateNote(Note note); - Future removeNote(Note note); + Future addNote(Note note); + Future updateNote(Note note); + Future removeNote(Note note); Future> listNotes(); }