Files
GitJournal/lib/core/views/notes_materialized_view.dart
Vishesh Handa e1ea7a4953 Fetch the modified + created time from git
Fixes #78

This is probably the largest commit that I have ever made. From now on -
every File always has an mtime and ctime which is fetched from git.
Notes can optionally override that time by providing yaml metadata.

Additionally the 'filePath' and 'folderPath' is now relative to the
repoPath instead of being the full path.

This will slow down GitJournal like crazy as all the mtimes and ctime
still need to be cached. For my test repo it takes about 23 seconds for
GitJournal to become responsive.
2021-10-26 17:49:08 +02:00

90 lines
2.3 KiB
Dart

/*
* SPDX-FileCopyrightText: 2019-2021 Vishesh Handa <me@vhanda.in>
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import 'package:hive/hive.dart';
import 'package:mutex/mutex.dart';
import 'package:path/path.dart' as p;
import 'package:gitjournal/core/note.dart';
import 'package:gitjournal/logger/logger.dart';
typedef NotesViewComputer<T> = Future<T> Function(Note note);
class NotesMaterializedView<T> {
Box? storageBox;
final String name;
final NotesViewComputer<T> computeFn;
final String repoPath;
final _readMutex = ReadWriteMutex();
final _writeMutex = Mutex();
NotesMaterializedView({
required this.name,
required this.computeFn,
required this.repoPath,
}) {
var path = repoPath;
if (!path.endsWith(p.separator)) {
path += p.separator;
}
}
// FIXME: The return value doesn't need to be optional
// FIXME: Use a LazyBox instead and add a cache on top?
// FIXME: Maybe removing all the old keys after each put is too expensive?
Future<T> fetch(Note note) async {
assert(!note.filePath.startsWith(p.separator));
assert(!note.filePath.endsWith(p.separator));
var ts = note.fileLastModified.toUtc().millisecondsSinceEpoch ~/ 1000;
var keyPrefix = '${note.filePath}_';
var key = keyPrefix + ts.toString();
// Open the Box
await _readMutex.protectRead(() async {
if (storageBox != null) return;
await _writeMutex.protect(() async {
if (storageBox != null) return;
var startTime = DateTime.now();
try {
storageBox = await Hive.openBox<T>(name);
} on HiveError catch (ex, st) {
Log.e("HiveError $name", ex: ex, stacktrace: st);
// Get the file Path
await Hive.deleteBoxFromDisk(name);
storageBox = await Hive.openBox<T>(name);
}
var endTime = DateTime.now().difference(startTime);
Log.i("Loading View $name: $endTime");
});
});
var box = storageBox!;
T? val = box.get(key, defaultValue: null);
if (val == null) {
val = await computeFn(note);
if (ts != 0) {
box.put(key, val);
// Remove old keys
var keys = box.keys.where((k) => k.startsWith(keyPrefix) && k != key);
box.deleteAll(keys);
}
}
return val!;
}
}