Models: - Convert Conversation and ChatMessage to SwiftData @Model classes with a one-to-many relationship and cascade delete. - Rename ChatMessage.id to clientID and drop Conversation.id in favor of jid (the string id would clash with PersistentIdentifier). - Keep MessageReaction as a Codable value stored in an array attribute. Persistence: - Add ArchiveStore, a per-account ModelContainer/ModelContext that replaces the JSON ChatArchive with load/save/erase. - Add ArchiveMetadataRecord for the durable MAM checkpoints, cursor, locally deleted message IDs and roster contact JIDs. - Add LegacyArchiveImporter to import the old JSON snapshot once and delete the file afterwards, preserving existing history. AppModel: - Replace ChatArchive with ArchiveStore and persist via context.save(); insert new models and delete the replaced object on upsert merges so SwiftData never keeps an orphaned duplicate. Tests: - Replace ChatArchiveTests with ArchiveStoreTests (store round-trip and legacy import) and drop the now-obsolete Codable round-trip assertions.
39 lines
1.2 KiB
Swift
39 lines
1.2 KiB
Swift
import Foundation
|
|
|
|
/// A cached presentation row for the selected chat. Building day boundaries
|
|
/// once per message mutation avoids walking the full history during every
|
|
/// scroll-position or composer-state update.
|
|
struct ChatTimelineEntry: Identifiable, Hashable {
|
|
let message: ChatMessage
|
|
let startsNewDay: Bool
|
|
|
|
var id: String { message.clientID }
|
|
|
|
static func == (lhs: Self, rhs: Self) -> Bool {
|
|
lhs.message.clientID == rhs.message.clientID
|
|
&& lhs.startsNewDay == rhs.startsNewDay
|
|
}
|
|
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(message.clientID)
|
|
hasher.combine(startsNewDay)
|
|
}
|
|
|
|
static func make(
|
|
from messages: [ChatMessage],
|
|
calendar: Calendar = .autoupdatingCurrent
|
|
) -> [ChatTimelineEntry] {
|
|
var previousTimestamp: Date?
|
|
return messages.map { message in
|
|
let startsNewDay = previousTimestamp.map {
|
|
!calendar.isDate($0, inSameDayAs: message.timestamp)
|
|
} ?? true
|
|
previousTimestamp = message.timestamp
|
|
return ChatTimelineEntry(
|
|
message: message,
|
|
startsNewDay: startsNewDay
|
|
)
|
|
}
|
|
}
|
|
}
|