another.im-ios/Monal/another.im/XMPP/MonalXmppWrapper.swift

163 lines
5.8 KiB
Swift
Raw Normal View History

2024-11-19 12:59:22 +00:00
import Foundation
import monalxmpp
final class MonalXmppWrapper: ObservableObject {
2024-11-21 16:03:55 +00:00
@Published private(set) var accountsAvailability: AccountsAvailability = .noAccounts
@Published private(set) var accounts: [Account] = []
@Published private(set) var contacts: [Contact] = []
2024-11-23 16:23:56 +00:00
@Published private(set) var activeChats: [Chat] = []
2024-11-19 12:59:22 +00:00
2024-11-20 15:52:49 +00:00
private let xmpp: MLXMPPManager
private let db: DataLayer
2024-11-19 12:59:22 +00:00
2024-11-21 16:03:55 +00:00
private var notificationObservers: [AnyObject] = []
2024-11-19 12:59:22 +00:00
init() {
2024-11-20 15:52:49 +00:00
// here is some inits (just for now)
MLProcessLock.initialize(forProcess: "MainApp")
2024-11-21 13:32:38 +00:00
// init monalxmpp components
xmpp = MLXMPPManager.sharedInstance()
db = DataLayer.sharedInstance()
2024-11-21 16:03:55 +00:00
// subscribe to monalxmpp notifications and fire notification for update
subscribeToUpdates()
NotificationCenter.default.post(name: Notification.Name(kMonalRefresh), object: nil)
}
deinit {
notificationObservers.forEach { NotificationCenter.default.removeObserver($0) }
2024-11-19 12:59:22 +00:00
}
2024-11-22 14:45:38 +00:00
}
2024-11-20 15:52:49 +00:00
2024-11-22 14:45:38 +00:00
// MARK: - Public
extension MonalXmppWrapper {
2024-11-20 15:52:49 +00:00
func tryLogin(_ login: String, _ password: String) async throws {
let loginObject = LoginTry(xmpp: xmpp)
let result = await loginObject.tryLogin(login, password)
if !result {
throw AimErrors.loginError
2024-11-19 12:59:22 +00:00
} else {
2024-11-21 16:03:55 +00:00
NotificationCenter.default.post(name: Notification.Name(kMonalRefresh), object: nil)
2024-11-19 12:59:22 +00:00
}
}
2024-11-22 14:45:38 +00:00
2024-11-22 16:34:56 +00:00
func addContact(contactJid: String, forAccountID: Int) async {
2024-11-22 16:54:07 +00:00
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
2024-11-22 16:34:56 +00:00
}
2024-11-22 14:45:38 +00:00
}
2024-11-19 12:59:22 +00:00
}
2024-11-20 15:52:49 +00:00
2024-11-21 13:32:38 +00:00
// MARK: - Try login from Login screen
2024-11-20 15:52:49 +00:00
private final class LoginTry {
weak var xmpp: MLXMPPManager?
var successObserver: AnyObject?
var failureObserver: AnyObject?
init(xmpp: MLXMPPManager) {
self.xmpp = xmpp
}
2024-11-21 13:32:38 +00:00
// TODO: Добавить автовключение отключенных аккаунтов при попытке ввести тот же JID
2024-11-20 15:52:49 +00:00
// Обработать кейс когда бесячий monalxmpp возвращает nil при попытке добавить тот же JID
func tryLogin(_ login: String, _ password: String) async -> Bool {
async let notify = await withCheckedContinuation { [weak self] continuation in
self?.successObserver = NotificationCenter.default.addObserver(forName: Notification.Name("kMLResourceBoundNotice"), object: nil, queue: .main) { notification in
print(notification.debugDescription)
continuation.resume(returning: true)
}
self?.failureObserver = NotificationCenter.default.addObserver(forName: Notification.Name("kXMPPError"), object: nil, queue: .main) { notification in
print(notification.debugDescription)
continuation.resume(returning: false)
}
}
defer {
if let successObserver {
NotificationCenter.default.removeObserver(successObserver)
}
if let failureObserver {
NotificationCenter.default.removeObserver(failureObserver)
}
}
let accountNumber = xmpp?.login(login, password: password)
let result = await notify
if let accountNumber, !result {
xmpp?.removeAccount(forAccountID: accountNumber)
}
2024-11-20 19:51:05 +00:00
2024-11-20 15:52:49 +00:00
return result
}
}
2024-11-21 16:03:55 +00:00
// MARK: - Handle notifications
private extension MonalXmppWrapper {
func subscribeToUpdates() {
2024-11-22 13:12:09 +00:00
// General
2024-11-21 16:03:55 +00:00
let generalRefresh = NotificationCenter.default.addObserver(forName: Notification.Name(kMonalRefresh), object: nil, queue: .main) { [weak self] _ in
2024-11-22 13:12:09 +00:00
self?.refreshAccounts()
self?.refreshContacts()
2024-11-23 16:23:56 +00:00
self?.refreshChats()
2024-11-22 13:12:09 +00:00
}
notificationObservers.append(generalRefresh)
2024-11-21 16:03:55 +00:00
2024-11-22 13:12:09 +00:00
// For contacts
let contactRefresh = NotificationCenter.default.addObserver(forName: Notification.Name(kMonalContactRefresh), object: nil, queue: .main) { [weak self] _ in
self?.refreshContacts()
2024-11-23 16:23:56 +00:00
self?.refreshChats()
2024-11-22 13:12:09 +00:00
}
let contactRemove = NotificationCenter.default.addObserver(forName: Notification.Name(kMonalContactRemoved), object: nil, queue: .main) { [weak self] _ in
self?.refreshContacts()
2024-11-23 16:23:56 +00:00
self?.refreshChats()
2024-11-22 13:12:09 +00:00
}
notificationObservers.append(contentsOf: [contactRefresh, contactRemove])
2024-11-23 16:23:56 +00:00
// For chats
// ???
2024-11-22 13:12:09 +00:00
}
2024-11-21 16:03:55 +00:00
2024-11-22 13:12:09 +00:00
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
2024-11-21 16:03:55 +00:00
}
2024-11-22 13:12:09 +00:00
}
func refreshContacts() {
2024-11-22 16:34:56 +00:00
contacts = db.contactList()
.filter { $0.isSubscribedTo || $0.hasOutgoingContactRequest || $0.isSubscribedFrom }
.filter { !$0.isSelfChat } // removed for now
.compactMap { Contact($0) }
2024-11-21 16:03:55 +00:00
}
2024-11-23 16:23:56 +00:00
func refreshChats() {
activeChats = db.activeContacts(withPinned: false)
.compactMap {
guard let contact = $0 as? MLContact else { return nil }
return Chat(contact)
}
}
2024-11-21 16:03:55 +00:00
}