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

287 lines
10 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)
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-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-27 15:49:36 +00:00
private var mamRequestInProgress = false
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 {
contact.name ?? contact.contactJid
}
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
self?.mamRequestInProgress = false
if !(msgs ?? []).isEmpty {
self?.refreshMessages()
}
}
}
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 }
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
}