Skip to content

Commit

Permalink
test json deserialization of reaction in rust
Browse files Browse the repository at this point in the history
  • Loading branch information
cameronvoell committed Jan 13, 2025
1 parent b9f90f1 commit d150b91
Show file tree
Hide file tree
Showing 15 changed files with 304 additions and 10 deletions.
115 changes: 115 additions & 0 deletions library/src/androidTest/java/org/xmtp/android/library/ReactionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import org.xmtp.android.library.codecs.Reaction
import org.xmtp.android.library.codecs.ReactionAction
import org.xmtp.android.library.codecs.ReactionCodec
import org.xmtp.android.library.codecs.ReactionSchema
import org.xmtp.android.library.libxmtp.Message
import org.xmtp.android.library.messages.walletAddress
import uniffi.xmtpv3.FfiReaction
import uniffi.xmtpv3.FfiReactionAction
import uniffi.xmtpv3.FfiReactionSchema
import uniffi.xmtpv3.org.xmtp.android.library.codecs.ContentTypeReactionV2
import uniffi.xmtpv3.org.xmtp.android.library.codecs.ReactionV2Codec

@RunWith(AndroidJUnit4::class)
class ReactionTest {
Expand Down Expand Up @@ -98,4 +104,113 @@ class ReactionTest {
assertEquals(ReactionSchema.Unicode, content?.schema)
}
}

@Test
fun testCanUseReactionV2Codec() {
Client.register(codec = ReactionV2Codec())

val fixtures = fixtures()
val aliceClient = fixtures.alixClient
val aliceConversation = runBlocking {
aliceClient.conversations.newConversation(fixtures.bo.walletAddress)
}

runBlocking { aliceConversation.send(text = "hey alice 2 bob") }

val messageToReact = runBlocking { aliceConversation.messages()[0] }

val reaction = FfiReaction(
reference = messageToReact.id,
referenceInboxId = aliceClient.inboxId,
action = FfiReactionAction.ADDED,
content = "U+1F603",
schema = FfiReactionSchema.UNICODE,
)

runBlocking {
aliceConversation.send(
content = reaction,
options = SendOptions(contentType = ContentTypeReactionV2),
)
}
val messages = runBlocking { aliceConversation.messages() }
assertEquals(messages.size, 2)
if (messages.size == 2) {
val content: FfiReaction? = messages.first().content()
assertEquals("U+1F603", content?.content)
assertEquals(messageToReact.id, content?.reference)
assertEquals(FfiReactionAction.ADDED, content?.action)
assertEquals(FfiReactionSchema.UNICODE, content?.schema)
}

val messagesWithReactions: List<Message.MessageWithChildMessages> = runBlocking {
aliceConversation.messagesWithReactions()
}
assertEquals(messagesWithReactions.size, 1)
assertEquals(messagesWithReactions[0].id, messageToReact.id)
val reactionContent: FfiReaction? = messagesWithReactions[0].childMessages[0].content()
assertEquals(reactionContent?.reference, messageToReact.id)
}

@Test
fun testCanMixReactionTypes() = runBlocking {
// Register both codecs
Client.register(codec = ReactionV2Codec())
Client.register(codec = ReactionCodec())

val fixtures = fixtures()
val aliceClient = fixtures.alixClient
val aliceConversation =
aliceClient.conversations.newConversation(fixtures.bo.walletAddress)


// Send initial message
aliceConversation.send(text = "hey alice 2 bob")
val messageToReact = aliceConversation.messages()[0]

// Send V2 reaction
val reactionV2 = FfiReaction(
reference = messageToReact.id,
referenceInboxId = aliceClient.inboxId,
action = FfiReactionAction.ADDED,
content = "U+1F603",
schema = FfiReactionSchema.UNICODE,
)
aliceConversation.send(
content = reactionV2,
options = SendOptions(contentType = ContentTypeReactionV2),
)


// Send V1 reaction
val reactionV1 = Reaction(
reference = messageToReact.id,
action = ReactionAction.Added,
content = "U+1F604", // Different emoji to distinguish
schema = ReactionSchema.Unicode,
)
aliceConversation.send(
content = reactionV1,
options = SendOptions(contentType = ContentTypeReaction),
)


// Verify both reactions appear in messagesWithReactions
val messagesWithReactions =
aliceConversation.messagesWithReactions()

assertEquals(1, messagesWithReactions.size)
assertEquals(messageToReact.id, messagesWithReactions[0].id)
assertEquals(2, messagesWithReactions[0].childMessages.size)

// Verify both reaction contents
val childContents = messagesWithReactions[0].childMessages.mapNotNull {
when (val content = it.content<Any>()) {
is FfiReaction -> content.content
is Reaction -> content.content
else -> null
}
}.toSet()
assertEquals(setOf("U+1F603", "U+1F604"), childContents)
}
}
6 changes: 3 additions & 3 deletions library/src/main/java/libxmtp-version.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Version: 13846991
Branch: cv/01-02-save_parent_id_to_db_and_add_get_group_messages_with_reactions_function
Date: 2025-01-07 17:52:41 +0000
Version: 76983ce7
Branch: cv/01-10-json_deserialization_for_legacy_reactions
Date: 2025-01-11 00:19:35 +0000
13 changes: 13 additions & 0 deletions library/src/main/java/org/xmtp/android/library/Conversation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,19 @@ sealed class Conversation {
}
}

