From 606d8001bd28fb9893a8e2a2447b1ca26c6d42f2 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Fri, 10 Mar 2023 21:40:09 -0500 Subject: [PATCH] And emoji reply is a reaction Retracting an emoji reply retracts the reaction --- .../conversations/entities/Conversation.java | 25 +++++++++++++ .../siacs/conversations/entities/Message.java | 36 ++++++++++++++++++- .../ui/ConversationFragment.java | 17 +++++++-- 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 8382af89a349663ae58d045cbe573672f7b4262b..cdc922b22eff26aaeab5c403bb6f44ce3d61e6f8 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -69,10 +69,12 @@ import org.json.JSONObject; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; +import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; +import java.util.Set; import java.util.Timer; import java.util.TimerTask; @@ -541,6 +543,29 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return false; } + public Set findOwnReactionsTo(String id) { + Set reactionEmoji = new HashSet<>(); + Element reactions = null; + synchronized (this.messages) { + for (Message message : this.messages) { + if (message.getStatus() < Message.STATUS_SEND) continue; + + final Element r = message.getReactions(); + if (r != null && r.getAttribute("id") != null && id.equals(r.getAttribute("id"))) { + reactions = r; + } + } + } + if (reactions != null) { + for (Element el : reactions.getChildren()) { + if (el.getName().equals("reaction") && el.getNamespace().equals("urn:xmpp:reactions:0")) { + reactionEmoji.add(el.getContent()); + } + } + } + return reactionEmoji; + } + public void populateWithMessages(final List messages) { synchronized (this.messages) { messages.clear(); diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 52d67d4448b50260945e688ff6ca611d29abed17..3a0b677bcd7ef0f1657e4e219301c1c0bb88c316 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -33,6 +33,7 @@ import java.time.Duration; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -380,13 +381,17 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable return values; } + public String replyId() { + return conversation.getMode() == Conversation.MODE_MULTI ? getServerMsgId() : getRemoteMsgId(); + } + public Message reply() { Message m = new Message(conversation, QuoteHelper.quote(MessageUtils.prepareQuote(this)) + "\n", ENCRYPTION_NONE); m.setThread(getThread()); m.addPayload( new Element("reply", "urn:xmpp:reply:0") .setAttribute("to", getCounterpart()) - .setAttribute("id", conversation.getMode() == Conversation.MODE_MULTI ? getServerMsgId() : getRemoteMsgId()) + .setAttribute("id", replyId()) ); final Element fallback = new Element("fallback", "urn:xmpp:fallback:0").setAttribute("for", "urn:xmpp:reply:0"); fallback.addChild("body", "urn:xmpp:fallback:0") @@ -396,6 +401,35 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable return m; } + public Message react(String emoji) { + Set emojis = new HashSet<>(); + if (conversation instanceof Conversation) emojis = ((Conversation) conversation).findOwnReactionsTo(replyId()); + emojis.add(emoji); + final Message m = reply(); + m.appendBody(emoji); + final Element fallback = new Element("fallback", "urn:xmpp:fallback:0").setAttribute("for", "urn:xmpp:reactions:0"); + fallback.addChild("body", "urn:xmpp:fallback:0"); + m.addPayload(fallback); + final Element reactions = new Element("reactions", "urn:xmpp:reactions:0").setAttribute("id", replyId()); + for (String oneEmoji : emojis) { + reactions.addChild("reaction", "urn:xmpp:reactions:0").setContent(oneEmoji); + } + m.addPayload(reactions); + return m; + } + + public Element getReactions() { + if (this.payloads == null) return null; + + for (Element el : this.payloads) { + if (el.getName().equals("reactions") && el.getNamespace().equals("urn:xmpp:reactions:0")) { + return el; + } + } + + return null; + } + public String getConversationUuid() { return conversationUuid; } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 851e208956896415238d188e48b823be4ef82c33..b83ae0478c608989447ad7385be20f2cfe99522e 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -137,6 +137,7 @@ import eu.siacs.conversations.ui.util.ViewUtil; import eu.siacs.conversations.ui.widget.EditMessage; import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.Compatibility; +import eu.siacs.conversations.utils.Emoticons; import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.MessageUtils; import eu.siacs.conversations.utils.MimeUtils; @@ -895,8 +896,12 @@ public class ConversationFragment extends XmppFragment final Message message; if (conversation.getCorrectingMessage() == null) { if (conversation.getReplyTo() != null) { - message = conversation.getReplyTo().reply(); - message.appendBody(body); + if (Emoticons.isEmoji(body)) { + message = conversation.getReplyTo().react(body); + } else { + message = conversation.getReplyTo().reply(); + message.appendBody(body); + } message.setEncryption(conversation.getNextEncryption()); } else { message = new Message(conversation, body, conversation.getNextEncryption()); @@ -1568,6 +1573,14 @@ public class ConversationFragment extends XmppFragment while (message.mergeable(message.next())) { message = message.next(); } + Element reactions = message.getReactions(); + if (reactions != null) { + for (Element el : reactions.getChildren()) { + if (message.getQuoteableBody().endsWith(el.getContent())) { + reactions.removeChild(el); + } + } + } message.setBody(" "); message.putEdited(message.getUuid(), message.getServerMsgId()); message.setServerMsgId(null);