From ced41759062c8103d52613cc1eabafd2e2359747 Mon Sep 17 00:00:00 2001 From: fmodf Date: Mon, 13 Jan 2025 04:42:08 +0100 Subject: [PATCH] wip --- AnotherXMPP/modules/roster/RosterModule.swift | 110 +++++++++++++----- 1 file changed, 78 insertions(+), 32 deletions(-) diff --git a/AnotherXMPP/modules/roster/RosterModule.swift b/AnotherXMPP/modules/roster/RosterModule.swift index 70eaaea..494577f 100644 --- a/AnotherXMPP/modules/roster/RosterModule.swift +++ b/AnotherXMPP/modules/roster/RosterModule.swift @@ -118,7 +118,7 @@ final class RosterModule: XmppModule { } // process stanza - return await processInbound(stanza: stanza) + return await processInbound(state: state, stanza: stanza) } else { return nil } @@ -130,47 +130,93 @@ final class RosterModule: XmppModule { } private extension RosterModule { - func processInbound(stanza: Stanza) async -> Event? { + func processInbound(state: ClientState, stanza: Stanza) async -> Event? { switch stanza.type { + // "set" type stanza from server is just "push", so change roster accordingly case .iq(.set): - return nil + // sanity check + if stanza.wrapped.attributes["to"] != state.jid.bare { + return nil + } - case .iq(.error): - return nil + // get exists roster items + var existItems: [RosterItem] = [] + if let data = await storage?.getRoster(jid: state.jid), let decoded = try? JSONDecoder().decode([XMLElement].self, from: data) { + existItems = decoded.compactMap { RosterItem(wrap: $0, owner: state.jid) } + } + // get item from stanza + let xmlItem = stanza.wrapped + .nodes + .first(where: { $0.name == "query" })? + .nodes + .first(where: { $0.name == "item" }) + guard let xmlItem, let item = RosterItem(wrap: xmlItem, owner: state.jid) else { return nil } + + // filter out exists items + existItems = existItems.filter { $0.id != item.id } + + // append updated/new item (if it not "removed") + if item.subsription != .remove { + existItems.append(item) + } + + // save roster + guard let data = try? JSONEncoder().encode(existItems.map { $0.wrapped }) else { return nil } + await storage?.setRoster(jid: state.jid, roster: data) + return .rosterUpdated + + // the "result" is an answer from one of our "set", or initial roster request case .iq(.result): + // get request stanza from registry + guard let respId = stanza.id, let req = await registry.deuque(for: respId) else { return nil } + + // get exists roster items + var existItems: [RosterItem] = [] + if let data = await storage?.getRoster(jid: state.jid), let decoded = try? JSONDecoder().decode([XMLElement].self, from: data) { + existItems = decoded.compactMap { RosterItem(wrap: $0, owner: state.jid) } + } + + // perform changes based on stanza request type + switch req.type { + // for simple roster request + case .iq(.get): + // get items from response + let stanzaItems = stanza.wrapped + .nodes + .first(where: { $0.name == "query" })? + .nodes + .filter { $0.name == "item" } + .compactMap { RosterItem(wrap: $0, owner: state.jid) } ?? [] + let stanzaItemsIds = stanzaItems.map { $0.id } + + // filter out exists items and append updated + existItems = existItems.filter { !stanzaItemsIds.contains($0.id) } + existItems.append(contentsOf: stanzaItems) + + // save roster + guard let data = try? JSONEncoder().encode(existItems.map { $0.wrapped }) else { return nil } + await storage?.setRoster(jid: state.jid, roster: data) + return .rosterUpdated + + // for result of one of our request + case .iq(.set): + break + + default: + break + } + + // append items from requested roster + return nil + + // TODO: add error handling here + case .iq(.error): return nil default: return nil } - // get items from stanza - // var items: [XMLElement] = [] - // switch stanza.type { - // case .iq(.set): - // break - // // return await processSet(state: state, stanza: stanza) - // - // case .iq(.result): - // let stanzaItems = stanza.wrapped - // .nodes - // .first(where: { $0.name == "query" })? - // .nodes - // .filter { $0.name == "item" } ?? [] - // items.append(contentsOf: stanzaItems) - // - // case .iq(.error): - // // handle errors here - // break - // - // default: - // break - // } - - // process items - - // result - // } }