From c49aa31d4c539874d4b993c9a742ab46e36b9ef0 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 22 May 2024 11:17:07 -0500 Subject: [PATCH] Better support for correcting replies/reactions --- .../siacs/conversations/entities/Message.java | 67 ++++++++++++++----- .../persistance/DatabaseBackend.java | 22 ++++++ .../services/XmppConnectionService.java | 4 ++ .../ui/ConversationFragment.java | 17 ++++- 4 files changed, 90 insertions(+), 20 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index a014cd480879a7b22d2b8085d484e7441d05ac7e..ad89812746376f096a467dcf9c31230db8323700 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -406,39 +406,63 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable } public Message reply() { - Message m = new Message(conversation, QuoteHelper.quote(MessageUtils.prepareQuote(this)) + "\n", ENCRYPTION_NONE); + Message m = new Message(conversation, "", ENCRYPTION_NONE); m.setThread(getThread()); - final String replyId = replyId(); - if (replyId == null) return m; - m.addPayload( + m.updateReplyTo(this, null); + return m; + } + + public void clearReplyReact() { + this.payloads.remove(getReactions()); + this.payloads.remove(getReply()); + clearFallbacks("urn:xmpp:reply:0", "urn:xmpp:reactions:0"); + } + + public void updateReplyTo(final Message replyTo, Spanned body) { + clearReplyReact(); + + if (body == null) body = new SpannableStringBuilder(getBody(true)); + setBody(QuoteHelper.quote(MessageUtils.prepareQuote(replyTo)) + "\n"); + + final String replyId = replyTo.replyId(); + if (replyId == null) return; + + addPayload( new Element("reply", "urn:xmpp:reply:0") - .setAttribute("to", getCounterpart()) - .setAttribute("id", replyId()) + .setAttribute("to", replyTo.getCounterpart()) + .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") .setAttribute("start", "0") - .setAttribute("end", "" + m.body.codePointCount(0, m.body.length())); - m.addPayload(fallback); - return m; + .setAttribute("end", "" + this.body.codePointCount(0, this.body.length())); + addPayload(fallback); + + appendBody(body); } public Message react(String emoji) { - Set emojis = new HashSet<>(); - if (conversation instanceof Conversation) emojis = ((Conversation) conversation).findReactionsTo(replyId(), null); + final var m = reply(); + m.updateReaction(this, emoji); + return m; + } + + public void updateReaction(final Message reactTo, String emoji) { + Set emojis = new HashSet<>(); + if (conversation instanceof Conversation) emojis = ((Conversation) conversation).findReactionsTo(reactTo.replyId(), null); + emojis.remove(getBody(true)); emojis.add(emoji); - final Message m = reply(); - m.appendBody(emoji); + + updateReplyTo(reactTo, new SpannableStringBuilder(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); + 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; + addPayload(reactions); } public void setReactions(Element reactions) { @@ -553,9 +577,16 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable } public String getBody() { + return getBody(false); + } + + public String getBody(final boolean removeQuoteFallbacks) { if (body == null) return ""; - Pair result = bodyMinusFallbacks("http://jabber.org/protocol/address", Namespace.OOB); + Pair result = + removeQuoteFallbacks + ? bodyMinusFallbacks("http://jabber.org/protocol/address", Namespace.OOB, "urn:xmpp:reply:0") + : bodyMinusFallbacks("http://jabber.org/protocol/address", Namespace.OOB); StringBuilder body = result.first; final String aesgcm = MessageUtils.aesgcmDownloadable(body.toString()); @@ -579,7 +610,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable if (html != null) return html; html = new Element("html", "http://jabber.org/protocol/xhtml-im"); Element body = html.addChild("body", "http://www.w3.org/1999/xhtml"); - SpannedToXHTML.append(body, new SpannableStringBuilder(getBody())); + SpannedToXHTML.append(body, new SpannableStringBuilder(getBody(true))); addPayload(html); return body; } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index ad8b8961171b5c7021908ce7aeada091006d282c..85995c4bf05b54abf9134e9447c00990d455ad66 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -1097,6 +1097,28 @@ public class DatabaseBackend extends SQLiteOpenHelper { return getMessages(conversations, limit, -1); } + public Message getMessageFuzzyId(Conversation conversation, String id) { + ArrayList list = new ArrayList<>(); + SQLiteDatabase db = this.getReadableDatabase(); + Cursor cursor; + cursor = db.rawQuery( + "SELECT * FROM " + Message.TABLENAME + " " + + "LEFT JOIN cheogram." + Message.TABLENAME + + " USING (" + Message.UUID + ")" + + "WHERE " + Message.UUID + "=? OR " + Message.SERVER_MSG_ID + " =? OR " + Message.REMOTE_MSG_ID + " =?", + new String[]{id,id,id} + ); + while (cursor.moveToNext()) { + try { + return Message.fromCursor(cursor, conversation); + } catch (Exception e) { + Log.e(Config.LOGTAG, "unable to restore message"); + } + } + cursor.close(); + return null; + } + public ArrayList getMessages(Conversation conversation, int limit, long timestamp) { ArrayList list = new ArrayList<>(); SQLiteDatabase db = this.getReadableDatabase(); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 5dc0e72a59dc48d8734b848a87c1146cc2efeac8..f9c3ac72ea2a41c0ab0214f45d191946bd05e110 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -671,6 +671,10 @@ public class XmppConnectionService extends Service { return this.databaseBackend.getMessage(conversation, uuid); } + public Message getMessageFuzzyId(Conversation conversation, String id) { + return this.databaseBackend.getMessageFuzzyId(conversation, id); + } + public void insertWebxdcUpdate(final WebxdcUpdate update) { this.databaseBackend.insertWebxdcUpdate(update); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index bbef816e3f53e2db89f7c8901468cfd1e5da27f9..02bb00704602b74573500edc24a66e9c8d4ee18f 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -1000,9 +1000,18 @@ public class ConversationFragment extends XmppFragment Message.configurePrivateMessage(message); } else { message = conversation.getCorrectingMessage(); - message.setBody(hasSubject && body.length() == 0 ? null : body); if (hasSubject) message.setSubject(binding.textinputSubject.getText().toString()); message.setThread(conversation.getThread()); + if (conversation.getReplyTo() != null) { + if (Emoticons.isEmoji(body.toString().replaceAll("\\s", ""))) { + message.updateReaction(conversation.getReplyTo(), body.toString().replaceAll("\\s", "")); + } else { + message.updateReplyTo(conversation.getReplyTo(), body); + } + } else { + message.clearReplyReact(); + message.setBody(hasSubject && body.length() == 0 ? null : body); + } if (message.getStatus() == Message.STATUS_WAITING) { if (sendAt != null) message.setTime(sendAt); activity.xmppConnectionService.updateMessage(message); @@ -2933,11 +2942,15 @@ public class ConversationFragment extends XmppFragment final Editable editable = binding.textinput.getText(); this.conversation.setDraftMessage(editable.toString()); this.binding.textinput.setText(""); - this.binding.textinput.append(message.getBody()); + this.binding.textinput.append(message.getBody(true)); if (message.getSubject() != null && message.getSubject().length() > 0) { this.binding.textinputSubject.setText(message.getSubject()); this.binding.textinputSubject.setVisibility(View.VISIBLE); } + final var reply = message.getReply(); + if (reply != null) { + setupReply(activity.xmppConnectionService.getMessageFuzzyId(conversation, reply.getAttribute("id"))); + } } private void highlightInConference(String nick) {