This commit is contained in:
fmodf 2024-12-17 16:28:30 +01:00
parent 0be3f68710
commit 7a722e200c
5 changed files with 51 additions and 24 deletions

View file

@ -66,6 +66,7 @@ final class TestStorage: XMPPStorage {
func setRoster(jid: JID, roster: Data) async {
self.roster[jid.bare] = roster
print("updated")
}
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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]

View file

@ -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
}
}
}