import Foundation import SwiftData /// Rebuildable cache row for one note. Holds nothing that isn't also, more /// authoritatively, in the note's JSON file — the metadata observer (via /// `NoteMapper`) is the only writer. @Model final class NoteEntity { @Attribute(.unique) var id: String = "" var text: String = "" var sourceRaw: String = Note.Source.typed.rawValue var tags: [String] = [] var createdAt: Date = Date() var modifiedAt: Date = Date() /// Back-pointer to the authoritative file, so a "removed" event can find /// the row to delete by path when it lacks the id. var jsonRelativePath: String = "" // Flattened CaptureContext — @Model can't store a nested Codable struct // as one column; call sites use the `context` computed property instead. var latitude: Double? var longitude: Double? var horizontalAccuracy: Double? var placeName: String? var thoroughfare: String? var locality: String? var administrativeArea: String? var country: String? var timeZoneID: String? var device: String? init(id: String, jsonRelativePath: String) { self.id = id self.jsonRelativePath = jsonRelativePath } } extension NoteEntity { var context: CaptureContext { get { CaptureContext( latitude: latitude, longitude: longitude, horizontalAccuracy: horizontalAccuracy, placeName: placeName, thoroughfare: thoroughfare, locality: locality, administrativeArea: administrativeArea, country: country, timeZoneID: timeZoneID, device: device ) } set { latitude = newValue.latitude longitude = newValue.longitude horizontalAccuracy = newValue.horizontalAccuracy placeName = newValue.placeName thoroughfare = newValue.thoroughfare locality = newValue.locality administrativeArea = newValue.administrativeArea country = newValue.country timeZoneID = newValue.timeZoneID device = newValue.device } } var source: Note.Source { Note.Source(rawValue: sourceRaw) ?? .typed } /// First line of the text, used as the display title. var title: String { text.split(separator: "\n", omittingEmptySubsequences: true) .first.map(String.init) ?? "" } } extension PersistentModel { /// Safe-to-read guard for objects reached through `@Query` or a /// relationship during a rebuild/sync burst. `isDeleted` alone only /// covers the unsaved window. var isLive: Bool { modelContext != nil && !isDeleted } }