| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-10-19 03:00:37 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2023-10-19 03:00:37 UTC |
| parent | 2e643339aada086ad26929665f74eeb011247ace |
| xmpp/Chat.hx | +27 | -11 |
| xmpp/Client.hx | +54 | -1 |
| xmpp/persistence/browser.js | +4 | -0 |
| xmpp/queries/PubsubGet.hx | +1 | -1 |
diff --git a/xmpp/Chat.hx b/xmpp/Chat.hx index a7a598c..b8d539e 100644 --- a/xmpp/Chat.hx +++ b/xmpp/Chat.hx @@ -30,13 +30,15 @@ abstract class Chat { public var jingleSessions: Map<String, xmpp.jingle.Session> = []; private var displayName:String; public var uiState = Open; + private var extensions: Stanza; - public function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open) { + public function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, extensions: Null<Stanza> = null) { this.client = client; this.stream = stream; this.persistence = persistence; this.chatId = chatId; this.uiState = uiState; + this.extensions = extensions ?? new Stanza("extensions", { xmlns: "urn:xmpp:bookmarks:1" }); this.displayName = chatId; } @@ -54,6 +56,14 @@ abstract class Chat { abstract public function close():Void; + public function updateFromBookmark(item: Stanza) { + final conf = item.getChild("conference", "urn:xmpp:bookmarks:1"); + final fn = conf.attr.get("name"); + if (fn != null) setDisplayName(fn); + uiState = (conf.attr.get("autojoin") == "1" || conf.attr.get("autojoin") == "true") ? Open : Closed; + extensions = conf.getChild("extensions") ?? new Stanza("extensions", { xmlns: "urn:xmpp:bookmarks:1" }); + } + public function getPhoto(callback:(String)->Void) { callback(Color.defaultPhoto(chatId, getDisplayName().charAt(0))); } @@ -250,10 +260,10 @@ class DirectChat extends Chat { @:expose class Channel extends Chat { - private var disco: Caps = new Caps("", [], ["http://jabber.org/protocol/muc"]); // TODO: persist this + public var disco: Caps = new Caps("", [], ["http://jabber.org/protocol/muc"]); - public function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, ?disco: Caps) { - super(client, stream, persistence, chatId, uiState); + public function new(client:Client, stream:GenericStream, persistence:Persistence, chatId:String, uiState = Open, extensions = null, ?disco: Caps) { + super(client, stream, persistence, chatId, uiState, extensions); if (disco != null) this.disco = disco; selfPing(disco == null); } @@ -369,15 +379,13 @@ class Channel extends Chat { } public function bookmark() { - // TODO: we should have been created from an existing bookmark if there was one, - // and we need to be sure to preserve everything we don't mean to change if this is an update stream.sendIq( new Stanza("iq", { type: "set" }) .tag("pubsub", { xmlns: "http://jabber.org/protocol/pubsub" }) .tag("publish", { node: "urn:xmpp:bookmarks:1" }) .tag("item", { id: chatId }) .tag("conference", { xmlns: "urn:xmpp:bookmarks:1", name: getDisplayName(), autojoin: uiState == Closed ? "false" : "true" }) - .textTag("nick", client.displayName()) + .addChild(extensions) .up().up() .tag("publish-options") .tag("x", { xmlns: "jabber:x:data", type: "submit" }) @@ -435,16 +443,20 @@ class SerializedChat { public final avatarSha1:Null<BytesData>; public final caps:haxe.DynamicAccess<Caps>; public final displayName:Null<String>; - public final uiState: String; + public final uiState:String; + public final extensions:String; + public final disco:Null<Caps>; public final klass:String; - public function new(chatId: String, trusted: Bool, avatarSha1: Null<BytesData>, caps: haxe.DynamicAccess<Caps>, displayName: Null<String>, uiState: Null<String>, klass: String) { + public function new(chatId: String, trusted: Bool, avatarSha1: Null<BytesData>, caps: haxe.DynamicAccess<Caps>, displayName: Null<String>, uiState: Null<String>, extensions: Null<String>, disco: Null<Caps>, klass: String) { this.chatId = chatId; this.trusted = trusted; this.avatarSha1 = avatarSha1; this.caps = caps; this.displayName = displayName; this.uiState = uiState ?? "Open"; + this.extensions = extensions ?? "<extensions xmlns='urn:app:bookmarks:1' />"; + this.disco = disco; this.klass = klass; } @@ -455,10 +467,14 @@ class SerializedChat { default: Open; } + final extensionsStanza = Stanza.fromXml(Xml.parse(extensions)); + final chat = if (klass == "DirectChat") { - new DirectChat(client, stream, persistence, chatId, uiStateEnum); + new DirectChat(client, stream, persistence, chatId, uiStateEnum, extensionsStanza); } else if (klass == "Channel") { - new Channel(client, stream, persistence, chatId, uiStateEnum); + final channel = new Channel(client, stream, persistence, chatId, uiStateEnum, extensionsStanza); + channel.disco = disco ?? new Caps("", [], ["http://jabber.org/protocol/muc"]); + channel; } else { throw "Unknown class: " + klass; } diff --git a/xmpp/Client.hx b/xmpp/Client.hx index 79a04e4..4d42ce2 100644 --- a/xmpp/Client.hx +++ b/xmpp/Client.hx @@ -322,6 +322,7 @@ class Client extends xmpp.EventEmitter { ); rosterGet(); + bookmarksGet(); sync(() -> { // Set self to online sendPresence(); @@ -352,7 +353,7 @@ class Client extends xmpp.EventEmitter { } final chat = if (caps.isChannel(chatId)) { - final channel = new Channel(this, this.stream, this.persistence, chatId, Open, caps); + final channel = new Channel(this, this.stream, this.persistence, chatId, Open, null, caps); chats.unshift(channel); channel; } else { @@ -544,6 +545,58 @@ class Client extends xmpp.EventEmitter { sendQuery(rosterGet); } + private function bookmarksGet() { + final pubsubGet = new PubsubGet(null, "urn:xmpp:bookmarks:1"); + pubsubGet.onFinished(() -> { + for (item in pubsubGet.getResult()) { + if (item.attr.get("id") != null) { + final chat = getChat(item.attr.get("id")); + if (chat == null) { + final discoGet = new DiscoInfoGet(item.attr.get("id")); + discoGet.onFinished(() -> { + final resultCaps = discoGet.getResult(); + if (resultCaps == null) { + final err = discoGet.responseStanza?.getChild("error")?.getChild(null, "urn:ietf:params:xml:ns:xmpp-stanzas"); + if (err == null || err?.name == "service-unavailable" || err?.name == "feature-not-implemented") { + final chat = getDirectChat(item.attr.get("id"), false); + chat.updateFromBookmark(item); + persistence.storeChat(accountId(), chat); + this.trigger("chats/update", [chat]); + } + } else { + persistence.storeCaps(resultCaps); + final identity = resultCaps.identities[0]; + final conf = item.getChild("conference", "urn:xmpp:bookmarks:1"); + if (conf.attr.get("name") == null) { + conf.attr.set("name", identity?.name); + } + if (resultCaps.isChannel(item.attr.get("id"))) { + final uiState = (conf.attr.get("autojoin") == "1" || conf.attr.get("autojoin") == "true") ? Open : Closed; + final chat = new Channel(this, this.stream, this.persistence, item.attr.get("id"), uiState, null, resultCaps); + chat.updateFromBookmark(item); + chats.unshift(chat); + persistence.storeChat(accountId(), chat); + this.trigger("chats/update", [chat]); + } else { + final chat = getDirectChat(item.attr.get("id"), false); + chat.updateFromBookmark(item); + persistence.storeChat(accountId(), chat); + this.trigger("chats/update", [chat]); + } + } + }); + sendQuery(discoGet); + } else { + chat.updateFromBookmark(item); + persistence.storeChat(accountId(), chat); + this.trigger("chats/update", [chat]); + } + } + } + }); + sendQuery(pubsubGet); + } + private function sync(?callback: ()->Void) { persistence.lastId(jid, null, (lastId) -> doSync(callback, lastId)); } diff --git a/xmpp/persistence/browser.js b/xmpp/persistence/browser.js index 19a86be..c10a2a3 100644 --- a/xmpp/persistence/browser.js +++ b/xmpp/persistence/browser.js @@ -84,6 +84,8 @@ exports.xmpp.persistence = { caps: chat.caps, displayName: chat.displayName, uiState: chat.uiState?.toString(), + extensions: chat.extensions?.toString(), + disco: chat.disco, class: chat instanceof xmpp.DirectChat ? "DirectChat" : (chat instanceof xmpp.Channel ? "Channel" : "Chat") }); }, @@ -99,6 +101,8 @@ exports.xmpp.persistence = { r.caps, r.displayName, r.uiState, + r.extensions, + r.disco, r.class )))); }, diff --git a/xmpp/queries/PubsubGet.hx b/xmpp/queries/PubsubGet.hx index 29407f8..0399de7 100644 --- a/xmpp/queries/PubsubGet.hx +++ b/xmpp/queries/PubsubGet.hx @@ -16,7 +16,7 @@ class PubsubGet extends GenericQuery { private var responseStanza:Stanza; private var result: Array<Stanza>; - public function new(to: String, node: String, ?itemId: String) { + public function new(to: Null<String>, node: String, ?itemId: String) { var attr: DynamicAccess<String> = { node: node }; if (ver != null) attr["ver"] = ver; /* Build basic query */