import Foundation import GRDB import Martin // MARK: - Martin's roster manager extension Database: Martin.RosterManager { 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 extension Database: Martin.ChatManager { 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 } } // MARK: - Martin's rooms manager extension Database: Martin.RoomManager { func rooms(for _: Martin.Context) -> [any Martin.RoomProtocol] { [] } func room(for _: Martin.Context, with _: Martin.BareJID) -> (any Martin.RoomProtocol)? { nil } func createRoom(for _: Martin.Context, with _: Martin.BareJID, nickname _: String, password _: String?) -> (any Martin.RoomProtocol)? { nil } func close(room _: any Martin.RoomProtocol) -> Bool { false } } // MARK: - Martin's channels manager extension Database: Martin.ChannelManager { func channels(for _: Martin.Context) -> [any Martin.ChannelProtocol] { [] // do { // let channels: [Channel] = try _db.read { db in // try Channel.filter(Column("account") == context.userBareJid.stringValue).fetchAll(db) // } // return channels.map { channel in // Martin.ChannelBase(context: context, channelJid: channel.channel, participantId: , nickname: , state: ) // } // } catch { // logIt(.error, "Error fetching channels: \(error.localizedDescription)") // return [] // } } func createChannel(for _: Martin.Context, with _: Martin.BareJID, participantId _: String, nick _: String?, state _: Martin.ChannelState) -> Martin.ConversationCreateResult { .none } func channel(for _: Martin.Context, with _: Martin.BareJID) -> (any Martin.ChannelProtocol)? { nil } func close(channel _: any Martin.ChannelProtocol) -> Bool { false } }