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)
|
2024-11-25 10:51:00 +00:00
|
|
|
|
|
|
|
|
|
// reconnect
|
2024-11-25 11:49:47 +00:00
|
|
|
|
// xmpp.nowForegrounded()
|
|
|
|
|
// xmpp.connectIfNecessary()
|
2024-11-21 16:03:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-23 23:22:07 +00:00
|
|
|
|
|
2024-11-24 18:42:46 +00:00
|
|
|
|
func chat(with: Contact) -> MonalChatWrapper {
|
2024-11-25 13:14:23 +00:00
|
|
|
|
// swiftlint:disable:next force_unwrapping
|
|
|
|
|
let account = accounts.first { $0.id == with.ownerId }!
|
|
|
|
|
let chatModel = MonalChatWrapper(account: account, contact: with, db: db, xmpp: xmpp)
|
2024-11-23 23:22:07 +00:00
|
|
|
|
return chatModel
|
|
|
|
|
}
|
2024-11-25 10:51:00 +00:00
|
|
|
|
|
|
|
|
|
func chat(with: Chat) -> MonalChatWrapper? {
|
2024-11-25 13:14:23 +00:00
|
|
|
|
guard let account = accounts.first(where: { $0.id == with.accountId }) else { return nil }
|
2024-11-25 10:51:00 +00:00
|
|
|
|
guard let contact = contacts.first(where: { $0.ownerId == with.accountId && $0.contactJid == with.participantJid }) else { return nil }
|
2024-11-25 13:14:23 +00:00
|
|
|
|
let chatModel = MonalChatWrapper(account: account, contact: contact, db: db, xmpp: xmpp)
|
2024-11-25 10:51:00 +00:00
|
|
|
|
return chatModel
|
|
|
|
|
}
|
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
|
2024-11-25 11:49:47 +00:00
|
|
|
|
self?.successObserver = NotificationCenter.default.addObserver(forName: Notification.Name("kMLResourceBoundNotice"), object: nil, queue: .main) { _ in
|
2024-11-20 15:52:49 +00:00
|
|
|
|
continuation.resume(returning: true)
|
|
|
|
|
}
|
2024-11-25 11:49:47 +00:00
|
|
|
|
self?.failureObserver = NotificationCenter.default.addObserver(forName: Notification.Name("kXMPPError"), object: nil, queue: .main) { _ in
|
2024-11-20 15:52:49 +00:00
|
|
|
|
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
|
|
|
|
}
|
2024-11-23 23:22:07 +00:00
|
|
|
|
|
|
|
|
|
// MARK: - Chat object
|
2024-11-24 18:42:46 +00:00
|
|
|
|
final class MonalChatWrapper: ObservableObject {
|
|
|
|
|
@Published private(set) var messages: [Message] = []
|
2024-11-25 13:14:23 +00:00
|
|
|
|
@Published var replyText: String = ""
|
2024-11-24 18:42:46 +00:00
|
|
|
|
|
2024-11-23 23:22:07 +00:00
|
|
|
|
let contact: Contact
|
2024-11-25 13:14:23 +00:00
|
|
|
|
|
|
|
|
|
private let monalContact: MLContact
|
|
|
|
|
private let account: Account
|
2024-11-24 18:42:46 +00:00
|
|
|
|
private let xmpp: MLXMPPManager
|
|
|
|
|
private let db: DataLayer
|
2024-11-25 10:51:00 +00:00
|
|
|
|
private var notificationObservers: [AnyObject] = []
|
2024-11-23 23:22:07 +00:00
|
|
|
|
|
2024-11-25 13:14:23 +00:00
|
|
|
|
init(account: Account, contact: Contact, db: DataLayer, xmpp: MLXMPPManager) {
|
2024-11-23 23:22:07 +00:00
|
|
|
|
self.contact = contact
|
2024-11-25 13:14:23 +00:00
|
|
|
|
self.account = account
|
2024-11-24 18:42:46 +00:00
|
|
|
|
self.db = db
|
|
|
|
|
self.xmpp = xmpp
|
2024-11-25 11:49:47 +00:00
|
|
|
|
|
2024-11-25 13:14:23 +00:00
|
|
|
|
// swiftlint:disable:next force_unwrapping
|
|
|
|
|
monalContact = db.contactList().first { $0.accountID.intValue == contact.ownerId && $0.contactJid == contact.contactJid }!
|
|
|
|
|
|
2024-11-25 11:49:47 +00:00
|
|
|
|
subscribe()
|
2024-11-25 13:14:23 +00:00
|
|
|
|
NotificationCenter.default.post(name: Notification.Name(kMonalNewMessageNotice), object: nil)
|
2024-11-24 18:42:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-11-25 10:51:00 +00:00
|
|
|
|
deinit {
|
|
|
|
|
notificationObservers.forEach { NotificationCenter.default.removeObserver($0) }
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-25 13:14:23 +00:00
|
|
|
|
func sendText(_ text: String) {
|
|
|
|
|
let msgId = db.addMessageHistory(
|
|
|
|
|
to: contact.contactJid,
|
|
|
|
|
forAccount: monalContact.accountID,
|
|
|
|
|
withMessage: text,
|
|
|
|
|
actuallyFrom: account.jid,
|
|
|
|
|
withId: UUID().uuidString,
|
|
|
|
|
encrypted: monalContact.isEncrypted,
|
|
|
|
|
messageType: kMessageTypeText,
|
|
|
|
|
mimeType: nil,
|
|
|
|
|
size: nil
|
|
|
|
|
)
|
|
|
|
|
xmpp.sendMessage(text, to: monalContact, isEncrypted: monalContact.isEncrypted, isUpload: false, messageId: "\(msgId)")
|
|
|
|
|
NotificationCenter.default.post(name: Notification.Name(kMonalSentMessageNotice), object: nil)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private extension MonalChatWrapper {
|
|
|
|
|
func subscribe() {
|
2024-11-25 10:51:00 +00:00
|
|
|
|
let newMsg = NotificationCenter.default.addObserver(forName: Notification.Name(kMonalNewMessageNotice), object: nil, queue: .main) { [weak self] _ in
|
|
|
|
|
self?.refreshMessages()
|
|
|
|
|
}
|
|
|
|
|
notificationObservers.append(newMsg)
|
2024-11-25 13:14:23 +00:00
|
|
|
|
|
|
|
|
|
let sentMsg = NotificationCenter.default.addObserver(forName: Notification.Name(kMonalSentMessageNotice), object: nil, queue: .main) { [weak self] _ in
|
|
|
|
|
self?.refreshMessages()
|
|
|
|
|
}
|
|
|
|
|
notificationObservers.append(sentMsg)
|
2024-11-25 10:51:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-11-25 13:14:23 +00:00
|
|
|
|
func refreshMessages() {
|
|
|
|
|
let messages = db.messages(forContact: contact.contactJid, forAccount: NSNumber(value: contact.ownerId))
|
|
|
|
|
.compactMap { obj -> Message? in
|
|
|
|
|
guard let message = obj as? MLMessage else { return nil }
|
2024-11-24 18:42:46 +00:00
|
|
|
|
return Message(message)
|
|
|
|
|
}
|
2024-11-25 13:14:23 +00:00
|
|
|
|
.sorted { $0.timestamp > $1.timestamp }
|
|
|
|
|
self.messages = messages
|
2024-11-23 23:22:07 +00:00
|
|
|
|
}
|
|
|
|
|
}
|