83 lines
3.1 KiB
Swift
83 lines
3.1 KiB
Swift
import AppKit
|
|
|
|
public struct Note: Identifiable {
|
|
public let id: UUID
|
|
public let contents: NSAttributedString
|
|
public let title: String
|
|
|
|
init(id: UUID, contents: NSAttributedString) {
|
|
self.id = id
|
|
self.contents = NSAttributedString(attributedString: contents)
|
|
var title = "Untitled Note"
|
|
contents.string.enumerateLines { line, stop in
|
|
let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
if !trimmed.isEmpty {
|
|
title = String(trimmed.prefix(60))
|
|
stop = true
|
|
}
|
|
}
|
|
self.title = title
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
public final class NotesStore {
|
|
public private(set) var notes: [Note] = []
|
|
private let directory: URL
|
|
|
|
public init(directory: URL = FileManager.default.urls(
|
|
for: .applicationSupportDirectory, in: .userDomainMask
|
|
)[0].appendingPathComponent("Conjure/Notes", isDirectory: true)) throws {
|
|
self.directory = directory
|
|
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
|
let files = try FileManager.default.contentsOfDirectory(
|
|
at: directory, includingPropertiesForKeys: nil
|
|
).filter { $0.pathExtension.lowercased() == "rtfd" }
|
|
.sorted { $0.lastPathComponent < $1.lastPathComponent }
|
|
notes = try files.map { url in
|
|
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)
|
|
}
|
|
}
|
|
|
|
public func create() throws -> UUID {
|
|
let note = Note(id: UUID(), contents: NSAttributedString(string: ""))
|
|
try save(note)
|
|
notes.append(note)
|
|
notes.sort { $0.id.uuidString < $1.id.uuidString }
|
|
return note.id
|
|
}
|
|
|
|
public func update(id: UUID, contents: NSAttributedString) throws {
|
|
guard let index = notes.firstIndex(where: { $0.id == id }) else {
|
|
throw CocoaError(.fileNoSuchFile)
|
|
}
|
|
let note = Note(id: id, contents: contents)
|
|
try save(note)
|
|
notes[index] = note
|
|
}
|
|
|
|
public func delete(id: UUID) throws {
|
|
guard let index = notes.firstIndex(where: { $0.id == id }) else {
|
|
throw CocoaError(.fileNoSuchFile)
|
|
}
|
|
try FileManager.default.removeItem(at: directory.appendingPathComponent(id.uuidString + ".rtfd"))
|
|
notes.remove(at: index)
|
|
}
|
|
|
|
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 url = directory.appendingPathComponent(note.id.uuidString + ".rtfd", isDirectory: true)
|
|
try wrapper.write(to: url, options: .atomic, originalContentsURL: url)
|
|
}
|
|
}
|