From 71f1dea234bdb3f368b8c5089061dc89c4a32672 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Thu, 14 Nov 2024 00:29:52 -0500 Subject: [PATCH] Support for removeReaction of custom emoji --- snikket/Chat.hx | 35 +++++++++++++++---- snikket/ChatMessage.hx | 63 +++++++++++++++++----------------- snikket/Message.hx | 6 ++-- snikket/Reaction.hx | 8 +++-- snikket/persistence/browser.js | 4 +-- 5 files changed, 70 insertions(+), 46 deletions(-) diff --git a/snikket/Chat.hx b/snikket/Chat.hx index 12a8590..d2d479e 100644 --- a/snikket/Chat.hx +++ b/snikket/Chat.hx @@ -195,6 +195,7 @@ abstract class Chat { **/ public function addReaction(m:ChatMessage, reaction:Reaction) { final toSend = m.reply(); + toSend.localId = ID.long(); reaction.render( (text) -> { toSend.text = text.replace("\u{fe0f}", ""); @@ -214,7 +215,7 @@ abstract class Chat { @param m ChatMessage to remove the reaction from @param reaction the emoji to remove **/ - abstract public function removeReaction(m:ChatMessage, reaction:String):Void; + abstract public function removeReaction(m:ChatMessage, reaction:Reaction):Void; abstract private function sendChatState(state: String, threadId: Null):Void; @@ -725,7 +726,7 @@ class DirectChat extends Chat { message.versions = [toSend]; // This is a correction message.localId = localId; client.storeMessage(message, (corrected) -> { - toSend.versions = corrected.versions; + toSend.versions = corrected.localId == localId ? corrected.versions : [message]; for (recipient in message.recipients) { message.to = recipient; client.sendStanza(toSend.asStanza()); @@ -776,11 +777,21 @@ class DirectChat extends Chat { } @HaxeCBridge.noemit // on superclass as abstract - public function removeReaction(m:ChatMessage, reaction:String) { + public function removeReaction(m:ChatMessage, reaction:Reaction) { + if (Std.is(reaction, CustomEmojiReaction)) { + if (reaction.envelopeId == null) throw "Cannot remove custom emoji reaction without envelopeId"; + final correct = m.reply(); + correct.localId = ID.long(); + correct.setHtml(""); + correct.text = null; + correctMessage(reaction.envelopeId, correct); + return; + } + // NOTE: doing it this way means no fallback behaviour final reactions = []; for (areaction => reacts in m.reactions) { - if (areaction != reaction) { + if (areaction != reaction.key) { final react = reacts.find(r -> r.senderId == client.accountId()); if (react != null && !Std.is(react, CustomEmojiReaction)) { reactions.push(react); @@ -1162,7 +1173,7 @@ class Channel extends Chat { message.versions = [toSend]; // This is a correction message.localId = localId; client.storeMessage(message, (corrected) -> { - toSend.versions = corrected.versions; + toSend.versions = corrected.localId == localId ? corrected.versions : [message]; client.sendStanza(toSend.asStanza()); if (localId == lastMessage?.localId) { setLastMessage(corrected); @@ -1207,11 +1218,21 @@ class Channel extends Chat { } @HaxeCBridge.noemit // on superclass as abstract - public function removeReaction(m:ChatMessage, reaction:String) { + public function removeReaction(m:ChatMessage, reaction:Reaction) { + if (Std.is(reaction, CustomEmojiReaction)) { + if (reaction.envelopeId == null) throw "Cannot remove custom emoji reaction without envelopeId"; + final correct = m.reply(); + correct.localId = ID.long(); + correct.setHtml(""); + correct.text = null; + correctMessage(reaction.envelopeId, correct); + return; + } + // NOTE: doing it this way means no fallback behaviour final reactions = []; for (areaction => reacts in m.reactions) { - if (areaction != reaction) { + if (areaction != reaction.key) { final react = reacts.find(r -> r.senderId == getFullJid().asString()); if (react != null && !Std.is(react, CustomEmojiReaction)) reactions.push(react); } diff --git a/snikket/ChatMessage.hx b/snikket/ChatMessage.hx index 1234d9e..adebd57 100644 --- a/snikket/ChatMessage.hx +++ b/snikket/ChatMessage.hx @@ -412,43 +412,44 @@ class ChatMessage { final replyToM = replyToMessage; if (replyToM != null) { - if (body == null) body = ""; - final lines = replyToM.text?.split("\n") ?? []; - var quoteText = ""; - for (line in lines) { - if (!~/^(?:> ?){3,}/.match(line)) { - if (line.charAt(0) == ">") { - quoteText += ">" + line + "\n"; - } else { - quoteText += "> " + line + "\n"; + final replyId = replyToM.getReplyId(); + if (body != null) { + final lines = replyToM.text?.split("\n") ?? []; + var quoteText = ""; + for (line in lines) { + if (!~/^(?:> ?){3,}/.match(line)) { + if (line.charAt(0) == ">") { + quoteText += ">" + line + "\n"; + } else { + quoteText += "> " + line + "\n"; + } } } - } - final reaction = EmojiUtil.isEmoji(StringTools.trim(body)) ? StringTools.trim(body) : null; - body = quoteText + body; - final replyId = replyToM.getReplyId(); - if (replyId != null) { - final codepoints = StringUtil.codepointArray(quoteText); - if (reaction != null) { - final addedReactions: Map = []; - stanza.tag("reactions", { xmlns: "urn:xmpp:reactions:0", id: replyId }); - stanza.textTag("reaction", reaction); - addedReactions[reaction] = true; - - for (areaction => reactions in replyToM.reactions) { - if (!(addedReactions[areaction] ?? false) && reactions.find(r -> r.senderId == senderId()) != null) { - addedReactions[areaction] = true; - stanza.textTag("reaction", areaction); + final reaction = EmojiUtil.isEmoji(StringTools.trim(body)) ? StringTools.trim(body) : null; + body = quoteText + body; + if (replyId != null) { + final codepoints = StringUtil.codepointArray(quoteText); + if (reaction != null) { + final addedReactions: Map = []; + stanza.tag("reactions", { xmlns: "urn:xmpp:reactions:0", id: replyId }); + stanza.textTag("reaction", reaction); + addedReactions[reaction] = true; + + for (areaction => reactions in replyToM.reactions) { + if (!(addedReactions[areaction] ?? false) && reactions.find(r -> r.senderId == senderId()) != null) { + addedReactions[areaction] = true; + stanza.textTag("reaction", areaction); + } } + stanza.up(); + stanza.tag("fallback", { xmlns: "urn:xmpp:fallback:0", "for": "urn:xmpp:reactions:0" }) + .tag("body").up().up(); } - stanza.up(); - stanza.tag("fallback", { xmlns: "urn:xmpp:fallback:0", "for": "urn:xmpp:reactions:0" }) - .tag("body").up().up(); + stanza.tag("fallback", { xmlns: "urn:xmpp:fallback:0", "for": "urn:xmpp:reply:0" }) + .tag("body", { start: "0", end: Std.string(codepoints.length) }).up().up(); } - stanza.tag("fallback", { xmlns: "urn:xmpp:fallback:0", "for": "urn:xmpp:reply:0" }) - .tag("body", { start: "0", end: Std.string(codepoints.length) }).up().up(); - stanza.tag("reply", { xmlns: "urn:xmpp:reply:0", to: replyToM.from?.asString(), id: replyId }).up(); } + if (replyId != null) stanza.tag("reply", { xmlns: "urn:xmpp:reply:0", to: replyToM.from?.asString(), id: replyId }).up(); } for (attachment in attachments) { diff --git a/snikket/Message.hx b/snikket/Message.hx index 72e4613..dc06ea4 100644 --- a/snikket/Message.hx +++ b/snikket/Message.hx @@ -169,7 +169,7 @@ class Message { msg.chatId(), msg.senderId(), timestamp, - reactions.map(text -> new Reaction(msg.senderId(), timestamp, text)), + reactions.map(text -> new Reaction(msg.senderId(), timestamp, text, msg.localId)), EmojiReactions ))); } @@ -229,7 +229,7 @@ class Message { msg.chatId(), msg.senderId(), timestamp, - [new Reaction(msg.senderId(), timestamp, text.trim())], + [new Reaction(msg.senderId(), timestamp, text.trim(), msg.localId)], AppendReactions ))); } @@ -249,7 +249,7 @@ class Message { msg.chatId(), msg.senderId(), timestamp, - [new CustomEmojiReaction(msg.senderId(), timestamp, els[0].attr.get("alt") ?? "", hash.serializeUri())], + [new CustomEmojiReaction(msg.senderId(), timestamp, els[0].attr.get("alt") ?? "", hash.serializeUri(), msg.localId)], AppendReactions ))); } diff --git a/snikket/Reaction.hx b/snikket/Reaction.hx index 281be21..2fab0be 100644 --- a/snikket/Reaction.hx +++ b/snikket/Reaction.hx @@ -9,11 +9,13 @@ class Reaction { public final timestamp: String; public final text: String; public final key: String; + public final envelopeId: Null; - public function new(senderId: String, timestamp: String, text: String, key: Null = null) { + public function new(senderId: String, timestamp: String, text: String, envelopeId: Null = null, key: Null = null) { this.senderId = senderId; this.timestamp = timestamp; this.text = text.replace("\u{fe0f}", ""); + this.envelopeId = envelopeId; this.key = key ?? this.text; } @@ -26,8 +28,8 @@ class Reaction { class CustomEmojiReaction extends Reaction { public final uri: String; - public function new(senderId: String, timestamp: String, text: String, uri: String) { - super(senderId, timestamp, text, uri); + public function new(senderId: String, timestamp: String, text: String, uri: String, envelopeId: Null = null) { + super(senderId, timestamp, text, envelopeId, uri); this.uri = uri; } diff --git a/snikket/persistence/browser.js b/snikket/persistence/browser.js index d2de20e..42a7806 100644 --- a/snikket/persistence/browser.js +++ b/snikket/persistence/browser.js @@ -70,9 +70,9 @@ const browser = (dbname, tokenize, stemmer) => { function hydrateObjectReaction(r) { if (r.uri) { - return new snikket.CustomEmojiReaction(r.senderId, r.timestamp, r.text, r.uri); + return new snikket.CustomEmojiReaction(r.senderId, r.timestamp, r.text, r.uri, r.envelopeId); } else { - return new snikket.Reaction(r.senderId, r.timestamp, r.text, r.key); + return new snikket.Reaction(r.senderId, r.timestamp, r.text, r.envelopeId, r.key); } }