import Foundation import monalxmpp final class WrapperChat: ObservableObject { @Published private(set) var messages: [Message] = [] @Published var replyText: String = "" @Published private(set) var mamRequestInProgress = false @Published var isOmemoEnabled: Bool { didSet { toggleOmemo(isOmemoEnabled) } } let contact: Contact let account: Account private var xmpp: MLXMPPManager? private var db: DataLayer? private var monalContact: MLContact? private var notificationObservers: [AnyObject] = [] init?(with: Contact, account: Account) { contact = with xmpp = MLXMPPManager.sharedInstance() db = DataLayer.sharedInstance() self.account = account guard let monalContact = db?.contactList() .first(where: { $0.accountID.intValue == with.ownerId && $0.contactJid == with.contactJid }) else { return nil } self.monalContact = monalContact isOmemoEnabled = monalContact.isEncrypted subscribe() reloadMessages() } init?(with: Chat, account: Account) { xmpp = MLXMPPManager.sharedInstance() db = DataLayer.sharedInstance() self.account = account guard let monalContact = db?.contactList() .first(where: { $0.accountID.intValue == with.accountId && $0.contactJid == with.participantJid }) else { return nil } self.monalContact = monalContact isOmemoEnabled = monalContact.isEncrypted guard let contact = Contact(monalContact) else { return nil } self.contact = contact subscribe() reloadMessages() } deinit { notificationObservers.forEach { NotificationCenter.default.removeObserver($0) } // print("Chat wrapper deinit") } func deactivate() { db = nil xmpp = nil monalContact = nil messages = [] notificationObservers.forEach { NotificationCenter.default.removeObserver($0) } // print("Chat wrapper deactivated") } var chatTitle: String { contact.name } func sendText(_ text: String) { guard let monalContact else { return } let newMessageId = UUID().uuidString let histId = db?.addMessageHistory( to: contact.contactJid, forAccount: monalContact.accountID, withMessage: text, actuallyFrom: account.jid, withId: newMessageId, encrypted: monalContact.isEncrypted, messageType: kMessageTypeText, mimeType: nil, size: nil ) if let newMlMessage = db?.message(forHistoryID: histId), let message = Message(newMlMessage) { messages.insert(message, at: 0) } xmpp?.sendMessage(text, to: monalContact, isEncrypted: monalContact.isEncrypted, isUpload: false, messageId: newMessageId) } func requestMAM() { guard let monalContact else { return } guard let acc = monalContact.account else { return } if mamRequestInProgress { return } mamRequestInProgress = true let lastStanzaId = messages.last?.stanzaId // last here because list is reversed ?? db?.lastStanzaId(forAccount: NSNumber(value: contact.ownerId)) acc.setMAMQueryMostRecentFor(monalContact, before: lastStanzaId) { [weak self] msgs, _ in DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in self?.mamRequestInProgress = false if let msgs, !msgs.isEmpty { print(msgs) self?.reloadMessages() } } } } } private extension WrapperChat { func subscribe() { let notificationNames = [ kMonalNewMessageNotice, kMonalHistoryMessagesNotice, kMonalMessageDisplayedNotice, kMonalMessageErrorNotice, kMonalSentMessageNotice, kMonalMessageDisplayedNotice ] notificationObservers = notificationNames.map { name in NotificationCenter.default.addObserver(forName: Notification.Name(name), object: nil, queue: .main) { [weak self] notification in self?.processEvent(notification: notification) } } } func processEvent(notification: Notification) { switch notification.name.rawValue { case kMonalSentMessageNotice: if let stanza = notification.userInfo?["message"] as? XMPPStanza, let id = stanza.id { if let index = messages.firstIndex(where: { $0.messageId == id }) { var message = messages[index] message.status = .sent messages[index] = message } } default: guard let mlMessage = notification.userInfo?["message"] as? MLMessage else { return } guard let message = Message(mlMessage) else { return } guard message.accountId == contact.ownerId, message.participantJid == contact.contactJid else { return } if let index = messages.firstIndex(where: { $0.id == message.id }) { messages[index] = message } else { messages.append(message) messages.sort { $0.timestamp > $1.timestamp } } } } func reloadMessages() { 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 } return Message(message) } .sorted { $0.timestamp > $1.timestamp } self.messages = messages ?? [] } func toggleOmemo(_ new: Bool) { if let monalContact { if monalContact.isEncrypted != new { monalContact.toggleEncryption(new) } } } }