refactor: use structured rich text for Notes editing
This commit is contained in:
@@ -24,6 +24,7 @@ public struct Note: Identifiable {
|
||||
public final class NotesStore {
|
||||
public private(set) var notes: [Note] = []
|
||||
private let directory: URL
|
||||
private var legacyIDs: Set<UUID> = []
|
||||
|
||||
public init(directory: URL = FileManager.default.urls(
|
||||
for: .applicationSupportDirectory, in: .userDomainMask
|
||||
@@ -34,16 +35,26 @@ public final class NotesStore {
|
||||
at: directory, includingPropertiesForKeys: nil
|
||||
).filter { $0.pathExtension.lowercased() == "rtfd" }
|
||||
.sorted { $0.lastPathComponent < $1.lastPathComponent }
|
||||
notes = try files.map { url in
|
||||
var loaded: [Note] = []
|
||||
for url in files {
|
||||
guard let id = UUID(uuidString: url.deletingPathExtension().lastPathComponent) else {
|
||||
throw CocoaError(.fileReadCorruptFile)
|
||||
}
|
||||
let contents = try NSAttributedString(
|
||||
url: url, options: [.documentType: NSAttributedString.DocumentType.rtfd],
|
||||
documentAttributes: nil
|
||||
)
|
||||
return Note(id: id, contents: contents)
|
||||
let wrapper = try FileWrapper(url: url, options: .immediate)
|
||||
guard wrapper.isDirectory else { throw CocoaError(.fileReadCorruptFile) }
|
||||
let contents: NSAttributedString
|
||||
if wrapper.fileWrappers?[NoteDocument.metadataFilename] != nil {
|
||||
contents = try NoteDocument.contents(from: wrapper)
|
||||
} else {
|
||||
guard let legacy = NSAttributedString(rtfdFileWrapper: wrapper, documentAttributes: nil) else {
|
||||
throw CocoaError(.fileReadCorruptFile)
|
||||
}
|
||||
contents = NoteDocument.importMarkdown(legacy)
|
||||
legacyIDs.insert(id)
|
||||
}
|
||||
loaded.append(Note(id: id, contents: contents))
|
||||
}
|
||||
notes = loaded
|
||||
}
|
||||
|
||||
public func create() throws -> UUID {
|
||||
@@ -69,14 +80,29 @@ public final class NotesStore {
|
||||
}
|
||||
try FileManager.default.removeItem(at: directory.appendingPathComponent(id.uuidString + ".rtfd"))
|
||||
notes.remove(at: index)
|
||||
legacyIDs.remove(id)
|
||||
}
|
||||
|
||||
private func save(_ note: Note) throws {
|
||||
let wrapper = try note.contents.fileWrapper(
|
||||
from: NSRange(location: 0, length: note.contents.length),
|
||||
documentAttributes: [.documentType: NSAttributedString.DocumentType.rtfd]
|
||||
)
|
||||
let wrapper = try NoteDocument.fileWrapper(note.contents)
|
||||
let url = directory.appendingPathComponent(note.id.uuidString + ".rtfd", isDirectory: true)
|
||||
if legacyIDs.contains(note.id) {
|
||||
let backups = directory.appendingPathComponent("LegacyBackups", isDirectory: true)
|
||||
try FileManager.default.createDirectory(at: backups, withIntermediateDirectories: true)
|
||||
let backup = backups.appendingPathComponent(note.id.uuidString + ".rtfd", isDirectory: true)
|
||||
if !FileManager.default.fileExists(atPath: backup.path) {
|
||||
// copyItem leaves the live legacy package untouched if backup creation fails.
|
||||
try FileManager.default.copyItem(at: url, to: backup)
|
||||
} else {
|
||||
// A previous failed save may already have preserved the original package.
|
||||
let original = try FileWrapper(url: backup, options: .immediate)
|
||||
guard original.isDirectory,
|
||||
NSAttributedString(rtfdFileWrapper: original, documentAttributes: nil) != nil else {
|
||||
throw CocoaError(.fileWriteFileExists)
|
||||
}
|
||||
}
|
||||
}
|
||||
try wrapper.write(to: url, options: .atomic, originalContentsURL: url)
|
||||
legacyIDs.remove(note.id)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user