suspend fun messagesWithReactions(
limit: Int? = null,
beforeNs: Long? = null,
afterNs: Long? = null,
direction: Message.SortDirection = Message.SortDirection.DESCENDING,
deliveryStatus: Message.MessageDeliveryStatus = Message.MessageDeliveryStatus.ALL,
): List<Message.MessageWithChildMessages> {
return when (this) {
is Group -> group.messagesWithReactions(limit, beforeNs, afterNs, direction, deliveryStatus)
is Dm -> dm.messagesWithReactions(limit, beforeNs, afterNs, direction, deliveryStatus)
}
}

suspend fun processMessage(messageBytes: ByteArray): Message? {
return when (this) {
is Group -> group.processMessage(messageBytes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ data class Conversations(
groupImageUrlSquare = groupImageUrlSquare,
groupDescription = groupDescription,
groupPinnedFrameUrl = groupPinnedFrameUrl,
customPermissionPolicySet = permissionsPolicySet
customPermissionPolicySet = permissionsPolicySet,
messageExpirationFromMs = null,
messageExpirationMs = null
)
)
return Group(client, group)
Expand Down
36 changes: 36 additions & 0 deletions library/src/main/java/org/xmtp/android/library/Dm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,42 @@ class Dm(val client: Client, private val libXMTPGroup: FfiConversation, private
}
}

suspend fun messagesWithReactions(
limit: Int? = null,
beforeNs: Long? = null,
afterNs: Long? = null,
direction: SortDirection = SortDirection.DESCENDING,
deliveryStatus: MessageDeliveryStatus = MessageDeliveryStatus.ALL,
): List<Message.MessageWithChildMessages> {
val ffiMessageWithReactions = libXMTPGroup.findMessagesWithReactions(
opts = FfiListMessagesOptions(
sentBeforeNs = beforeNs,
sentAfterNs = afterNs,
limit = limit?.toLong(),
deliveryStatus = when (deliveryStatus) {
MessageDeliveryStatus.PUBLISHED -> FfiDeliveryStatus.PUBLISHED
MessageDeliveryStatus.UNPUBLISHED -> FfiDeliveryStatus.UNPUBLISHED
MessageDeliveryStatus.FAILED -> FfiDeliveryStatus.FAILED
else -> null
},
when (direction) {
SortDirection.ASCENDING -> FfiDirection.ASCENDING
else -> FfiDirection.DESCENDING
},
contentTypes = null
)
)

return ffiMessageWithReactions.mapNotNull { ffiMessageWithReactions ->
val parentMessage = Message.create(ffiMessageWithReactions.message)!!
val childMessages = ffiMessageWithReactions.reactions.mapNotNull { childMessage ->
Message.create(childMessage)!!
}

Message.MessageWithChildMessages(parentMessage, childMessages)
}
}

suspend fun processMessage(messageBytes: ByteArray): Message? {
val message = libXMTPGroup.processStreamedConversationMessage(messageBytes)
return Message.create(message)
Expand Down
36 changes: 36 additions & 0 deletions library/src/main/java/org/xmtp/android/library/Group.kt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,42 @@ class Group(
}
}

suspend fun messagesWithReactions(
limit: Int? = null,
beforeNs: Long? = null,
afterNs: Long? = null,
direction: SortDirection = SortDirection.DESCENDING,
deliveryStatus: MessageDeliveryStatus = MessageDeliveryStatus.ALL,
): List<Message.MessageWithChildMessages> {
val ffiMessageWithReactions = libXMTPGroup.findMessagesWithReactions(
opts = FfiListMessagesOptions(
sentBeforeNs = beforeNs,
sentAfterNs = afterNs,
limit = limit?.toLong(),
deliveryStatus = when (deliveryStatus) {
MessageDeliveryStatus.PUBLISHED -> FfiDeliveryStatus.PUBLISHED
MessageDeliveryStatus.UNPUBLISHED -> FfiDeliveryStatus.UNPUBLISHED
MessageDeliveryStatus.FAILED -> FfiDeliveryStatus.FAILED
else -> null
},
when (direction) {
SortDirection.ASCENDING -> FfiDirection.ASCENDING
else -> FfiDirection.DESCENDING
},
contentTypes = null
)
)

return ffiMessageWithReactions.mapNotNull { ffiMessageWithReactions ->
val parentMessage = Message.create(ffiMessageWithReactions.message)!!
val childMessages = ffiMessageWithReactions.reactions.mapNotNull { childMessage ->
Message.create(childMessage)!!
}

Message.MessageWithChildMessages(parentMessage, childMessages)
}
}

suspend fun processMessage(messageBytes: ByteArray): Message? {
val message = libXMTPGroup.processStreamedConversationMessage(messageBytes)
return Message.create(message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ContentTypeIdBuilder {
}

val ContentTypeId.id: String
get() = "$authorityId:$typeId"
get() = "$authorityId:$typeId:$versionMajor.$versionMinor"

val ContentTypeId.description: String
get() = "$authorityId/$typeId:$versionMajor.$versionMinor"
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package uniffi.xmtpv3.org.xmtp.android.library.codecs

import org.xmtp.android.library.codecs.ContentCodec
import org.xmtp.android.library.codecs.ContentTypeId
import org.xmtp.android.library.codecs.ContentTypeIdBuilder
import org.xmtp.android.library.codecs.EncodedContent
import uniffi.xmtpv3.FfiReaction
import uniffi.xmtpv3.FfiReactionAction
import uniffi.xmtpv3.decodeReaction
import uniffi.xmtpv3.encodeReaction

val ContentTypeReactionV2 = ContentTypeIdBuilder.builderFromAuthorityId(
"xmtp.org",
"reaction",
versionMajor = 2,
versionMinor = 0,
)

data class ReactionV2Codec(override var contentType: ContentTypeId = ContentTypeReactionV2) :
ContentCodec<FfiReaction> {

override fun encode(content: FfiReaction): EncodedContent {
return EncodedContent.parseFrom(encodeReaction(content))
}

override fun decode(content: EncodedContent): FfiReaction {
return decodeReaction(content.toByteArray())
}

override fun fallback(content: FfiReaction): String? {
return when (content.action) {
FfiReactionAction.ADDED -> "Reacted “${content.content}” to an earlier message"
FfiReactionAction.REMOVED -> "Removed “${content.content}” from an earlier message"
else -> null
}
}

override fun shouldPush(content: FfiReaction): Boolean = when (content.action) {
FfiReactionAction.ADDED -> true
else -> false
}
}
37 changes: 37 additions & 0 deletions library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,41 @@ class Message private constructor(
}
}
}

data class MessageWithChildMessages(
var message: Message,
var childMessages: List<Message>
) {
val id: String
get() = message.id

val convoId: String
get() = message.convoId

val senderInboxId: String
get() = message.senderInboxId

val sentAt: Date
get() = message.sentAt

val sentAtNs: Long
get() = message.sentAtNs

val deliveryStatus: MessageDeliveryStatus
get() = message.deliveryStatus

val topic: String
get() = message.topic

@Suppress("UNCHECKED_CAST")
fun <T> content(): T? = message.content() as? T

val fallbackContent: String
get() = message.fallbackContent

val body: String
get() {
return content() as? String ?: fallbackContent
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ enum class GroupPermissionPreconfiguration {
companion object {
fun toFfiGroupPermissionOptions(option: GroupPermissionPreconfiguration): FfiGroupPermissionsOptions {
return when (option) {
ALL_MEMBERS -> FfiGroupPermissionsOptions.ALL_MEMBERS
ALL_MEMBERS -> FfiGroupPermissionsOptions.DEFAULT
ADMIN_ONLY -> FfiGroupPermissionsOptions.ADMIN_ONLY
}
}
Expand All @@ -66,7 +66,8 @@ data class PermissionPolicySet(
updateGroupNamePolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.updateGroupNamePolicy),
updateGroupDescriptionPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.updateGroupDescriptionPolicy),
updateGroupImageUrlSquarePolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.updateGroupImagePolicy),
updateGroupPinnedFrameUrlPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.updateGroupPinnedFrameUrlPolicy)
updateGroupPinnedFrameUrlPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.updateGroupPinnedFrameUrlPolicy),
updateMessageExpirationMsPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.addAdminPolicy)
)
}

Expand Down
Loading

0 comments on commit d150b91

Please sign in to comment.