import Foundation import GRDB import Martin extension Database: MartinsManager { // MARK: - Martin's roster manager func clear(for context: Martin.Context) { print("Clearing roster for context: \(context)") do { try _db.write { db in try Roster .filter(Column("bareJid") == context.userBareJid.stringValue) .deleteAll(db) try RosterVersion .filter(Column("bareJid") == context.userBareJid.stringValue) .deleteAll(db) } } catch { logIt(.error, "Error clearing roster: \(error.localizedDescription)") } } func items(for context: Martin.Context) -> [any Martin.RosterItemProtocol] { do { let rosters: [Roster] = try _db.read { db in try Roster.filter(Column("bareJid") == context.userBareJid.stringValue).fetchAll(db) } return rosters.map { roster in RosterItemBase( jid: JID(roster.bareJid), name: roster.name, subscription: RosterItemSubscription(rawValue: roster.subscription) ?? .none, groups: roster.data.groups, ask: roster.ask, annotations: roster.data.annotations ) } } catch { logIt(.error, "Error fetching roster items: \(error.localizedDescription)") return [] } } func item(for context: Martin.Context, jid: Martin.JID) -> (any Martin.RosterItemProtocol)? { do { let roster: Roster? = try _db.read { db in try Roster .filter(Column("bareJid") == context.userBareJid.stringValue) .filter(Column("contactBareJid") == jid.stringValue) .fetchOne(db) } if let roster { return RosterItemBase( jid: JID(roster.bareJid), name: roster.name, subscription: RosterItemSubscription(rawValue: roster.subscription) ?? .none, groups: roster.data.groups, ask: roster.ask, annotations: roster.data.annotations ) } else { return nil } } catch { logIt(.error, "Error fetching roster item: \(error.localizedDescription)") return nil } } func updateItem(for context: Martin.Context, jid: Martin.JID, name: String?, subscription: Martin.RosterItemSubscription, groups: [String], ask: Bool, annotations: [Martin.RosterItemAnnotation]) -> (any Martin.RosterItemProtocol)? { do { let roster = Roster( bareJid: context.userBareJid.stringValue, contactBareJid: jid.stringValue, name: name, subscription: subscription.rawValue, ask: ask, data: DBRosterData( groups: groups, annotations: annotations ) ) try _db.write { db in try roster.save(db) } return RosterItemBase(jid: jid, name: name, subscription: subscription, groups: groups, ask: ask, annotations: annotations) } catch { logIt(.error, "Error updating roster item: \(error.localizedDescription)") return nil } } func deleteItem(for context: Martin.Context, jid: Martin.JID) -> (any Martin.RosterItemProtocol)? { do { let roster: Roster? = try _db.read { db in try Roster .filter(Column("bareJid") == context.userBareJid.stringValue) .filter(Column("contactBareJid") == jid.stringValue) .fetchOne(db) } if let roster { _ = try _db.write { db in try roster.delete(db) } return RosterItemBase( jid: JID(roster.bareJid), name: roster.name, subscription: RosterItemSubscription(rawValue: roster.subscription) ?? .none, groups: roster.data.groups, ask: roster.ask, annotations: roster.data.annotations ) } else { return nil } } catch { logIt(.error, "Error fetching roster version: \(error.localizedDescription)") return nil } } func version(for context: Martin.Context) -> String? { do { let version: RosterVersion? = try _db.read { db in try RosterVersion .filter(Column("bareJid") == context.userBareJid.stringValue) .fetchOne(db) } return version?.version } catch { logIt(.error, "Error fetching roster version: \(error.localizedDescription)") return nil } } func set(version: String?, for context: Martin.Context) { guard let version else { return } do { try _db.write { db in let rosterVersion = RosterVersion( bareJid: context.userBareJid.stringValue, version: version ) try rosterVersion.save(db) } } catch { logIt(.error, "Error setting roster version: \(error.localizedDescription)") } } func initialize(context _: Martin.Context) {} func deinitialize(context _: Martin.Context) {} // MARK: - Martin's chats manager func chats(for context: Martin.Context) -> [any Martin.ChatProtocol] { do { let chats: [Chat] = try _db.read { db in try Chat.filter(Column("account") == context.userBareJid.stringValue).fetchAll(db) } return chats.map { chat in Martin.ChatBase(context: context, jid: BareJID(chat.participant)) } } catch { logIt(.error, "Error fetching chats: \(error.localizedDescription)") return [] } } func chat(for context: Martin.Context, with: Martin.BareJID) -> (any Martin.ChatProtocol)? { do { let chat: Chat? = try _db.read { db in try Chat .filter(Column("account") == context.userBareJid.stringValue) .filter(Column("participant") == with.stringValue) .fetchOne(db) } if chat != nil { return Martin.ChatBase(context: context, jid: with) } else { return nil } } catch { logIt(.error, "Error fetching chat: \(error.localizedDescription)") return nil } } func createChat(for context: Martin.Context, with: Martin.BareJID) -> (any Martin.ChatProtocol)? { do { let chat: Chat? = try _db.read { db in try Chat .filter(Column("account") == context.userBareJid.stringValue) .filter(Column("participant") == with.stringValue) .fetchOne(db) } if chat != nil { return Martin.ChatBase(context: context, jid: with) } else { let chat = Chat( id: UUID().uuidString, account: context.userBareJid.stringValue, participant: with.stringValue, type: .chat ) try _db.write { db in try chat.save(db) } return Martin.ChatBase(context: context, jid: with) } } catch { logIt(.error, "Error fetching chat: \(error.localizedDescription)") return nil } } func close(chat: any Martin.ChatProtocol) -> Bool { // not used in Martin library for now print("Closing chat: \(chat)") return false } }