git » sdk » commit 71f1dea

Support for removeReaction of custom emoji

author Stephen Paul Weber
2024-11-14 05:29:52 UTC
committer Stephen Paul Weber
2024-11-14 05:29:52 UTC
parent 05d737984469a2e93a1c8533eab8a2d654092d5c

Support for removeReaction of custom emoji

snikket/Chat.hx +28 -7
snikket/ChatMessage.hx +32 -31
snikket/Message.hx +3 -3
snikket/Reaction.hx +5 -3
snikket/persistence/browser.js +2 -2

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<String>):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<String, Bool> = [];
-					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<String, Bool> = [];
+						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<String>;
 
-	public function new(senderId: String, timestamp: String, text: String, key: Null<String> = null) {
+	public function new(senderId: String, timestamp: String, text: String, envelopeId: Null<String> = null, key: Null<String> = 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<String> = 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);
 		}
 	}