@@ -82,6 +82,21 @@ public class Reaction {
}
}
+ public static Collection<Reaction> append(
+ final Collection<Reaction> existing,
+ final Collection<String> reactions,
+ final boolean received,
+ final Jid from,
+ final Jid trueJid,
+ final String occupantId) {
+ final ImmutableSet.Builder<Reaction> builder = new ImmutableSet.Builder<>();
+ builder.addAll(existing);
+ builder.addAll(
+ Collections2.transform(
+ reactions, r -> new Reaction(r, received, from, trueJid, occupantId)));
+ return builder.build();
+ }
+
public static Collection<Reaction> withOccupantId(
final Collection<Reaction> existing,
final Collection<String> reactions,
@@ -89,7 +104,7 @@ public class Reaction {
final Jid from,
final Jid trueJid,
final String occupantId) {
- final ImmutableList.Builder<Reaction> builder = new ImmutableList.Builder<>();
+ final ImmutableSet.Builder<Reaction> builder = new ImmutableSet.Builder<>();
builder.addAll(Collections2.filter(existing, e -> !occupantId.equals(e.occupantId)));
builder.addAll(
Collections2.transform(
@@ -109,12 +124,22 @@ public class Reaction {
.toString();
}
+ public int hashCode() {
+ return toString().hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (o == null) return false;
+ if (!(o instanceof Reaction)) return false;
+ return toString().equals(o.toString());
+ }
+
public static Collection<Reaction> withFrom(
final Collection<Reaction> existing,
final Collection<String> reactions,
final boolean received,
final Jid from) {
- final ImmutableList.Builder<Reaction> builder = new ImmutableList.Builder<>();
+ final ImmutableSet.Builder<Reaction> builder = new ImmutableSet.Builder<>();
builder.addAll(
Collections2.filter(existing, e -> !from.asBareJid().equals(e.from.asBareJid())));
builder.addAll(
@@ -51,6 +51,7 @@ import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
+import eu.siacs.conversations.utils.Emoticons;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.LocalizedContent;
import eu.siacs.conversations.xml.Namespace;
@@ -466,7 +467,6 @@ public class MessageParser extends AbstractParser implements Consumer<im.convers
if (timestamp == null) {
timestamp = AbstractParser.parseTimestamp(original, AbstractParser.parseTimestamp(packet));
}
- final Reactions reactions = packet.getExtension(Reactions.class);
final Element mucUserElement = packet.findChild("x", Namespace.MUC_USER);
final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
Element replaceElement = packet.findChild("replace", "urn:xmpp:message-correct:0");
@@ -502,6 +502,33 @@ public class MessageParser extends AbstractParser implements Consumer<im.convers
}
LocalizedContent body = packet.getBody();
+ var appendReactions = false;
+ var reactions = packet.getExtension(Reactions.class);
+ final var reply = packet.findChild("reply", "urn:xmpp:reply:0");
+ if (reactions == null && reply != null && reply.getAttribute("id") != null && body != null) {
+ StringBuilder bodyB = new StringBuilder(body.content);
+
+ for (Element el : packet.getChildren()) {
+ if ("fallback".equals(el.getName()) && "urn:xmpp:fallback:0".equals(el.getNamespace()) && "urn:xmpp:reply:0".equals(el.getAttribute("for"))) {
+ for (final var span : el.getChildren()) {
+ if (!span.getName().equals("body") && !span.getNamespace().equals("urn:xmpp:fallback:0")) continue;
+ if (span.getAttribute("start") == null || span.getAttribute("end") == null) {
+ bodyB.setLength(0);
+ } else {
+ bodyB.delete(bodyB.offsetByCodePoints(0, parseInt(span.getAttribute("start"))), bodyB.offsetByCodePoints(0, parseInt(span.getAttribute("end"))));
+ }
+ }
+ }
+ }
+
+ final var emojiMaybe = bodyB.toString().replaceAll("\\s", "");
+ if (Emoticons.isEmoji(emojiMaybe)) {
+ appendReactions = true;
+ reactions = im.conversations.android.xmpp.model.reactions.Reactions.to(reply.getAttribute("id"));
+ reactions.addExtension(new im.conversations.android.xmpp.model.reactions.Reaction(emojiMaybe));
+ }
+ }
+
final Element axolotlEncrypted = packet.findChildEnsureSingle(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
int status;
final Jid counterpart;
@@ -609,7 +636,7 @@ public class MessageParser extends AbstractParser implements Consumer<im.convers
}
}
- if ((body != null || pgpEncrypted != null || (axolotlEncrypted != null && axolotlEncrypted.hasChild("payload")) || !attachments.isEmpty() || html != null || (packet.hasChild("subject") && packet.hasChild("thread"))) && !isMucStatusMessage) {
+ if (reactions == null && (body != null || pgpEncrypted != null || (axolotlEncrypted != null && axolotlEncrypted.hasChild("payload")) || !attachments.isEmpty() || html != null || (packet.hasChild("subject") && packet.hasChild("thread"))) && !isMucStatusMessage) {
final Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), conversationIsProbablyMuc, false, query, false);
final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI;
@@ -1299,6 +1326,7 @@ public class MessageParser extends AbstractParser implements Consumer<im.convers
final boolean isReceived = !mucOptions.isSelf(counterpart);
if (occupantId != null && message != null) {
final var combinedReactions =
+ appendReactions ? Reaction.append(message.getReactions(), reactions.getReactions(), isReceived, counterpart, null, occupantId) :
Reaction.withOccupantId(
message.getReactions(),
reactions.getReactions(),
@@ -1325,6 +1353,7 @@ public class MessageParser extends AbstractParser implements Consumer<im.convers
packet.fromAccount(account);
if (message != null) {
final var combinedReactions =
+ appendReactions ? Reaction.append(message.getReactions(), reactions.getReactions(), isReceived, reactionFrom, null, null) :
Reaction.withFrom(
message.getReactions(),
reactions.getReactions(),
@@ -1469,4 +1498,12 @@ public class MessageParser extends AbstractParser implements Consumer<im.convers
return true;
}
}
+
+ private static int parseInt(String value) {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
+ }
}