import Foundation import GRDB import Martin final class ClientMartinRosterManager: Martin.RosterManager { func clear(for context: Martin.Context) { do { try Database.shared.dbQueue.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 Database.shared.dbQueue.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 Database.shared.dbQueue.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 Database.shared.dbQueue.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 Database.shared.dbQueue.read { db in try Roster .filter(Column("bareJid") == context.userBareJid.stringValue) .filter(Column("contactBareJid") == jid.stringValue) .fetchOne(db) } if let roster { _ = try Database.shared.dbQueue.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 Database.shared.dbQueue.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 Database.shared.dbQueue.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) {} }