store reference to inReplyTo in database
This commit is contained in:
parent
56a462833e
commit
405445afbe
|
@ -2,7 +2,7 @@
|
|||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "b6a7be8218829fd38f51dcd76cb9cccd",
|
||||
"identityHash": "219a451e9a1889222b7549c8b3c0a5b3",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "account",
|
||||
|
@ -1420,7 +1420,7 @@
|
|||
},
|
||||
{
|
||||
"tableName": "message",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `chatId` INTEGER NOT NULL, `receivedAt` INTEGER, `sentAt` INTEGER, `outgoing` INTEGER NOT NULL, `toBare` TEXT, `toResource` TEXT, `fromBare` TEXT, `fromResource` TEXT, `occupantId` TEXT, `messageId` TEXT, `stanzaId` TEXT, `stanzaIdVerified` INTEGER NOT NULL, `latestVersion` INTEGER, `acknowledged` INTEGER NOT NULL, FOREIGN KEY(`chatId`) REFERENCES `chat`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`latestVersion`) REFERENCES `message_version`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `chatId` INTEGER NOT NULL, `receivedAt` INTEGER, `sentAt` INTEGER, `outgoing` INTEGER NOT NULL, `toBare` TEXT, `toResource` TEXT, `fromBare` TEXT, `fromResource` TEXT, `occupantId` TEXT, `messageId` TEXT, `stanzaId` TEXT, `stanzaIdVerified` INTEGER NOT NULL, `latestVersion` INTEGER, `acknowledged` INTEGER NOT NULL, `inReplyToMessageId` TEXT, `inReplyToStanzaId` TEXT, `inReplyToMessageEntityId` INTEGER, FOREIGN KEY(`chatId`) REFERENCES `chat`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`latestVersion`) REFERENCES `message_version`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`inReplyToMessageEntityId`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
|
@ -1511,6 +1511,24 @@
|
|||
"columnName": "acknowledged",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "inReplyToMessageId",
|
||||
"columnName": "inReplyToMessageId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "inReplyToStanzaId",
|
||||
"columnName": "inReplyToStanzaId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "inReplyToMessageEntityId",
|
||||
"columnName": "inReplyToMessageEntityId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
|
@ -1537,6 +1555,15 @@
|
|||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_message_latestVersion` ON `${TABLE_NAME}` (`latestVersion`)"
|
||||
},
|
||||
{
|
||||
"name": "index_message_inReplyToMessageEntityId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"inReplyToMessageEntityId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_message_inReplyToMessageEntityId` ON `${TABLE_NAME}` (`inReplyToMessageEntityId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
|
@ -1561,6 +1588,17 @@
|
|||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "message",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"inReplyToMessageEntityId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2228,7 +2266,7 @@
|
|||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b6a7be8218829fd38f51dcd76cb9cccd')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '219a451e9a1889222b7549c8b3c0a5b3')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import eu.siacs.conversations.xmpp.Jid;
|
|||
import im.conversations.android.IDs;
|
||||
import im.conversations.android.database.ConversationsDatabase;
|
||||
import im.conversations.android.database.entity.AccountEntity;
|
||||
import im.conversations.android.database.model.EmbeddedMessage;
|
||||
import im.conversations.android.database.model.Modification;
|
||||
import im.conversations.android.transformer.Transformation;
|
||||
import im.conversations.android.transformer.Transformer;
|
||||
|
@ -16,6 +17,7 @@ import im.conversations.android.xmpp.model.correction.Replace;
|
|||
import im.conversations.android.xmpp.model.jabber.Body;
|
||||
import im.conversations.android.xmpp.model.reactions.Reaction;
|
||||
import im.conversations.android.xmpp.model.reactions.Reactions;
|
||||
import im.conversations.android.xmpp.model.reply.Reply;
|
||||
import im.conversations.android.xmpp.model.stanza.Message;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
@ -371,4 +373,37 @@ public class TransformationTest {
|
|||
Assert.assertEquals(
|
||||
"Please give me a thumbs up", Iterables.getOnlyElement(dbMessage.contents).body);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void inReplyTo() {
|
||||
final var m1 = new Message();
|
||||
m1.setId("1");
|
||||
m1.setTo(ACCOUNT);
|
||||
m1.setFrom(REMOTE.withResource("junit"));
|
||||
m1.addExtension(new Body("Hi. How are you?"));
|
||||
|
||||
this.transformer.transform(Transformation.of(m1, Instant.now(), REMOTE, "stanza-a", null));
|
||||
|
||||
final var m2 = new Message();
|
||||
m2.setId("2");
|
||||
m2.setTo(REMOTE);
|
||||
m2.setFrom(ACCOUNT);
|
||||
m2.addExtension(new Body("I am fine."));
|
||||
final var reply = m2.addExtension(new Reply());
|
||||
reply.setId("1");
|
||||
reply.setTo(REMOTE);
|
||||
|
||||
this.transformer.transform(Transformation.of(m2, Instant.now(), REMOTE, "stanza-b", null));
|
||||
|
||||
final var messages = database.messageDao().getMessages(1L);
|
||||
Assert.assertEquals(2, messages.size());
|
||||
final var response = Iterables.get(messages, 1);
|
||||
Assert.assertNotNull(response.inReplyToMessageEntityId);
|
||||
final EmbeddedMessage embeddedMessage = response.inReplyTo;
|
||||
Assert.assertNotNull(embeddedMessage);
|
||||
Assert.assertEquals(REMOTE, embeddedMessage.fromBare);
|
||||
Assert.assertEquals(1L, embeddedMessage.contents.size());
|
||||
Assert.assertEquals(
|
||||
"Hi. How are you?", Iterables.getOnlyElement(embeddedMessage.contents).body);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,6 @@ public final class Namespace {
|
|||
public static final String MUC = "http://jabber.org/protocol/muc";
|
||||
public static final String MUC_USER = MUC + "#user";
|
||||
public static final String NICK = "http://jabber.org/protocol/nick";
|
||||
public static final String REACTIONS = "urn:xmpp:reactions:0";
|
||||
public static final String OCCUPANT_ID = "urn:xmpp:occupant-id:0";
|
||||
public static final String OMEMO_DTLS_SRTP_VERIFICATION =
|
||||
"http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification";
|
||||
|
@ -88,8 +87,10 @@ public final class Namespace {
|
|||
public static final String PUB_SUB_PERSISTENT_ITEMS = PUB_SUB + "#persistent-items";
|
||||
public static final String PUB_SUB_PUBLISH_OPTIONS = PUB_SUB + "#publish-options";
|
||||
public static final String PUSH = "urn:xmpp:push:0";
|
||||
public static final String REACTIONS = "urn:xmpp:reactions:0";
|
||||
public static final String REGISTER = "jabber:iq:register";
|
||||
public static final String REGISTER_STREAM_FEATURE = "http://jabber.org/features/iq-register";
|
||||
public static final String REPLY = "urn:xmpp:reply:0";
|
||||
public static final String ROSTER = "jabber:iq:roster";
|
||||
public static final String SASL = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||
public static final String SASL_2 = "urn:xmpp:sasl:2";
|
||||
|
|
|
@ -135,7 +135,7 @@ public class InvalidJid implements Jid {
|
|||
}
|
||||
|
||||
public static boolean isValid(Jid jid) {
|
||||
return !(jid != null && jid instanceof InvalidJid);
|
||||
return !(jid instanceof InvalidJid);
|
||||
}
|
||||
|
||||
public static boolean invalid(final Jid jid) {
|
||||
|
|
|
@ -399,8 +399,45 @@ public abstract class MessageDao {
|
|||
@Query(
|
||||
"SELECT message.id as"
|
||||
+ " id,sentAt,outgoing,toBare,toResource,fromBare,fromResource,modification,latestVersion"
|
||||
+ " as version FROM message JOIN message_version ON"
|
||||
+ " as version,inReplyToMessageEntityId FROM message JOIN message_version ON"
|
||||
+ " message.latestVersion=message_version.id WHERE message.chatId=:chatId AND"
|
||||
+ " latestVersion IS NOT NULL ORDER BY message.receivedAt")
|
||||
public abstract List<MessageWithContentReactions> getMessages(long chatId);
|
||||
|
||||
public void setInReplyTo(
|
||||
ChatIdentifier chat,
|
||||
MessageIdentifier messageIdentifier,
|
||||
Message.Type messageType,
|
||||
final Jid to,
|
||||
String inReplyTo) {
|
||||
if (messageType == Message.Type.GROUPCHAT) {
|
||||
final Long messageEntityId = getMessageByStanzaId(chat.id, inReplyTo);
|
||||
setInReplyToStanzaId(messageIdentifier.id, inReplyTo, messageEntityId);
|
||||
} else {
|
||||
final Long messageEntityId = getMessageByMessageId(chat.id, to.asBareJid(), inReplyTo);
|
||||
setInReplyToMessageId(messageIdentifier.id, inReplyTo, messageEntityId);
|
||||
}
|
||||
}
|
||||
|
||||
@Query(
|
||||
"UPDATE message SET"
|
||||
+ " inReplyToMessageId=null,inReplyToStanzaId=:stanzaId,inReplyToMessageEntityId=:inReplyToMessageEntityId"
|
||||
+ " WHERE id=:id")
|
||||
protected abstract void setInReplyToStanzaId(
|
||||
final long id, String stanzaId, long inReplyToMessageEntityId);
|
||||
|
||||
@Query(
|
||||
"UPDATE message SET"
|
||||
+ " inReplyToMessageId=:messageId,inReplyToStanzaId=null,inReplyToMessageEntityId=:inReplyToMessageEntityId"
|
||||
+ " WHERE id=:id")
|
||||
protected abstract void setInReplyToMessageId(
|
||||
final long id, String messageId, long inReplyToMessageEntityId);
|
||||
|
||||
@Query(
|
||||
"SELECT id FROM message WHERE chatId=:chatId AND fromBare=:fromBare AND"
|
||||
+ " messageId=:messageId")
|
||||
protected abstract Long getMessageByMessageId(long chatId, Jid fromBare, String messageId);
|
||||
|
||||
@Query("SELECT id FROM message WHERE chatId=:chatId AND stanzaId=:stanzaId")
|
||||
protected abstract Long getMessageByStanzaId(long chatId, String stanzaId);
|
||||
}
|
||||
|
|
|
@ -24,8 +24,17 @@ import java.util.Objects;
|
|||
parentColumns = {"id"},
|
||||
childColumns = {"latestVersion"},
|
||||
onDelete = ForeignKey.CASCADE),
|
||||
@ForeignKey(
|
||||
entity = MessageEntity.class,
|
||||
parentColumns = {"id"},
|
||||
childColumns = {"inReplyToMessageEntityId"},
|
||||
onDelete = ForeignKey.SET_NULL),
|
||||
},
|
||||
indices = {@Index(value = "chatId"), @Index(value = "latestVersion")})
|
||||
indices = {
|
||||
@Index(value = "chatId"),
|
||||
@Index(value = "latestVersion"),
|
||||
@Index("inReplyToMessageEntityId")
|
||||
})
|
||||
public class MessageEntity {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
|
@ -55,6 +64,10 @@ public class MessageEntity {
|
|||
|
||||
public boolean acknowledged = false;
|
||||
|
||||
public String inReplyToMessageId;
|
||||
public String inReplyToStanzaId;
|
||||
@Nullable public Long inReplyToMessageEntityId;
|
||||
|
||||
public static MessageEntity of(final long chatId, final Transformation transformation) {
|
||||
final var entity = new MessageEntity();
|
||||
entity.chatId = chatId;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package im.conversations.android.database.model;
|
||||
|
||||
import androidx.room.Relation;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import im.conversations.android.database.entity.MessageContentEntity;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public class EmbeddedMessage {
|
||||
|
||||
public long id;
|
||||
public Jid fromBare;
|
||||
public String fromResource;
|
||||
public Instant sentAt;
|
||||
|
||||
public Long latestVersion;
|
||||
|
||||
@Relation(
|
||||
entity = MessageContentEntity.class,
|
||||
parentColumn = "latestVersion",
|
||||
entityColumn = "messageVersionId")
|
||||
public List<MessageContent> contents;
|
||||
}
|
|
@ -6,6 +6,7 @@ import com.google.common.collect.Maps;
|
|||
import com.google.common.collect.Multimaps;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import im.conversations.android.database.entity.MessageContentEntity;
|
||||
import im.conversations.android.database.entity.MessageEntity;
|
||||
import im.conversations.android.database.entity.MessageReactionEntity;
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
|
@ -28,6 +29,13 @@ public class MessageWithContentReactions {
|
|||
|
||||
public Modification modification;
|
||||
public long version;
|
||||
public Long inReplyToMessageEntityId;
|
||||
|
||||
@Relation(
|
||||
entity = MessageEntity.class,
|
||||
parentColumn = "inReplyToMessageEntityId",
|
||||
entityColumn = "id")
|
||||
public EmbeddedMessage inReplyTo;
|
||||
|
||||
@Relation(
|
||||
entity = MessageContentEntity.class,
|
||||
|
|
|
@ -17,6 +17,7 @@ import im.conversations.android.xmpp.model.markers.Displayed;
|
|||
import im.conversations.android.xmpp.model.muc.user.MultiUserChat;
|
||||
import im.conversations.android.xmpp.model.oob.OutOfBandData;
|
||||
import im.conversations.android.xmpp.model.reactions.Reactions;
|
||||
import im.conversations.android.xmpp.model.reply.Reply;
|
||||
import im.conversations.android.xmpp.model.stanza.Message;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
|
@ -36,7 +37,8 @@ public class Transformation {
|
|||
MultiUserChat.class,
|
||||
Displayed.class,
|
||||
Replace.class,
|
||||
Reactions.class);
|
||||
Reactions.class,
|
||||
Reply.class);
|
||||
|
||||
public final Instant receivedAt;
|
||||
public final Jid to;
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.google.common.base.Preconditions;
|
|||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import eu.siacs.conversations.xmpp.InvalidJid;
|
||||
import im.conversations.android.database.ConversationsDatabase;
|
||||
import im.conversations.android.database.model.Account;
|
||||
import im.conversations.android.database.model.ChatIdentifier;
|
||||
|
@ -19,6 +20,7 @@ import im.conversations.android.xmpp.model.markers.Displayed;
|
|||
import im.conversations.android.xmpp.model.muc.user.MultiUserChat;
|
||||
import im.conversations.android.xmpp.model.oob.OutOfBandData;
|
||||
import im.conversations.android.xmpp.model.reactions.Reactions;
|
||||
import im.conversations.android.xmpp.model.reply.Reply;
|
||||
import im.conversations.android.xmpp.model.stanza.Message;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -113,6 +115,14 @@ public class Transformer {
|
|||
return false;
|
||||
}
|
||||
database.messageDao().insertMessageContent(messageIdentifier.version, contents);
|
||||
final var reply = transformation.getExtension(Reply.class);
|
||||
if (Objects.nonNull(reply)
|
||||
&& Objects.nonNull(reply.getId())
|
||||
&& InvalidJid.isValid(reply.getTo())) {
|
||||
database.messageDao()
|
||||
.setInReplyTo(
|
||||
chat, messageIdentifier, messageType, reply.getTo(), reply.getId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package im.conversations.android.xmpp.model.reply;
|
||||
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import im.conversations.android.annotation.XmlElement;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
|
||||
@XmlElement(namespace = Namespace.REPLY)
|
||||
public class Reply extends Extension {
|
||||
|
||||
public Reply() {
|
||||
super(Reply.class);
|
||||
}
|
||||
|
||||
public Jid getTo() {
|
||||
return this.getAttributeAsJid("to");
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.getAttribute("id");
|
||||
}
|
||||
|
||||
public void setTo(final Jid to) {
|
||||
this.setAttribute("to", to);
|
||||
}
|
||||
|
||||
public void setId(final String id) {
|
||||
this.setAttribute("id", id);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue