| author | Stephen Paul Weber
<singpolyma@singpolyma.net> 2025-10-27 03:37:45 UTC |
| committer | Stephen Paul Weber
<singpolyma@singpolyma.net> 2025-10-27 03:37:45 UTC |
| parent | 3bf6e6bf9760713e5cfa2805faa68ae63f16cd0d |
| borogove/Chat.hx | +2 | -2 |
| borogove/Client.hx | +25 | -23 |
| borogove/Presence.hx | +5 | -1 |
| borogove/persistence/IDB.js | +2 | -2 |
| borogove/persistence/Sqlite.hx | +6 | -2 |
diff --git a/borogove/Chat.hx b/borogove/Chat.hx index fed5314..a0e6ddd 100644 --- a/borogove/Chat.hx +++ b/borogove/Chat.hx @@ -524,7 +524,7 @@ abstract class Chat { presence.caps = caps; setPresence(resource, presence); } else { - setPresence(resource, new Presence(caps, null)); + setPresence(resource, new Presence(caps, null, null)); } } @@ -1336,7 +1336,7 @@ class Channel extends Chat { } else { final nick = JID.parse(participantId).resource; final placeholderUri = Color.defaultPhoto(participantId, nick == null ? " " : nick.charAt(0)); - return new Participant(nick ?? "", null, placeholderUri, false); + return new Participant(nick ?? "", presence[nick]?.avatarHash?.toUri(), placeholderUri, false); } } diff --git a/borogove/Client.hx b/borogove/Client.hx index 02973d8..33355ec 100644 --- a/borogove/Client.hx +++ b/borogove/Client.hx @@ -320,7 +320,6 @@ class Client extends 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()); @@ -328,13 +327,18 @@ class Client extends EventEmitter { trace("Presence for unknown JID: " + stanza.attr.get("from")); return EventUnhandled; } + + final mucUser = stanza.getChild("x", "http://jabber.org/protocol/muc#user"); + final avatarSha1Hex = stanza.findText("{vcard-temp:x:update}x/photo#"); + final avatarSha1 = avatarSha1Hex == null ? null : Hash.fromHex("sha-1", avatarSha1Hex); + if (c == null) { - chat.setPresence(JID.parse(stanza.attr.get("from")).resource, new Presence(null, mucUser)); + chat.setPresence(JID.parse(stanza.attr.get("from")).resource, new Presence(null, mucUser, avatarSha1)); persistence.storeChats(accountId(), [chat]); if (chat.livePresence()) this.trigger("chats/update", [chat]); } else { final handleCaps = (caps) -> { - chat.setPresence(JID.parse(stanza.attr.get("from")).resource, new Presence(caps, mucUser)); + chat.setPresence(JID.parse(stanza.attr.get("from")).resource, new Presence(caps, mucUser, avatarSha1)); if (mucUser == null || chat.livePresence()) persistence.storeChats(accountId(), [chat]); return chat; }; @@ -365,28 +369,26 @@ class Client extends EventEmitter { } }); } - if (from.isBare()) { - final avatarSha1Hex = stanza.findText("{vcard-temp:x:update}x/photo#"); - if (avatarSha1Hex != null) { - final avatarSha1 = Hash.fromHex("sha-1", avatarSha1Hex)?.hash; - chat.setAvatarSha1(avatarSha1); + if (avatarSha1 != null) { + if (from.isBare()) { + chat.setAvatarSha1(avatarSha1.hash); persistence.storeChats(accountId(), [chat]); - persistence.hasMedia("sha-1", avatarSha1).then((has) -> { - if (has) { - if (chat.livePresence()) this.trigger("chats/update", [chat]); - } else { - final vcardGet = new VcardTempGet(from); - vcardGet.onFinished(() -> { - final vcard = vcardGet.getResult(); - if (vcard.photo == null) return; - persistence.storeMedia(vcard.photo.mime, vcard.photo.data.getData()).then(_ -> { - this.trigger("chats/update", [chat]); - }); - }); - sendQuery(vcardGet); - } - }); } + persistence.hasMedia("sha-1", avatarSha1.hash).then((has) -> { + if (has) { + if (chat.livePresence()) this.trigger("chats/update", [chat]); + } else { + final vcardGet = new VcardTempGet(from); + vcardGet.onFinished(() -> { + final vcard = vcardGet.getResult(); + if (vcard.photo == null) return; + persistence.storeMedia(vcard.photo.mime, vcard.photo.data.getData()).then(_ -> { + this.trigger("chats/update", [chat]); + }); + }); + sendQuery(vcardGet); + } + }); } return EventHandled; } diff --git a/borogove/Presence.hx b/borogove/Presence.hx index cb45e4a..64b9261 100644 --- a/borogove/Presence.hx +++ b/borogove/Presence.hx @@ -1,12 +1,16 @@ package borogove; +import borogove.Hash; + @:expose class Presence { public var caps:Null<Caps>; public final mucUser:Null<Stanza>; + public final avatarHash:Null<Hash>; - public function new(caps: Null<Caps>, mucUser: Null<Stanza>) { + public function new(caps: Null<Caps>, mucUser: Null<Stanza>, avatarHash: Null<Hash>) { this.caps = caps; this.mucUser = mucUser; + this.avatarHash = avatarHash; } } diff --git a/borogove/persistence/IDB.js b/borogove/persistence/IDB.js index f6da818..4c7c3ba 100644 --- a/borogove/persistence/IDB.js +++ b/borogove/persistence/IDB.js @@ -272,7 +272,7 @@ export default async (dbname, media, tokenize, stemmer) => { chatId: chat.chatId, trusted: chat.trusted, avatarSha1: chat.avatarSha1, - presence: new Map([...chat.presence.entries()].map(([k, p]) => [k, { caps: p.caps?.ver(), mucUser: p.mucUser?.toString() }])), + presence: new Map([...chat.presence.entries()].map(([k, p]) => [k, { caps: p.caps?.ver(), mucUser: p.mucUser?.toString(), avatarHash: p.avatarHash?.serializeUri() }])), displayName: chat.displayName, uiState: chat.uiState, isBlocked: chat.isBlocked, @@ -297,7 +297,7 @@ export default async (dbname, media, tokenize, stemmer) => { r.trusted, r.avatarSha1, new Map(await Promise.all((r.presence instanceof Map ? [...r.presence.entries()] : Object.entries(r.presence)).map( - async ([k, p]) => [k, new borogove.Presence(p.caps && await this.getCaps(p.caps), p.mucUser && borogove.Stanza.parse(p.mucUser))] + async ([k, p]) => [k, new borogove.Presence(p.caps && await this.getCaps(p.caps), p.mucUser && borogove.Stanza.parse(p.mucUser), p.avatarHash && borogove.Hash.fromUri(p.avatarHash))] ))), r.displayName, r.uiState, diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx index 68994c8..7b0884a 100644 --- a/borogove/persistence/Sqlite.hx +++ b/borogove/persistence/Sqlite.hx @@ -184,7 +184,7 @@ class Sqlite implements Persistence implements KeyValueStore { storeChatTimer = haxe.Timer.delay(() -> { final mapPresence = (chat: Chat) -> { - final storePresence: DynamicAccess<{ ?caps: String, ?mucUser: String }> = {}; + final storePresence: DynamicAccess<{ ?caps: String, ?mucUser: String, ?avatarHash: String }> = {}; final caps: Map<BytesData, Caps> = []; for (resource => presence in chat.presence) { if (storePresence[resource ?? ""] == null) storePresence[resource ?? ""] = {}; @@ -195,6 +195,9 @@ class Sqlite implements Persistence implements KeyValueStore { if (presence.mucUser != null) { storePresence[resource ?? ""].mucUser = presence.mucUser.toString(); } + if (presence.avatarHash != null) { + storePresence[resource ?? ""].avatarHash = presence.avatarHash.serializeUri(); + } } storeCapsSet(caps); return storePresence; @@ -268,7 +271,8 @@ class Sqlite implements Persistence implements KeyValueStore { presenceJson.remove(resource); presenceMap[resource] = new Presence( presence.caps == null ? null : capsMap[presence.caps], - presence.mucUser == null || Config.constrainedMemoryMode ? null : Stanza.parse(presence.mucUser) + presence.mucUser == null || Config.constrainedMemoryMode ? null : Stanza.parse(presence.mucUser), + presence.avatarHash == null ? null : Hash.fromUri(presence.avatarHash) ); } // FIXME: Empty OMEMO contact device ids hardcoded in next line