git » sdk » commit 21b1d3f

Store mucUser data along with presence

author Stephen Paul Weber
2023-11-30 00:41:29 UTC
committer Stephen Paul Weber
2023-12-11 15:59:52 UTC
parent c959e8f6f318effe4ca6cbe2b181bae10ac67d2d

Store mucUser data along with presence

xmpp/Chat.hx +22 -4
xmpp/Client.hx +4 -3
xmpp/Presence.hx +3 -1
xmpp/persistence/browser.js +20 -15

diff --git a/xmpp/Chat.hx b/xmpp/Chat.hx
index 1b42a08..93bfef4 100644
--- a/xmpp/Chat.hx
+++ b/xmpp/Chat.hx
@@ -127,7 +127,7 @@ abstract class Chat {
 			presence.caps = caps;
 			setPresence(resource, presence);
 		} else {
-			setPresence(resource, new Presence(caps));
+			setPresence(resource, new Presence(caps, null));
 		}
 	}
 
@@ -398,7 +398,7 @@ class Channel extends Chat {
 
 	override public function setPresence(resource:String, presence:Presence) {
 		super.setPresence(resource, presence);
-		if (!inSync && resource == client.displayName()) {
+		if (!inSync && presence?.mucUser?.allTags("status").find((status) -> status.attr.get("code") == "110") != null) {
 			persistence.lastId(client.accountId(), chatId, doSync);
 		}
 	}
@@ -452,11 +452,29 @@ class Channel extends Chat {
 	}
 
 	override public function livePresence() {
-		return presence.exists(client.displayName());
+		for (nick => p in presence) {
+			for (status in p?.mucUser?.allTags("status") ?? []) {
+				if (status.attr.get("code") == "110") {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	private function nickInUse() {
+		for (nick => p in presence) {
+			for (status in p?.mucUser?.allTags("status") ?? []) {
+				if (status.attr.get("code") == "110") {
+					return nick;
+				}
+			}
+		}
+		return client.displayName();
 	}
 
 	private function getFullJid() {
-		return JID.parse(chatId).withResource(client.displayName());
+		return JID.parse(chatId).withResource(nickInUse());
 	}
 
 	public function getParticipants() {
diff --git a/xmpp/Client.hx b/xmpp/Client.hx
index dde8942..a46ab8b 100644
--- a/xmpp/Client.hx
+++ b/xmpp/Client.hx
@@ -297,6 +297,7 @@ class Client extends xmpp.EventEmitter {
 		stream.on("presence", function(event) {
 			final stanza:Stanza = event.stanza;
 			final c = stanza.getChild("c", "http://jabber.org/protocol/caps");
+			final mucUser = stanza.getChild("x", "http://jabber.org/protocol/muc#user");
 			if (stanza.attr.get("from") != null && stanza.attr.get("type") == null) {
 				final from = JID.parse(stanza.attr.get("from"));
 				final chat = getChat(from.asBare().asString());
@@ -305,7 +306,7 @@ class Client extends xmpp.EventEmitter {
 					return EventUnhandled;
 				}
 				if (c == null) {
-					chat.setCaps(JID.parse(stanza.attr.get("from")).resource, null);
+					chat.setPresence(JID.parse(stanza.attr.get("from")).resource, new Presence(null, mucUser));
 					persistence.storeChat(jid, chat);
 					if (chat.livePresence()) this.trigger("chats/update", [chat]);
 				} else {
@@ -313,14 +314,14 @@ class Client extends xmpp.EventEmitter {
 						if (caps == null) {
 							final discoGet = new DiscoInfoGet(stanza.attr.get("from"), c.attr.get("node") + "#" + c.attr.get("ver"));
 							discoGet.onFinished(() -> {
-								chat.setCaps(JID.parse(stanza.attr.get("from")).resource, discoGet.getResult());
+								chat.setPresence(JID.parse(stanza.attr.get("from")).resource, new Presence(discoGet.getResult(), mucUser));
 								if (discoGet.getResult() != null) persistence.storeCaps(discoGet.getResult());
 								persistence.storeChat(jid, chat);
 								if (chat.livePresence()) this.trigger("chats/update", [chat]);
 							});
 							sendQuery(discoGet);
 						} else {
-							chat.setCaps(JID.parse(stanza.attr.get("from")).resource, caps);
+							chat.setPresence(JID.parse(stanza.attr.get("from")).resource, new Presence(caps, mucUser));
 							persistence.storeChat(jid, chat);
 							if (chat.livePresence()) this.trigger("chats/update", [chat]);
 						}
diff --git a/xmpp/Presence.hx b/xmpp/Presence.hx
index fe25e63..5ea29bb 100644
--- a/xmpp/Presence.hx
+++ b/xmpp/Presence.hx
@@ -3,8 +3,10 @@ package xmpp;
 @:expose
 class Presence {
 	public var caps:Null<Caps>;
+	public var mucUser:Null<Stanza>;
 
-	public function new(caps: Null<Caps>) {
+	public function new(caps: Null<Caps>, mucUser: Null<Stanza>) {
 		this.caps = caps;
+		this.mucUser = mucUser;
 	}
 }
diff --git a/xmpp/persistence/browser.js b/xmpp/persistence/browser.js
index 4ebb5b4..f10b21b 100644
--- a/xmpp/persistence/browser.js
+++ b/xmpp/persistence/browser.js
@@ -167,7 +167,7 @@ exports.xmpp.persistence = {
 					chatId: chat.chatId,
 					trusted: chat.trusted,
 					avatarSha1: chat.avatarSha1,
-					presence: chat.presence,
+					presence: Object.fromEntries(Object.entries(chat.presence || {}).map(([k, p]) => [k, { caps: p.caps?.ver(), mucUser: p.mucUser?.toString() }])),
 					displayName: chat.displayName,
 					uiState: chat.uiState?.toString(),
 					extensions: chat.extensions?.toString(),
@@ -177,20 +177,25 @@ exports.xmpp.persistence = {
 			},
 
 			getChats: function(account, callback) {
-				const tx = db.transaction(["chats"], "readonly");
-				const store = tx.objectStore("chats");
-				const range = IDBKeyRange.bound([account], [account, []]);
-				promisifyRequest(store.getAll(range)).then((result) => callback(result.map((r) => new xmpp.SerializedChat(
-					r.chatId,
-					r.trusted,
-					r.avatarSha1,
-					r.presence,
-					r.displayName,
-					r.uiState,
-					r.extensions,
-					r.disco,
-					r.class
-				))));
+				(async () => {
+					const tx = db.transaction(["chats"], "readonly");
+					const store = tx.objectStore("chats");
+					const range = IDBKeyRange.bound([account], [account, []]);
+					const result = await promisifyRequest(store.getAll(range));
+					return await Promise.all(result.map(async (r) => new xmpp.SerializedChat(
+						r.chatId,
+						r.trusted,
+						r.avatarSha1,
+						Object.fromEntries(await Promise.all(Object.entries(r.presence).map(
+							async ([k, p]) => [k, new xmpp.Presence(p.caps && await new Promise((resolve) => this.getCaps(p.caps, resolve)), p.mucUser && xmpp.Stanza.parse(p.mucUser))]
+						))),
+						r.displayName,
+						r.uiState,
+						r.extensions,
+						r.disco,
+						r.class
+					)));
+				})().then(callback);
 			},
 
 			getChatsUnreadDetails: function(account, chatsArray, callback) {