| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
- import Foundation
- @MainActor
- @Observable
- final class AIHistoryManager {
- static let shared = AIHistoryManager()
- private(set) var entries: [AIHistoryEntry] = []
- private let defaults: UserDefaults
- private let storageKey = "reddora.ai.history.entries"
- private let maxEntries = 30
- private let persistQueue = DispatchQueue(label: "com.reddora.ai-history.persist", qos: .utility)
- init(defaults: UserDefaults = .standard) {
- self.defaults = defaults
- load()
- }
- var isEmpty: Bool {
- entries.isEmpty
- }
- func entries(matching filter: AIHistoryFilter) -> [AIHistoryEntry] {
- guard let toolKind = filter.toolKind else { return entries }
- return entries.filter { $0.toolKind == toolKind }
- }
- func save(_ entry: AIHistoryEntry) {
- entries.removeAll { $0.id == entry.id }
- entries.insert(entry, at: 0)
- enforceLimit()
- persist()
- }
- func delete(id: UUID) {
- guard entries.contains(where: { $0.id == id }) else { return }
- entries.removeAll { $0.id == id }
- persist()
- }
- func deleteAll() {
- guard !entries.isEmpty else { return }
- entries.removeAll()
- persist()
- }
- private func load() {
- guard let data = defaults.data(forKey: storageKey) else {
- entries = []
- return
- }
- do {
- let decoder = JSONDecoder()
- decoder.dateDecodingStrategy = .iso8601
- entries = try decoder.decode([AIHistoryEntry].self, from: data)
- let hadExcessEntries = entries.count > maxEntries
- enforceLimit()
- if hadExcessEntries {
- persist()
- }
- } catch {
- entries = []
- }
- }
- private func enforceLimit() {
- guard entries.count > maxEntries else { return }
- entries = Array(entries.prefix(maxEntries))
- }
- private func persist() {
- let snapshot = entries
- let storageKey = storageKey
- let defaults = defaults
- persistQueue.async {
- do {
- let encoder = JSONEncoder()
- encoder.dateEncodingStrategy = .iso8601
- let data = try encoder.encode(snapshot)
- defaults.set(data, forKey: storageKey)
- } catch {
- // Keep in-memory entries if persistence fails.
- }
- }
- }
- }
|