From fbf28124e02ff2d514deca7ca6df66b814e6e09f Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Fri, 4 Oct 2024 01:21:10 -0500 Subject: [PATCH] Allow removing custom reaction tapping the chip --- .../conversations/entities/Conversation.java | 22 +++++++++++++++++-- .../conversations/entities/Reaction.java | 15 ++++++++----- .../conversations/parser/MessageParser.java | 6 +++-- .../services/XmppConnectionService.java | 6 +++-- .../conversations/ui/BindingAdapters.java | 18 ++++++++++----- .../ui/adapter/MessageAdapter.java | 22 +++++++++++++++++++ 6 files changed, 72 insertions(+), 17 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 3240ecdd94247486ea8e1775042e8cc16af0dd76..0ed2256bc8c28d94d7acb979429c5c4c68605c1d 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -297,6 +297,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl for (int i = messages.size() - 1; i >= 0; --i) { final Message message = messages.get(i); if (message.getSubject() != null && !message.isOOb() && (message.getRawBody() == null || message.getRawBody().length() == 0)) continue; + if ((message.getRawBody() == null || "".equals(message.getRawBody()) || " ".equals(message.getRawBody())) && message.getReply() != null && message.edited() && message.getHtml() != null) continue; if (asReaction(message) != null) continue; if (message.isRead()) { return first; @@ -313,6 +314,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl synchronized (this.messages) { for (final Message message : Lists.reverse(this.messages)) { if (message.getSubject() != null && !message.isOOb() && (message.getRawBody() == null || message.getRawBody().length() == 0)) continue; + if ((message.getRawBody() == null || "".equals(message.getRawBody()) || " ".equals(message.getRawBody())) && message.getReply() != null && message.edited() && message.getHtml() != null) continue; if (asReaction(message) != null) continue; if (message.getStatus() == Message.STATUS_RECEIVED) { final String serverMsgId = message.getServerMsgId(); @@ -714,6 +716,12 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl thread.first = m; } } + + if ((m.getRawBody() == null || "".equals(m.getRawBody()) || " ".equals(m.getRawBody())) && m.getReply() != null && m.edited() && m.getHtml() != null) { + iterator.remove(); + continue; + } + final var asReaction = asReaction(m); if (asReaction != null) { reactions.put(asReaction.first, asReaction.second); @@ -734,9 +742,16 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl protected Pair asReaction(Message m) { final var reply = m.getReply(); if (reply != null && reply.getAttribute("id") != null) { + final String envelopeId; + if (m.isCarbon() || m.getStatus() == Message.STATUS_RECEIVED) { + envelopeId = m.getRemoteMsgId(); + } else { + envelopeId = m.getUuid(); + } + final var body = m.getBody(true).toString().replaceAll("\\s", ""); if (Emoticons.isEmoji(body)) { - return new Pair<>(reply.getAttribute("id"), new Reaction(body, null, m.getStatus() <= Message.STATUS_RECEIVED, m.getCounterpart(), m.getTrueCounterpart(), m.getOccupantId())); + return new Pair<>(reply.getAttribute("id"), new Reaction(body, null, m.getStatus() <= Message.STATUS_RECEIVED, m.getCounterpart(), m.getTrueCounterpart(), m.getOccupantId(), envelopeId)); } else { final var html = m.getHtml(); if (html == null) return null; @@ -758,7 +773,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl } if (source != null && source.length() > 0 && source.substring(0, 4).equals("cid:")) { final Cid cid = BobTransfer.cid(Uri.parse(source)); - return new Pair<>(reply.getAttribute("id"), new Reaction(shortcode, cid, m.getStatus() <= Message.STATUS_RECEIVED, m.getCounterpart(), m.getTrueCounterpart(), m.getOccupantId())); + return new Pair<>(reply.getAttribute("id"), new Reaction(shortcode, cid, m.getStatus() <= Message.STATUS_RECEIVED, m.getCounterpart(), m.getTrueCounterpart(), m.getOccupantId(), envelopeId)); } } } @@ -941,6 +956,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl for(final Message message : Lists.reverse(this.messages)) { if (message.getSubject() != null && !message.isOOb() && (message.getRawBody() == null || message.getRawBody().length() == 0)) continue; if (asReaction(message) != null) continue; + if ((message.getRawBody() == null || "".equals(message.getRawBody()) || " ".equals(message.getRawBody())) && message.getReply() != null && message.edited() && message.getHtml() != null) continue; return message; } @@ -1411,6 +1427,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl for(final Message message : Lists.reverse(this.messages)) { if (message.getSubject() != null && !message.isOOb() && (message.getRawBody() == null || message.getRawBody().length() == 0)) continue; if (asReaction(message) != null) continue; + if ((message.getRawBody() == null || "".equals(message.getRawBody()) || " ".equals(message.getRawBody())) && message.getReply() != null && message.edited() && message.getHtml() != null) continue; final boolean muted = xmppConnectionService != null && message.getStatus() == Message.STATUS_RECEIVED && getMode() == Conversation.MODE_MULTI && xmppConnectionService.isMucUserMuted(new MucOptions.User(null, getJid(), message.getOccupantId(), null, null)); if (muted) continue; if (message.isRead()) { @@ -1431,6 +1448,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl for (Message message : messages) { if (message.getSubject() != null && !message.isOOb() && (message.getRawBody() == null || message.getRawBody().length() == 0)) continue; if (asReaction(message) != null) continue; + if ((message.getRawBody() == null || "".equals(message.getRawBody()) || " ".equals(message.getRawBody())) && message.getReply() != null && message.edited() && message.getHtml() != null) continue; if (message.getStatus() == Message.STATUS_RECEIVED) { ++count; } diff --git a/src/main/java/eu/siacs/conversations/entities/Reaction.java b/src/main/java/eu/siacs/conversations/entities/Reaction.java index a4db43d27bcb62cbbe2b665519b44904b5398415..0f654a1285772bec5621ea4fb43bd38f041d9028 100644 --- a/src/main/java/eu/siacs/conversations/entities/Reaction.java +++ b/src/main/java/eu/siacs/conversations/entities/Reaction.java @@ -60,6 +60,7 @@ public class Reaction { public final Jid trueJid; public final String occupantId; public final Cid cid; + public final String envelopeId; public Reaction( final String reaction, @@ -67,13 +68,15 @@ public class Reaction { boolean received, final Jid from, final Jid trueJid, - final String occupantId) { + final String occupantId, + final String envelopeId) { this.reaction = reaction; this.cid = cid; this.received = received; this.from = from; this.trueJid = trueJid; this.occupantId = occupantId; + this.envelopeId = envelopeId; } public static String toString(final Collection reactions) { @@ -97,12 +100,13 @@ public class Reaction { final boolean received, final Jid from, final Jid trueJid, - final String occupantId) { + final String occupantId, + final String envelopeId) { final ImmutableSet.Builder builder = new ImmutableSet.Builder<>(); builder.addAll(Collections2.filter(existing, e -> !occupantId.equals(e.occupantId))); builder.addAll( Collections2.transform( - reactions, r -> new Reaction(r, null, received, from, trueJid, occupantId))); + reactions, r -> new Reaction(r, null, received, from, trueJid, occupantId, envelopeId))); return builder.build(); } @@ -133,13 +137,14 @@ public class Reaction { final Collection existing, final Collection reactions, final boolean received, - final Jid from) { + final Jid from, + final String envelopeId) { final ImmutableSet.Builder builder = new ImmutableSet.Builder<>(); builder.addAll( Collections2.filter(existing, e -> !from.asBareJid().equals(e.from.asBareJid()))); builder.addAll( Collections2.transform( - reactions, r -> new Reaction(r, null, received, from, null, null))); + reactions, r -> new Reaction(r, null, received, from, null, null, envelopeId))); return builder.build(); } diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index a13ac294361c8c3549c1743a98e93b86ee8bcd71..1722041c7e658c8d3c9a5616e84dd8a5594783af 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -1306,7 +1306,8 @@ public class MessageParser extends AbstractParser implements Consumer> onModifiedReactions, final Consumer onCustomReaction, + final Consumer onCustomReactionRemove, final Runnable addReaction) { - setReactions(chipGroup, conversation, reactions, true, onModifiedReactions, onCustomReaction, addReaction); + setReactions(chipGroup, conversation, reactions, true, onModifiedReactions, onCustomReaction, onCustomReactionRemove, addReaction); } public static void setReactionsOnSent( final ChipGroup chipGroup, final Reaction.Aggregated reactions, final Consumer> onModifiedReactions) { - setReactions(chipGroup, null, reactions, false, onModifiedReactions, null, null); + setReactions(chipGroup, null, reactions, false, onModifiedReactions, null, null, null); } private static void setReactions( @@ -50,6 +51,7 @@ public class BindingAdapters { final boolean onReceived, final Consumer> onModifiedReactions, final Consumer onCustomReaction, + final Consumer onCustomReactionRemove, final Runnable addReaction) { final var context = chipGroup.getContext(); final var size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 35, context.getResources().getDisplayMetrics()); @@ -71,9 +73,9 @@ public class BindingAdapters { chip.setLayoutParams(layoutParams); chip.setChipCornerRadius(corner); emoji.setupChip(chip, count); - final boolean oneOfOurs = aggregated.ourReactions.contains(emoji.toString()); + final var oneOfOurs = reaction.getValue().stream().filter(r -> !r.received).findFirst(); // received = surface; sent = surface high matches bubbles - if (oneOfOurs) { + if (oneOfOurs.isPresent()) { chip.setChipBackgroundColor( MaterialColors.getColorStateListOrNull( context, @@ -89,12 +91,16 @@ public class BindingAdapters { chip.setTextStartPadding(0.0f); chip.setOnClickListener( v -> { - if (oneOfOurs) { - onModifiedReactions.accept( + if (oneOfOurs.isPresent()) { + if (emoji instanceof EmojiSearch.CustomEmoji) { + onCustomReactionRemove.accept(oneOfOurs.get()); + } else { + onModifiedReactions.accept( ImmutableSet.copyOf( Collections2.filter( aggregated.ourReactions, r -> !r.equals(emoji.toString())))); + } } else { if (emoji instanceof EmojiSearch.CustomEmoji) { onCustomReaction.accept((EmojiSearch.CustomEmoji) emoji); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 396e5cfe3035ded2e210ae7f3d5b1f60b167dad2..72ed62165c083e6962d038db41532226ac914ff9 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -1524,6 +1524,7 @@ public class MessageAdapter extends ArrayAdapter { aggregatedReactions, reactions -> sendReactions(message, reactions), emoji -> sendCustomReaction(message, emoji), + reaction -> removeCustomReaction(conversation, reaction), () -> addReaction(message)); } else if (type == SENT) { final var aggregatedReactions = conversation instanceof Conversation ? ((Conversation) conversation).aggregatedReactionsFor(message, reactionThumbnailer) : message.getAggregatedReactions(); @@ -1533,6 +1534,7 @@ public class MessageAdapter extends ArrayAdapter { aggregatedReactions, reactions -> sendReactions(message, reactions), emoji -> sendCustomReaction(message, emoji), + reaction -> removeCustomReaction(conversation, reaction), () -> addReaction(message)); } @@ -1615,6 +1617,26 @@ public class MessageAdapter extends ArrayAdapter { new Thread(() -> activity.xmppConnectionService.sendMessage(message)).start(); } + private void removeCustomReaction(final Conversational conversation, final Reaction reaction) { + if (!(conversation instanceof Conversation)) { + Toast.makeText(activity, R.string.could_not_add_reaction, Toast.LENGTH_LONG).show(); + return; + } + + final var message = new Message(conversation, " ", ((Conversation) conversation).getNextEncryption()); + final var envelope = ((Conversation) conversation).findMessageWithUuidOrRemoteId(reaction.envelopeId); + if (envelope != null) { + ((Conversation) conversation).remove(envelope); + message.addPayload(envelope.getReply()); + message.getOrMakeHtml(); + message.putEdited(reaction.envelopeId, envelope.getServerMsgId()); + } else { + message.putEdited(reaction.envelopeId, null); + } + + new Thread(() -> activity.xmppConnectionService.sendMessage(message)).start(); + } + private void addReaction(final Message message) { activity.addReaction(message, reactions -> activity.xmppConnectionService.sendReactions(message,reactions)); }