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 let xmpp: MLXMPPManager private let db: DataLayer private let 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) } } var chatTitle: String { contact.name } func sendText(_ text: String) { 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 acc = monalContact.account else { return } if mamRequestInProgress { return } mamRequestInProgress = true let lastStanzaId = messages.last?.messageId // 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 } 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 monalContact.isEncrypted != new { monalContact.toggleEncryption(new) } } }