another.im-ios/Monal/another.im/XMPP/Wrappers/WrapperXMPP.swift
2024-12-05 18:14:22 +01:00

182 lines
6.4 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()
processOnStart()
}
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
}
}
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()
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 {
// Subsribe to monalxmpp events
func subscribeToUpdates() {
notificationObservers.append(
NotificationCenter.default.addObserver(forName: Notification.Name(kMonalRefresh), object: nil, queue: .main) { [weak self] notification in
self?.processEvent(kMonalRefresh, notification.userInfo?["contact"])
}
)
notificationObservers.append(
NotificationCenter.default.addObserver(forName: Notification.Name(kMonalContactRefresh), object: nil, queue: .main) { [weak self] notification in
self?.processEvent(kMonalContactRefresh, notification.userInfo?["contact"])
}
)
notificationObservers.append(
NotificationCenter.default.addObserver(forName: Notification.Name(kMonalContactRemoved), object: nil, queue: .main) { [weak self] notification in
self?.processEvent(kMonalContactRemoved, notification.userInfo?["contact"])
}
)
notificationObservers.append(
NotificationCenter.default.addObserver(forName: Notification.Name(kMLResourceBoundNotice), object: nil, queue: .main) { [weak self] _ in
self?.processOnStart()
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// }
}
)
}
// Process monalxmpp events
func processEvent(_ notificationName: String, _ object: Any?) {
switch notificationName {
case kMonalRefresh:
print("refresh?")
case kMonalContactRefresh:
if let mlContact = object as? MLContact, !mlContact.isSelfChat {
if let contact = Contact(mlContact) {
if let index = contacts.firstIndex(where: { $0.id == mlContact.id }) {
contacts[index] = contact
} else {
contacts.append(contact)
contacts.sort { $0.name < $1.name }
}
}
}
case kMonalContactRemoved:
if let mlContact = object as? MLContact {
contacts = contacts.filter { $0.id != mlContact.id }
}
default:
break
}
}
// Initital monalxmpp db fetch
func processOnStart() {
// get all accounts and contacts once
let accounts = db.accountList()
.compactMap { dict -> Account? in
guard let dict = dict as? NSDictionary else { return nil }
return Account(dict)
}
self.accounts = accounts
// check if active accounts existed
if accounts.isEmpty {
accountsAvailability = .noAccounts
} else if accounts.filter({ $0.isEnabled }).isEmpty {
accountsAvailability = .allDisabled
} else {
accountsAvailability = .someEnabled
}
// get all contacts
if !accounts.isEmpty {
contacts = db.contactList()
.filter { $0.isSubscribedTo || $0.hasOutgoingContactRequest || $0.isSubscribedFrom }
.filter { !$0.isSelfChat } // removed for now
.compactMap { Contact($0) }
// get active chats
if !contacts.isEmpty {
activeChats = db.activeContacts(withPinned: false)
.compactMap {
guard let contact = $0 as? MLContact else { return nil }
return Chat(contact)
}
}
}
// try reconnect active clients
xmpp.connectIfNecessary()
}
}