diff --git a/lib/note.dart b/lib/note.dart index 5761f27c..6132fa59 100644 --- a/lib/note.dart +++ b/lib/note.dart @@ -13,18 +13,17 @@ enum NoteLoadState { class Note implements Comparable<Note> { String filePath = ""; DateTime created; - String body = ""; + NoteData data = NoteData(); var _loadState = NoteLoadState.None; var _serializer = MarkdownYAMLSerializer(); - // FIXME: Make it an ordered Map - Map<String, dynamic> props = {}; - - Note({this.created, this.body, this.filePath, this.props}) { + Note([this.filePath]) { created = created ?? DateTime(0, 0, 0, 0, 0, 0, 0, 0); - props = props ?? <String, dynamic>{}; - body = body ?? ""; + } + + String get body { + return data.body; } bool hasValidDate() { @@ -45,15 +44,12 @@ class Note implements Comparable<Note> { } final string = await file.readAsString(); - var noteData = _serializer.decode(string); + data = _serializer.decode(string); - body = noteData.body; - props = noteData.props; - - if (props.containsKey("created")) { - var createdStr = props['created'].toString(); + if (data.props.containsKey("created")) { + var createdStr = data.props['created'].toString(); try { - created = DateTime.parse(props['created']).toLocal(); + created = DateTime.parse(data.props['created']).toLocal(); } catch (ex) { // Ignore it } @@ -80,13 +76,16 @@ class Note implements Comparable<Note> { // FIXME: What about error handling? Future<void> save() async { assert(filePath != null); + assert(data != null); + assert(data.body != null); + assert(data.props != null); if (hasValidDate()) { - props['created'] = toIso8601WithTimezone(created); + data.props['created'] = toIso8601WithTimezone(created); } var file = File(filePath); - var contents = _serializer.encode(NoteData(body, props)); + var contents = _serializer.encode(data); await file.writeAsString(contents); } @@ -98,8 +97,7 @@ class Note implements Comparable<Note> { // FIXME: Can't this part be auto-generated? @override - int get hashCode => - filePath.hashCode ^ created.hashCode ^ body.hashCode ^ props.hashCode; + int get hashCode => filePath.hashCode ^ created.hashCode ^ data.hashCode; @override bool operator ==(Object other) => @@ -107,19 +105,11 @@ class Note implements Comparable<Note> { other is Note && runtimeType == other.runtimeType && filePath == other.filePath && - body == other.body && - created == other.created && - _equalMaps(props, other.props); - - static bool _equalMaps(Map a, Map b) { - if (a.length != b.length) return false; - return a.keys - .every((dynamic key) => b.containsKey(key) && a[key] == b[key]); - } + data == other.data; @override String toString() { - return 'Note{filePath: $filePath, body: $body, created: $created, props: $props}'; + return 'Note{filePath: $filePath, created: $created, data: $data}'; } @override diff --git a/lib/screens/note_editor.dart b/lib/screens/note_editor.dart index 33aec355..c8269b66 100644 --- a/lib/screens/note_editor.dart +++ b/lib/screens/note_editor.dart @@ -57,7 +57,7 @@ class NoteEditorState extends State<NoteEditor> { icon: Icon(Icons.check), onPressed: () { final stateContainer = StateContainer.of(context); - note.body = _textController.text; + note.data.body = _textController.text; if (note.body.isNotEmpty) { newNote ? stateContainer.addNote(note) diff --git a/lib/storage/git_storage.dart b/lib/storage/git_storage.dart index 872b4f8d..f26b91ae 100644 --- a/lib/storage/git_storage.dart +++ b/lib/storage/git_storage.dart @@ -84,7 +84,7 @@ class GitNoteRepository { var notes = <Note>[]; var lister = dir.list(recursive: false); await for (var fileEntity in lister) { - var note = Note(filePath: fileEntity.path); + var note = Note(fileEntity.path); if (!note.filePath.toLowerCase().endsWith('.md')) { continue; } diff --git a/lib/storage/serializers.dart b/lib/storage/serializers.dart index 970a1d9b..daf16ad7 100644 --- a/lib/storage/serializers.dart +++ b/lib/storage/serializers.dart @@ -1,11 +1,17 @@ +import 'dart:collection'; + import 'package:fimber/fimber.dart'; import 'package:yaml/yaml.dart'; class NoteData { - String body; - Map<String, dynamic> props = {}; + String body = ""; + LinkedHashMap<String, dynamic> props = LinkedHashMap<String, dynamic>(); - NoteData(this.body, this.props); + NoteData([this.body, this.props]) { + body = body ?? ""; + // ignore: prefer_collection_literals + props = props ?? LinkedHashMap<String, dynamic>(); + } @override int get hashCode => body.hashCode ^ props.hashCode; @@ -23,6 +29,11 @@ class NoteData { return a.keys .every((dynamic key) => b.containsKey(key) && a[key] == b[key]); } + + @override + String toString() { + return 'NoteData{bodt: $body, props: $props}'; + } } abstract class NoteSerializer { @@ -54,7 +65,7 @@ class MarkdownYAMLSerializer implements NoteSerializer { return NoteData(body, map); } - return NoteData(str, <String, dynamic>{}); + return NoteData(str, LinkedHashMap<String, dynamic>()); } @override diff --git a/pubspec.lock b/pubspec.lock index 98374849..c3699285 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -578,7 +578,7 @@ packages: name: yaml url: "https://pub.dartlang.org" source: hosted - version: "2.1.16" + version: "2.2.0" sdks: dart: ">=2.4.0 <3.0.0" flutter: ">=1.6.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index a1aa5f1b..f975297e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ dependencies: intl: "^0.15.6" path: ^1.6.2 uuid: ^2.0.1 - yaml: ^2.1.15 + yaml: ^2.2.0 firebase_analytics: ^4.0.2 flutter_crashlytics: ^1.0.0 shared_preferences: ^0.5.2 diff --git a/test/note_storage_test.dart b/test/note_storage_test.dart index d30a62e2..d536fde9 100644 --- a/test/note_storage_test.dart +++ b/test/note_storage_test.dart @@ -1,6 +1,9 @@ +import 'dart:collection'; import 'dart:io'; +import 'package:journal/datetime_utils.dart'; import 'package:journal/note.dart'; +import 'package:journal/storage/serializers.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; @@ -15,12 +18,20 @@ void main() { tempDir = await Directory.systemTemp.createTemp('__storage_test__'); var dt = DateTime(2019, 12, 2, 5, 4, 2); + // ignore: prefer_collection_literals + var props = LinkedHashMap<String, dynamic>(); + props['created'] = toIso8601WithTimezone(dt); + n1Path = p.join(tempDir.path, "1.md"); n2Path = p.join(tempDir.path, "2.md"); - notes = <Note>[ - Note(filePath: n1Path, body: "test", created: dt), - Note(filePath: n2Path, body: "test2", created: dt), - ]; + + var n1 = Note(n1Path); + n1.data = NoteData("test", props); + + var n2 = Note(n2Path); + n2.data = NoteData("test2", props); + + notes = [n1, n2]; }); tearDownAll(() async { @@ -37,7 +48,7 @@ void main() { var loadedNotes = <Note>[]; await Future.forEach(notes, (origNote) async { - var note = Note(filePath: origNote.filePath); + var note = Note(origNote.filePath); var r = await note.load(); expect(r, NoteLoadState.Loaded); diff --git a/test/serializers_test.dart b/test/serializers_test.dart index 8b65087c..1c52e0f9 100644 --- a/test/serializers_test.dart +++ b/test/serializers_test.dart @@ -1,3 +1,5 @@ +import 'dart:collection'; + import 'package:journal/storage/serializers.dart'; import 'package:journal/datetime_utils.dart'; import 'package:test/test.dart'; @@ -10,7 +12,8 @@ DateTime nowWithoutMicro() { void main() { group('Serializers', () { var created = toIso8601WithTimezone(nowWithoutMicro()); - var note = NoteData("This is the body", {"created": created}); + var note = + NoteData("This is the body", LinkedHashMap.from({"created": created})); test('Markdown Serializer', () { var serializer = MarkdownYAMLSerializer(); @@ -47,7 +50,6 @@ Alright."""; expect(actualStr, note.body); }); - /* test('Markdown Serializer YAML Order', () { var str = """--- type: Journal @@ -63,6 +65,5 @@ Alright."""; expect(actualStr, str); }); - */ }); }