store reactions in database
This commit is contained in:
parent
a69b4b14a5
commit
6c24cb12dd
|
@ -70,6 +70,7 @@ 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";
|
||||
|
|
|
@ -38,11 +38,11 @@ import im.conversations.android.database.entity.DiscoIdentityEntity;
|
|||
import im.conversations.android.database.entity.DiscoItemEntity;
|
||||
import im.conversations.android.database.entity.MessageContentEntity;
|
||||
import im.conversations.android.database.entity.MessageEntity;
|
||||
import im.conversations.android.database.entity.MessageReactionEntity;
|
||||
import im.conversations.android.database.entity.MessageStateEntity;
|
||||
import im.conversations.android.database.entity.MessageVersionEntity;
|
||||
import im.conversations.android.database.entity.NickEntity;
|
||||
import im.conversations.android.database.entity.PresenceEntity;
|
||||
import im.conversations.android.database.entity.ReactionEntity;
|
||||
import im.conversations.android.database.entity.RosterItemEntity;
|
||||
import im.conversations.android.database.entity.RosterItemGroupEntity;
|
||||
|
||||
|
@ -74,7 +74,7 @@ import im.conversations.android.database.entity.RosterItemGroupEntity;
|
|||
MessageVersionEntity.class,
|
||||
NickEntity.class,
|
||||
PresenceEntity.class,
|
||||
ReactionEntity.class,
|
||||
MessageReactionEntity.class,
|
||||
RosterItemEntity.class,
|
||||
RosterItemGroupEntity.class
|
||||
},
|
||||
|
|
|
@ -6,10 +6,12 @@ import androidx.room.Insert;
|
|||
import androidx.room.Query;
|
||||
import androidx.room.Transaction;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
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 im.conversations.android.database.entity.MessageStateEntity;
|
||||
import im.conversations.android.database.entity.MessageVersionEntity;
|
||||
import im.conversations.android.database.model.Account;
|
||||
|
@ -19,6 +21,8 @@ import im.conversations.android.database.model.MessageIdentifier;
|
|||
import im.conversations.android.database.model.MessageState;
|
||||
import im.conversations.android.database.model.Modification;
|
||||
import im.conversations.android.transformer.Transformation;
|
||||
import im.conversations.android.xmpp.model.reactions.Reactions;
|
||||
import im.conversations.android.xmpp.model.stanza.Message;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -105,12 +109,12 @@ public abstract class MessageDao {
|
|||
// when found by stanzaId the stanzaId must either by verified or belonging to a stub
|
||||
// when found by messageId the from must either match (for corrections) or not be set (null) and
|
||||
// we only look up stubs
|
||||
// TODO the from matcher should be in the outer condition
|
||||
@Query(
|
||||
"SELECT id,stanzaId,messageId,fromBare,latestVersion as version FROM message WHERE"
|
||||
+ " chatId=:chatId AND (fromBare=:fromBare OR fromBare=NULL) AND ((stanzaId !="
|
||||
+ " NULL AND stanzaId=:stanzaId AND (stanzaIdVerified=1 OR latestVersion=NULL)) OR"
|
||||
+ " (stanzaId = NULL AND messageId=:messageId AND latestVersion = NULL))")
|
||||
+ " chatId=:chatId AND (fromBare=:fromBare OR fromBare IS NULL) AND ((stanzaId IS"
|
||||
+ " NOT NULL AND stanzaId=:stanzaId AND (stanzaIdVerified=1 OR latestVersion IS"
|
||||
+ " NULL)) OR (stanzaId IS NULL AND messageId=:messageId AND latestVersion IS"
|
||||
+ " NULL))")
|
||||
abstract MessageIdentifier get(long chatId, Jid fromBare, String stanzaId, String messageId);
|
||||
|
||||
public MessageIdentifier getOrCreateVersion(
|
||||
|
@ -200,14 +204,39 @@ public abstract class MessageDao {
|
|||
protected abstract void setLatestMessageId(
|
||||
final long messageEntityId, final long messageVersionId);
|
||||
|
||||
public Long getOrCreateStub(final Transformation transformation) {
|
||||
// TODO look up where parentId matches messageId (or stanzaId for group chats)
|
||||
|
||||
// when creating stub either set from (correction) or don’t (other attachment)
|
||||
|
||||
return null;
|
||||
public MessageIdentifier getOrCreateStub(
|
||||
final ChatIdentifier chat, final Message.Type messageType, final String parentId) {
|
||||
final MessageIdentifier existing;
|
||||
if (messageType == Message.Type.GROUPCHAT) {
|
||||
existing = getByStanzaId(chat.id, parentId);
|
||||
} else {
|
||||
existing = getByMessageId(chat.id, parentId);
|
||||
}
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
final MessageEntity messageEntity;
|
||||
if (messageType == Message.Type.GROUPCHAT) {
|
||||
LOGGER.info("Create stub for stanza id {}", parentId);
|
||||
messageEntity = MessageEntity.stubOfStanzaId(chat.id, parentId);
|
||||
} else {
|
||||
LOGGER.info("Create stub for message id {}", parentId);
|
||||
messageEntity = MessageEntity.stubOfMessageId(chat.id, parentId);
|
||||
}
|
||||
final long messageEntityId = insert(messageEntity);
|
||||
return new MessageIdentifier(messageEntityId, null, null, null, null);
|
||||
}
|
||||
|
||||
@Query(
|
||||
"SELECT id,stanzaId,messageId,fromBare,latestVersion as version FROM message WHERE"
|
||||
+ " chatId=:chatId AND messageId=:messageId")
|
||||
protected abstract MessageIdentifier getByMessageId(final long chatId, final String messageId);
|
||||
|
||||
@Query(
|
||||
"SELECT id,stanzaId,messageId,fromBare,latestVersion as version FROM message WHERE"
|
||||
+ " chatId=:chatId AND stanzaId=:stanzaId")
|
||||
protected abstract MessageIdentifier getByStanzaId(final long chatId, final String stanzaId);
|
||||
|
||||
public void insertMessageContent(Long latestVersion, List<MessageContent> contents) {
|
||||
Preconditions.checkNotNull(
|
||||
latestVersion, "Contents can only be inserted for a specific version");
|
||||
|
@ -245,4 +274,19 @@ public abstract class MessageDao {
|
|||
|
||||
@Insert
|
||||
protected abstract void insert(MessageStateEntity messageStateEntity);
|
||||
|
||||
@Insert
|
||||
protected abstract void insertReactions(Collection<MessageReactionEntity> reactionEntities);
|
||||
|
||||
public void insertReactions(
|
||||
ChatIdentifier chat, Reactions reactions, Transformation transformation) {
|
||||
final Message.Type messageType = transformation.type;
|
||||
final MessageIdentifier messageIdentifier =
|
||||
getOrCreateStub(chat, messageType, reactions.getId());
|
||||
// TODO delete old reactions
|
||||
insertReactions(
|
||||
Collections2.transform(
|
||||
reactions.getReactions(),
|
||||
r -> MessageReactionEntity.of(messageIdentifier.id, r, transformation)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ public abstract class RosterDao {
|
|||
}
|
||||
final RosterItemEntity entity = RosterItemEntity.of(account.id, item);
|
||||
final long id = insert(entity);
|
||||
// TODO groups
|
||||
}
|
||||
setRosterVersion(account.id, version);
|
||||
}
|
||||
|
|
|
@ -81,4 +81,18 @@ public class MessageEntity {
|
|||
entity.stanzaIdVerified = false;
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static MessageEntity stubOfStanzaId(final long chatId, String stanzaId) {
|
||||
final var entity = new MessageEntity();
|
||||
entity.stanzaIdVerified = false;
|
||||
entity.stanzaId = stanzaId;
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static MessageEntity stubOfMessageId(final long chatId, String messageId) {
|
||||
final var entity = new MessageEntity();
|
||||
entity.stanzaIdVerified = false;
|
||||
entity.messageId = messageId;
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ import androidx.room.Entity;
|
|||
import androidx.room.ForeignKey;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import im.conversations.android.transformer.Transformation;
|
||||
import java.time.Instant;
|
||||
|
||||
@Entity(
|
||||
|
@ -16,7 +18,7 @@ import java.time.Instant;
|
|||
childColumns = {"messageEntityId"},
|
||||
onDelete = ForeignKey.CASCADE),
|
||||
indices = {@Index(value = "messageEntityId")})
|
||||
public class ReactionEntity {
|
||||
public class MessageReactionEntity {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public Long id;
|
||||
|
@ -25,11 +27,25 @@ public class ReactionEntity {
|
|||
|
||||
public String stanzaId;
|
||||
public String messageId;
|
||||
public String reactionBy;
|
||||
public Jid reactionBy;
|
||||
public String reactionByResource;
|
||||
public String occupantId;
|
||||
|
||||
public Instant receivedAt;
|
||||
|
||||
public String reaction;
|
||||
|
||||
public static MessageReactionEntity of(
|
||||
long messageEntityId, final String reaction, final Transformation transformation) {
|
||||
final var entity = new MessageReactionEntity();
|
||||
entity.messageEntityId = messageEntityId;
|
||||
entity.reaction = reaction;
|
||||
entity.stanzaId = transformation.stanzaId;
|
||||
entity.messageId = transformation.messageId;
|
||||
entity.reactionBy = transformation.fromBare();
|
||||
entity.reactionByResource = transformation.fromResource();
|
||||
entity.occupantId = transformation.occupantId;
|
||||
entity.receivedAt = transformation.receivedAt;
|
||||
return entity;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ import im.conversations.android.xmpp.model.jabber.Thread;
|
|||
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.stanza.Message;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
|
@ -34,7 +35,8 @@ public class Transformation {
|
|||
DeliveryReceipt.class,
|
||||
MultiUserChat.class,
|
||||
Displayed.class,
|
||||
Replace.class);
|
||||
Replace.class,
|
||||
Reactions.class);
|
||||
|
||||
public final Instant receivedAt;
|
||||
public final Jid to;
|
||||
|
|
|
@ -18,6 +18,7 @@ import im.conversations.android.xmpp.model.jabber.Body;
|
|||
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.stanza.Message;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -70,11 +71,16 @@ public class Transformer {
|
|||
return false;
|
||||
}
|
||||
final Replace messageCorrection = transformation.getExtension(Replace.class);
|
||||
final Reactions reactions = transformation.getExtension(Reactions.class);
|
||||
final List<MessageContent> contents = parseContent(transformation);
|
||||
|
||||
final boolean identifiableSender =
|
||||
Arrays.asList(Message.Type.NORMAL, Message.Type.CHAT).contains(messageType)
|
||||
|| Objects.nonNull(transformation.occupantId);
|
||||
final boolean isReaction =
|
||||
Objects.nonNull(reactions)
|
||||
&& Objects.nonNull(reactions.getId())
|
||||
&& identifiableSender;
|
||||
final boolean isMessageCorrection =
|
||||
Objects.nonNull(messageCorrection)
|
||||
&& messageCorrection.getId() != null
|
||||
|
@ -83,7 +89,9 @@ public class Transformer {
|
|||
if (contents.isEmpty()) {
|
||||
LOGGER.info("Received message from {} w/o contents", transformation.from);
|
||||
transformMessageState(chat, transformation);
|
||||
// TODO apply reactions
|
||||
if (isReaction) {
|
||||
database.messageDao().insertReactions(chat, reactions, transformation);
|
||||
}
|
||||
} else {
|
||||
final MessageIdentifier messageIdentifier;
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package im.conversations.android.xmpp.model.reactions;
|
||||
|
||||
import im.conversations.android.annotation.XmlElement;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
|
||||
@XmlElement
|
||||
public class Reaction extends Extension {
|
||||
|
||||
public Reaction() {
|
||||
super(Reaction.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package im.conversations.android.xmpp.model.reactions;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Collections2;
|
||||
import im.conversations.android.annotation.XmlElement;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
@XmlElement
|
||||
public class Reactions extends Extension {
|
||||
|
||||
public Reactions() {
|
||||
super(Reactions.class);
|
||||
}
|
||||
|
||||
public Collection<String> getReactions() {
|
||||
return Collections2.filter(
|
||||
Collections2.transform(getExtensions(Reaction.class), Reaction::getContent),
|
||||
r -> Objects.nonNull(Strings.nullToEmpty(r)));
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.getAttribute("id");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@XmlPackage(namespace = Namespace.REACTIONS)
|
||||
package im.conversations.android.xmpp.model.reactions;
|
||||
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import im.conversations.android.annotation.XmlPackage;
|
Loading…
Reference in a new issue