another.im-ios/Monal/another.im/XMPP/Wrappers/WrapperXMPP.swift
2024-12-02 16:50:23 +01:00

148 lines
5.2 KiB
Swift

import Foundation
import monalxmpp
enum AccountsAvailability {
case noAccounts
case allDisabled
case someEnabled
}
final class WrapperXMPP: ObservableObject {
@Published private(set) var accountsAvailability: AccountsAvailability = .noAccounts
@Published private(set) var accounts: [Account] = []
@Published private(set) var contacts: [Contact] = []
@Published private(set) var activeChats: [Chat] = []
private let xmpp: MLXMPPManager
private let db: DataLayer
private var notificationObservers: [AnyObject] = []
init() {
// init monalxmpp components
xmpp = MLXMPPManager.sharedInstance()
db = DataLayer.sharedInstance()
// subscribe to monalxmpp notifications and fire notification for update
subscribeToUpdates()
xmpp.reconnectAll()
NotificationCenter.default.post(name: Notification.Name(kMonalRefresh), object: nil)
}
deinit {
notificationObservers.forEach { NotificationCenter.default.removeObserver($0) }
}
}
// MARK: - Public
extension WrapperXMPP {
func tryLogin(_ login: String, _ password: String) async throws {
let scenario = ScenarioLogIn()
let result = await scenario.tryLogin(login, password)
if !result {
throw AimErrors.loginError
} else {
NotificationCenter.default.post(name: Notification.Name(kMonalRefresh), object: nil)
}
}
func addContact(contactJid: String, forAccountID: Int) async {
let contact = MLContact.createContact(fromJid: contactJid, andAccountID: NSNumber(value: forAccountID))
xmpp.add(contact)
}
func deleteContact(_ contact: Contact) async throws {
if let mlContact = db.contactList().first(where: { $0.contactJid == contact.contactJid }) {
xmpp.remove(mlContact)
} else {
throw AimErrors.contactRemoveError
}
}
func chat(with: Contact) -> WrapperChat {
// swiftlint:disable:next force_unwrapping
let account = accounts.first { $0.id == with.ownerId }!
let chatModel = WrapperChat(account: account, contact: with, db: db, xmpp: xmpp)
return chatModel
}
func chat(with: Chat) -> WrapperChat? {
guard let account = accounts.first(where: { $0.id == with.accountId }) else { return nil }
var contact = contacts.first(where: { $0.ownerId == with.accountId && $0.contactJid == with.participantJid })
if contact == nil {
let semaphore = DispatchSemaphore(value: 0)
Task {
await addContact(contactJid: with.participantJid, forAccountID: with.accountId)
semaphore.signal()
}
semaphore.wait()
refreshChats()
refreshContacts()
contact = contacts.first(where: { $0.ownerId == with.accountId && $0.contactJid == with.participantJid })
}
guard let contact else { return nil }
let chatModel = WrapperChat(account: account, contact: contact, db: db, xmpp: xmpp)
return chatModel
}
}
// MARK: - Handle notifications
private extension WrapperXMPP {
func subscribeToUpdates() {
// General
let generalRefresh = NotificationCenter.default.addObserver(forName: Notification.Name(kMonalRefresh), object: nil, queue: .main) { [weak self] _ in
self?.refreshAccounts()
self?.refreshContacts()
self?.refreshChats()
}
notificationObservers.append(generalRefresh)
// For contacts
let contactRefresh = NotificationCenter.default.addObserver(forName: Notification.Name(kMonalContactRefresh), object: nil, queue: .main) { [weak self] _ in
self?.refreshContacts()
self?.refreshChats()
}
let contactRemove = NotificationCenter.default.addObserver(forName: Notification.Name(kMonalContactRemoved), object: nil, queue: .main) { [weak self] _ in
self?.refreshContacts()
self?.refreshChats()
}
notificationObservers.append(contentsOf: [contactRefresh, contactRemove])
}
func refreshAccounts() {
let accounts = db.accountList()
.compactMap { dict -> Account? in
guard let dict = dict as? NSDictionary else { return nil }
return Account(dict)
}
self.accounts = accounts
xmpp.connectIfNecessary()
// mark accounts availability
if accounts.isEmpty {
accountsAvailability = .noAccounts
} else if accounts.filter({ $0.isEnabled }).isEmpty {
accountsAvailability = .allDisabled
} else {
accountsAvailability = .someEnabled
}
}
func refreshContacts() {
contacts = db.contactList()
.filter { $0.isSubscribedTo || $0.hasOutgoingContactRequest || $0.isSubscribedFrom }
.filter { !$0.isSelfChat } // removed for now
.compactMap { Contact($0) }
}
func refreshChats() {
activeChats = db.activeContacts(withPinned: false)
.compactMap {
guard let contact = $0 as? MLContact else { return nil }
return Chat(contact)
}
}
}