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

264 lines
9 KiB
Swift
Raw Normal View History

2024-11-19 12:59:22 +00:00
import Foundation
import monalxmpp
2024-11-29 17:35:20 +00:00
enum AccountsAvailability {
case noAccounts
case allDisabled
case someEnabled
}
2024-11-19 12:59:22 +00:00
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)
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()
2024-11-29 23:06:11 +00:00
xmpp.reconnectAll()
2024-11-21 16:03:55 +00:00
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 {
2024-11-29 17:43:27 +00:00
let scenario = ScenarioLogIn()
let result = await scenario.tryLogin(login, password)
2024-11-20 15:52:49 +00:00
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-28 16:34:41 +00:00
var contact = contacts.first(where: { $0.ownerId == with.accountId && $0.contactJid == with.participantJid })
if contact == nil {
let semaphore = DispatchSemaphore(value: 0)
Task {
await addContact(contactJid: with.participantJid, forAccountID: with.accountId)
semaphore.signal()
}
semaphore.wait()
refreshChats()
refreshContacts()
contact = contacts.first(where: { $0.ownerId == with.accountId && $0.contactJid == with.participantJid })
}
guard let contact 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 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-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-29 22:18:54 +00:00
@Published private(set) var mamRequestInProgress = false
2024-11-28 15:46:16 +00:00
@Published var isOmemoEnabled: Bool {
didSet {
toggleOmemo(isOmemoEnabled)
}
}
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-28 15:46:16 +00:00
isOmemoEnabled = monalContact.isEncrypted
2024-11-25 13:14:23 +00:00
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-28 15:46:16 +00:00
//
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-28 15:46:16 +00:00
var chatTitle: String {
2024-11-29 23:06:11 +00:00
contact.name
2024-11-28 15:46:16 +00:00
}
2024-11-25 13:14:23 +00:00
func sendText(_ text: String) {
2024-11-25 15:44:05 +00:00
let newMessageId = UUID().uuidString
_ = db.addMessageHistory(
2024-11-25 13:14:23 +00:00
to: contact.contactJid,
forAccount: monalContact.accountID,
withMessage: text,
actuallyFrom: account.jid,
2024-11-25 15:44:05 +00:00
withId: newMessageId,
2024-11-25 13:14:23 +00:00
encrypted: monalContact.isEncrypted,
messageType: kMessageTypeText,
mimeType: nil,
size: nil
)
2024-11-25 15:44:05 +00:00
print(newMessageId)
xmpp.sendMessage(text, to: monalContact, isEncrypted: monalContact.isEncrypted, isUpload: false, messageId: newMessageId)
2024-11-25 13:14:23 +00:00
}
2024-11-27 15:49:36 +00:00
func requestMAM() {
if mamRequestInProgress { return }
mamRequestInProgress = true
guard let acc = xmpp.getEnabledAccount(forID: NSNumber(value: account.id)) else { return }
let lastStanzaId = messages.last?.stanzaId // last here because list is reversed
?? db.lastStanzaId(forAccount: NSNumber(value: account.id))
acc.setMAMQueryMostRecentFor(monalContact, before: lastStanzaId) { [weak self] msgs, _ in
2024-11-29 23:06:11 +00:00
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
2024-11-29 22:18:54 +00:00
self?.mamRequestInProgress = false
if !(msgs ?? []).isEmpty {
2024-11-29 17:35:20 +00:00
self?.refreshMessages()
}
2024-11-27 15:49:36 +00:00
}
}
}
2024-11-25 13:14:23 +00:00
}
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 }
2024-11-29 15:07:28 +00:00
self.messages = messages
2024-11-23 23:22:07 +00:00
}
2024-11-28 15:46:16 +00:00
func toggleOmemo(_ new: Bool) {
if monalContact.isEncrypted != new {
monalContact.toggleEncryption(new)
}
}
2024-11-23 23:22:07 +00:00
}