another.im-ios/Monal/another.im/XMPP/Wrappers/WrapperXMPP.swift
2024-12-06 20:32:41 +01:00

172 lines
5.8 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? {
guard let account = accounts.first(where: { $0.id == with.ownerId }) else { return nil }
return WrapperChat(with: with, account: account)
}
func chat(with: Chat) -> WrapperChat? {
guard let account = accounts.first(where: { $0.id == with.accountId }) else { return nil }
return WrapperChat(with: with, account: account)
}
}
// MARK: - Handle notifications
private extension WrapperXMPP {
// Subsribe to monalxmpp events
func subscribeToUpdates() {
let notificationNames = [
kMonalRefresh,
kMonalContactRefresh,
kMonalContactRemoved,
kMLResourceBoundNotice,
kMonalNewMessageNotice,
kMonalHistoryMessagesNotice
]
notificationObservers = notificationNames.map { name in
NotificationCenter.default.addObserver(forName: Notification.Name(name), object: nil, queue: .main) { [weak self] notification in
self?.processEvent(notification)
}
}
}
// Process monalxmpp events
func processEvent(_ notification: Notification) {
switch notification.name.rawValue {
case kMonalRefresh:
print("refresh?")
case kMonalContactRefresh:
if let mlContact = notification.userInfo?["contact"] 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 = notification.userInfo?["contact"] as? MLContact {
contacts = contacts.filter { $0.id != mlContact.id }
}
case kMLResourceBoundNotice:
processOnStart()
case kMonalNewMessageNotice, kMonalHistoryMessagesNotice:
if let mlContact = notification.userInfo?["contact"] as? MLContact, !mlContact.isSelfChat {
let contactJid = mlContact.contactJid
let accountId = mlContact.account?.accountID.intValue ?? 0
if activeChats.first(where: { $0.accountId == accountId && $0.participantJid == contactJid }) == nil {
if let chat = Chat(mlContact) {
activeChats.append(chat)
activeChats.sort { $0.participantJid < $1.participantJid }
}
}
}
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()
}
}