Files
GitJournal/lib/core/note_storage.dart
Eric Mink 51420f6c8d fix issue #579 -- this commit replaces PR #625
This commit was already in PR #625 but this new one is based on a more
recent version of the master branch.

As discussed in https://github.com/GitJournal/GitJournal/pull/625 the
problem was that if there is no yaml header, the title does not get
stored anywhere and hence gets lost. The fix is to write the title back
to the file as a first heading. This is used for loading the title
anyway, afaict.

The side effect is of course that any *other* application using the note
in the meantime will see the heading containing the note title. This is
way less disruptive than losing the title fully, though.
2023-12-12 20:14:01 +01:00

147 lines
4.3 KiB
Dart

/*
* SPDX-FileCopyrightText: 2019-2021 Vishesh Handa <me@vhanda.in>
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import 'dart:convert';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:gitjournal/core/file/file_storage.dart';
import 'package:gitjournal/core/markdown/md_yaml_doc.dart';
import 'package:gitjournal/core/markdown/md_yaml_doc_codec.dart';
import 'package:gitjournal/core/markdown/md_yaml_doc_loader.dart';
import 'package:gitjournal/core/markdown/md_yaml_note_serializer.dart';
import 'package:gitjournal/logger/logger.dart';
import 'package:path/path.dart' as p;
import 'package:universal_io/io.dart' as io;
import 'file/file.dart';
import 'folder/notes_folder_fs.dart';
import 'note.dart';
import 'notes/note.dart';
class NoteStorage {
static final _serializer = MarkdownYAMLCodec();
static String serialize(Note note) {
// HACK: This isn't great as the raw editor still shows the note with metadata
var data = note.data;
if (!note.canHaveMetadata) {
// Fix issue 579: If there is no yaml header, the title would get lost unless it is stored somewhere.
// Hence, store it in the file as a first heading.
data = MdYamlDoc(
body: (note.title != null ? "# ${note.title!}\n" : "") + data.body,
);
}
var contents = _serializer.encode(data);
// Make sure all docs end with a \n
if (!contents.endsWith('\n')) {
contents += '\n';
}
return contents;
}
static Future<Note> save(Note note) async {
assert(note.filePath.isNotEmpty);
assert(note.fileName.isNotEmpty);
assert(note.oid.isEmpty);
var contents = utf8.encode(serialize(note));
assert(note.fullFilePath.startsWith(p.separator));
var file = io.File(note.fullFilePath);
await file.writeAsBytes(contents, flush: true);
var stat = file.statSync();
note = note.copyWith(
file: note.file.copyFile(
fileLastModified: stat.modified,
oid: GitHash.compute(contents),
modified: DateTime.now(),
),
);
return note;
}
static final mdYamlDocLoader = MdYamlDocLoader();
/// Fails with 'NoteReloadNotRequired' if the note doesn't need to be reloaded
static Future<Note> reload(Note note, FileStorage fileStorage) async {
var newFile = await fileStorage.load(note.filePath);
if (note.file == newFile) {
throw NoteReloadNotRequired();
}
Log.d("Note modified: ${note.filePath}");
return load(newFile, note.parent);
}
static Future<Note> load(File file, NotesFolderFS parentFolder) async {
assert(file.filePath.isNotEmpty);
assert(!file.filePath.startsWith('/'));
assert(file.oid.isNotEmpty);
var filePath = file.fullFilePath;
var format = NoteFileFormatInfo.fromFilePath(filePath);
if (format == NoteFileFormat.Markdown) {
var data = await mdYamlDocLoader.loadDoc(filePath);
var settings = NoteSerializationSettings.fromConfig(parentFolder.config);
var noteSerializer = NoteSerializer.fromConfig(settings);
var note = noteSerializer.decode(
data: data,
parent: parentFolder,
file: file,
fileFormat: format,
);
return note;
} else if (format == NoteFileFormat.Txt) {
var note = Note.build(
parent: parentFolder,
file: file,
title: null,
body: await io.File(filePath).readAsString(),
noteType: NoteType.Unknown,
tags: ISet(),
extraProps: const {},
fileFormat: NoteFileFormat.Txt,
propsList: IList(),
serializerSettings:
NoteSerializationSettings.fromConfig(parentFolder.config),
created: null,
modified: null,
);
return note;
} else if (format == NoteFileFormat.OrgMode) {
var note = Note.build(
parent: parentFolder,
file: file,
title: null,
body: await io.File(filePath).readAsString(),
noteType: NoteType.Unknown,
tags: ISet(),
extraProps: const {},
fileFormat: NoteFileFormat.OrgMode,
propsList: IList(),
serializerSettings:
NoteSerializationSettings.fromConfig(parentFolder.config),
created: null,
modified: null,
);
return note;
}
throw Exception("Unknown Note type. WTF");
}
}
class NoteReloadNotRequired implements Exception {
NoteReloadNotRequired();
}