This commit is contained in:
fmodf 2024-05-02 15:10:10 +04:00
parent dc993147f5
commit 1b0a62db32
8 changed files with 56 additions and 121 deletions

View file

@ -23,6 +23,7 @@ final class AppState: ObservableObject {
private let accountsService: AccountsService
private let rostersService: RostersService
private let chatService: ChatsService
private var conversationService: ConversationService?
private var cancellables: Set<AnyCancellable> = []
// publishers
@ -102,11 +103,23 @@ extension AppState {
}
// Chats
func startChat(_: Chat) {
// chatService.startChat(chat)
func startChat(_ chat: Chat) {
let client = xmppService.getClient(for: chat.account)
let roster = rostersService.getRoster(ownerJID: chat.account, contactJID: chat.participant)
let account = accountsService.getAccount(jid: chat.account)
if let client, let roster, let account {
conversationService = ConversationService(account: account, chat: chat, roster: roster, db: Database()._db, client: client)
flow = .chat
}
}
func startChat(_: Roster) {
// chatService.startChat(roster)
func startChat(_ roster: Roster) {
let client = xmppService.getClient(for: roster.bareJid)
let account = accountsService.getAccount(jid: roster.bareJid)
let chat = chatService.getChat(roster.bareJid, roster.contactBareJid)
if let client, let chat, let account {
conversationService = ConversationService(account: account, chat: chat, roster: roster, db: Database()._db, client: client)
flow = .chat
}
}
}

View file

@ -29,6 +29,10 @@ final class AccountsService: ObservableObject {
}
// MARK: - Account actions
func getAccount(jid: String) -> Account? {
accounts.first(where: { $0.bareJid == jid })
}
func addAccount(jid: String, password: String) {
guard let db = database else { return }
let account = Account(bareJid: jid, pass: password, isActive: true)

View file

@ -27,40 +27,26 @@ final class ChatsService: ObservableObject {
.store(in: &cancellables)
}
// MARK: - Chats actions
// func makeChatActive(_ chat: Chat) {
// guard let client = xmppClients.first(where: { $0.connectionConfiguration.userJid.stringValue == chat.account }) else { return }
// guard let roster = rosters.first(where: { $0.bareJid == chat.account && $0.contactBareJid == chat.participant }) else { return }
// guard let account = accounts.first(where: { $0.bareJid == chat.account }) else { return }
// activeChat = ConversationState(account: account, chat: chat, roster: roster, dbService: dbService, client: client)
// }
//
// func makeChatActive(_ roster: Roster) {
// guard let client = xmppClients.first(where: { $0.connectionConfiguration.userJid.stringValue == roster.bareJid }) else { return }
// let chat = getChat(roster.bareJid, roster.contactBareJid)
// guard let account = accounts.first(where: { $0.bareJid == chat.account }) else { return }
// activeChat = ConversationState(account: account, chat: chat, roster: roster, dbService: dbService, client: client)
// }
//
// // get existing chat or create a new one
// private func getChat(_ account: String, _ participant: String) -> Chat {
// if let chat = chats.first(where: { $0.account == account && $0.participant == participant }) {
// return chat
// } else {
// let chat = Chat(
// id: UUID().uuidString,
// account: account,
// participant: participant,
// type: .chat
// )
// do {
// try dbService._db.write { db in
// try chat.save(db)
// }
// } catch {
// logIt(.error, "Failed to create chat: \(error)")
// }
// return chat
// }
// }
// get existing chat or create a new one
func getChat(_ account: String, _ participant: String) -> Chat? {
guard let db = database else { return nil }
if let chat = chats.first(where: { $0.account == account && $0.participant == participant }) {
return chat
} else {
let chat = Chat(
id: UUID().uuidString,
account: account,
participant: participant,
type: .chat
)
do {
try db.write { db in
try chat.save(db)
}
} catch {
logIt(.error, "Failed to create chat: \(error)")
}
return chat
}
}
}

View file

@ -3,22 +3,22 @@ import Foundation
import GRDB
import Martin
final class ConversationState: ObservableObject {
final class ConversationService: ObservableObject {
private var account: Account
private var chat: Chat
private var roster: Roster
private var cancellables: Set<AnyCancellable> = []
weak var dbService: Database?
weak var database: DatabaseQueue?
weak var client: XMPPClient?
@Published var messages: [Message] = []
init(account: Account, chat: Chat, roster: Roster, dbService: Database, client: XMPPClient) {
init(account: Account, chat: Chat, roster: Roster, db: DatabaseQueue, client: XMPPClient) {
self.account = account
self.chat = chat
self.roster = roster
self.dbService = dbService
database = db
self.client = client
}
}

View file

@ -2,7 +2,7 @@ import Foundation
import GRDB
import Martin
final class XMPPMessageService {
final class MessageService {
func processMessage(_ message: Martin.Message, _ chat: Martin.ChatProtocol) {
print("---Message received: \(message.body) from \(message.from)\n--Chat: \(chat)")
}

View file

@ -35,6 +35,10 @@ final class RostersService: ObservableObject {
}
// MARK: - Roster actions
func getRoster(ownerJID: String, contactJID: String) -> Roster? {
rosters.first(where: { $0.bareJid == ownerJID && $0.contactBareJid == contactJID })
}
func addRoster(ownerJID: String, contactJID: String, name: String? = nil, groups: [String] = [], completion: @escaping (Result<Void, Error>) -> Void) {
guard let service = xmppService, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else {
return completion(.failure(XMPPError.item_not_found))

View file

@ -73,4 +73,8 @@ final class XMPPService: ObservableObject {
_ = client.disconnect()
}
}
func getClient(for jid: String) -> XMPPClient? {
clients.first { $0.connectionConfiguration.userJid.stringValue == jid }
}
}

View file

@ -1,76 +0,0 @@
import Combine
import Foundation
import GRDB
import Martin
protocol MartinsManager: Martin.RosterManager & Martin.ChatManager {}
final class XMPPService: ObservableObject {
private let manager: MartinsManager
@Published private(set) var clients: [XMPPClient] = []
init(manager: MartinsManager) {
self.manager = manager
}
func updateClients(for accounts: [Account]) {
// get simple diff
let forAdd = accounts
.filter { !self.clients.map { $0.connectionConfiguration.userJid.stringValue }.contains($0.bareJid) }
let forRemove = clients
.map { $0.connectionConfiguration.userJid.stringValue }
.filter { !accounts.map { $0.bareJid }.contains($0) }
// init and add clients
for account in forAdd {
let client = makeClient(for: account, with: manager)
clients.append(client)
// doClientSubscription(for: client)
client.login()
}
// remove clients
for jid in forRemove {
deinitClient(jid: jid)
}
}
private func makeClient(for account: Account, with manager: MartinsManager) -> XMPPClient {
let client = XMPPClient()
// register modules
// core modules RFC 6120
client.modulesManager.register(StreamFeaturesModule())
client.modulesManager.register(SaslModule())
client.modulesManager.register(AuthModule())
client.modulesManager.register(SessionEstablishmentModule())
client.modulesManager.register(ResourceBinderModule())
client.modulesManager.register(DiscoveryModule(identity: .init(category: "client", type: "iOS", name: Const.appName)))
// messaging modules RFC 6121
client.modulesManager.register(RosterModule(rosterManager: manager))
client.modulesManager.register(PresenceModule())
client.modulesManager.register(PubSubModule())
client.modulesManager.register(MessageModule(chatManager: manager))
client.modulesManager.register(MessageCarbonsModule())
client.modulesManager.register(MessageArchiveManagementModule())
// extensions
client.modulesManager.register(SoftwareVersionModule())
client.modulesManager.register(PingModule())
client.connectionConfiguration.userJid = .init(account.bareJid)
client.connectionConfiguration.credentials = .password(password: account.pass)
// add client to clients
return client
}
func deinitClient(jid: String) {
if let index = clients.firstIndex(where: { $0.connectionConfiguration.userJid.stringValue == jid }) {
let client = clients.remove(at: index)
_ = client.disconnect()
}
}
}