From 7a722e200c9a9c04be070c13c68271bf06d5faa9 Mon Sep 17 00:00:00 2001 From: fmodf Date: Tue, 17 Dec 2024 16:28:30 +0100 Subject: [PATCH] wip --- AnotherIM/View/TestScreen.swift | 1 + AnotherIM/xmpp/XMPPClient.swift | 16 +++++- AnotherIM/xmpp/models/Roster.swift | 6 +-- AnotherIM/xmpp/models/XMLElement.swift | 2 +- .../xmpp/modules/roster/RosterModule.swift | 50 ++++++++++++------- 5 files changed, 51 insertions(+), 24 deletions(-) diff --git a/AnotherIM/View/TestScreen.swift b/AnotherIM/View/TestScreen.swift index e916a0e..2e77561 100644 --- a/AnotherIM/View/TestScreen.swift +++ b/AnotherIM/View/TestScreen.swift @@ -66,6 +66,7 @@ final class TestStorage: XMPPStorage { func setRoster(jid: JID, roster: Data) async { self.roster[jid.bare] = roster + print("updated") } } diff --git a/AnotherIM/xmpp/XMPPClient.swift b/AnotherIM/xmpp/XMPPClient.swift index 0d6b183..4585017 100644 --- a/AnotherIM/xmpp/XMPPClient.swift +++ b/AnotherIM/xmpp/XMPPClient.swift @@ -43,7 +43,9 @@ enum Event { case streamReady case requestRoster - // case gotRoster // by request or by server push + case rosterUpdated + case addRosterItem(jidStr: String) + case deleteRosterItem(jidStr: String) } // MARK: State @@ -118,6 +120,18 @@ extension XMPPClient { await fire(.startClientLogin(jid: jid, credsId: credentialsId)) } } + + func addContact(jidStr: String) { + Task { + await fire(.addRosterItem(jidStr: jidStr)) + } + } + + func deleteRosterItem(jidStr: String) { + Task { + await fire(.deleteRosterItem(jidStr: jidStr)) + } + } } // MARK: Private part diff --git a/AnotherIM/xmpp/models/Roster.swift b/AnotherIM/xmpp/models/Roster.swift index 796d8b2..c105f35 100644 --- a/AnotherIM/xmpp/models/Roster.swift +++ b/AnotherIM/xmpp/models/Roster.swift @@ -30,7 +30,7 @@ enum RosterSubsriptionType: String { // Roster is a "transparent" structure // which is just wrap xml item around -struct Roster: Identifiable, Equatable { +struct RosterItem: Identifiable, Equatable { let owner: String let wrapped: XMLElement @@ -62,8 +62,8 @@ struct Roster: Identifiable, Equatable { return RosterSubsriptionType(rawValue: str) ?? .none } - static func == (_ rhs: Roster, _ lhs: Roster) -> Bool { - rhs.id == lhs.id + static func == (_ rhs: RosterItem, _ lhs: RosterItem) -> Bool { + rhs.id == lhs.id && rhs.wrapped == lhs.wrapped } } diff --git a/AnotherIM/xmpp/models/XMLElement.swift b/AnotherIM/xmpp/models/XMLElement.swift index f9d9d81..64919ce 100644 --- a/AnotherIM/xmpp/models/XMLElement.swift +++ b/AnotherIM/xmpp/models/XMLElement.swift @@ -1,6 +1,6 @@ import Foundation -struct XMLElement: Codable, CustomStringConvertible { +struct XMLElement: Codable, Equatable, CustomStringConvertible { let name: String let xmlns: String? let attributes: [String: String] diff --git a/AnotherIM/xmpp/modules/roster/RosterModule.swift b/AnotherIM/xmpp/modules/roster/RosterModule.swift index e0488b9..3928af5 100644 --- a/AnotherIM/xmpp/modules/roster/RosterModule.swift +++ b/AnotherIM/xmpp/modules/roster/RosterModule.swift @@ -1,6 +1,7 @@ import Foundation // TODO: add versioning (XEP-0237) if needed +// TODO: implement error catching final class RosterModule: XmppModule { let id = "Roseter module" @@ -31,12 +32,11 @@ final class RosterModule: XmppModule { } case .stanzaInbound(let stanza): - if stanza.type == .iq(.result) { - if let query = stanza.wrapped.nodes.first(where: { $0.name == "query" }), query.xmlns == "jabber:iq:roster" { - return await processRoster(state: state, xml: query) - } + if let query = stanza.wrapped.nodes.first(where: { $0.name == "query" }), query.xmlns == "jabber:iq:roster" { + return await processRoster(state: state, stanza: stanza) + } else { + return nil } - return nil default: return nil @@ -45,26 +45,38 @@ final class RosterModule: XmppModule { } private extension RosterModule { - func processRoster(state: ClientState, xml: XMLElement) async -> Event? { + func processRoster(state: ClientState, stanza: Stanza) async -> Event? { + // get inner query + guard let query = stanza.wrapped.nodes.first(where: { $0.name == "query" }) + else { return nil } + // get exists roster items - var existItems: [Roster] = [] + var existItems: [RosterItem] = [] if let data = await storage?.getRoster(jid: state.jid), let decoded = try? JSONDecoder().decode([XMLElement].self, from: data) { - existItems = decoded.compactMap { Roster(wrap: $0, owner: state.jid) } + existItems = decoded.compactMap { RosterItem(wrap: $0, owner: state.jid) } } - // extract items from incoming xml - var newItems = xml.nodes - .compactMap { Roster(wrap: $0, owner: state.jid) } - - // manage it ????? - var roster: [XMLElement] = newItems.map { $0.wrapped } - - // save updated roster - if let data = try? JSONEncoder().encode(roster) { + // process push (.set from server) + if stanza.type == .iq(.set) { + guard + let item = query.nodes.first(where: { $0.name == "item" }), + let new = RosterItem(wrap: item, owner: state.jid) + else { return nil } + existItems = existItems.filter { $0.jid != new.jid } + existItems.append(new) + guard let data = try? JSONEncoder().encode(existItems.map { $0.wrapped }) else { return nil } await storage?.setRoster(jid: state.jid, roster: data) - } + return .rosterUpdated - return nil + // process get response (.result from server) + } else if stanza.type == .iq(.get) { + let items = query.nodes.filter { $0.name == "item" } + guard let data = try? JSONEncoder().encode(items) else { return nil } + await storage?.setRoster(jid: state.jid, roster: data) + return .rosterUpdated + } else { + return nil + } } }