Skip to content

Commit e6a7bfe

Browse files
authored
Group message delivery status (#232)
* dump the schema changes * dump the latest jni * add delivery status to message * correctly return message id * fix the test * add test for filtering
1 parent 6030e8c commit e6a7bfe

File tree

12 files changed

+138
-19
lines changed

12 files changed

+138
-19
lines changed

library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt

+26-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import org.xmtp.android.library.codecs.Reaction
1717
import org.xmtp.android.library.codecs.ReactionAction
1818
import org.xmtp.android.library.codecs.ReactionCodec
1919
import org.xmtp.android.library.codecs.ReactionSchema
20+
import org.xmtp.android.library.messages.MessageDeliveryStatus
2021
import org.xmtp.android.library.messages.PrivateKey
2122
import org.xmtp.android.library.messages.PrivateKeyBuilder
2223
import org.xmtp.android.library.messages.walletAddress
@@ -330,9 +331,11 @@ class GroupTest {
330331
fun testCanSendMessageToGroup() {
331332
val group = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress)) }
332333
runBlocking { group.send("howdy") }
333-
runBlocking { group.send("gm") }
334+
val messageId = runBlocking { group.send("gm") }
334335
runBlocking { group.sync() }
335336
assertEquals(group.messages().first().body, "gm")
337+
assertEquals(group.messages().first().id, messageId)
338+
assertEquals(group.messages().first().deliveryStatus, MessageDeliveryStatus.PUBLISHED)
336339
assertEquals(group.messages().size, 3)
337340

338341
runBlocking { alixClient.conversations.syncGroups() }
@@ -342,6 +345,28 @@ class GroupTest {
342345
assertEquals(sameGroup.messages().first().body, "gm")
343346
}
344347

348+
@Test
349+
fun testCanListGroupMessages() {
350+
val group = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress)) }
351+
runBlocking {
352+
group.send("howdy")
353+
group.send("gm")
354+
}
355+
356+
assertEquals(group.messages().size, 3)
357+
assertEquals(group.messages(deliveryStatus = MessageDeliveryStatus.UNPUBLISHED).size, 2)
358+
assertEquals(group.messages(deliveryStatus = MessageDeliveryStatus.PUBLISHED).size, 1)
359+
runBlocking { group.sync() }
360+
assertEquals(group.messages().size, 3)
361+
assertEquals(group.messages(deliveryStatus = MessageDeliveryStatus.UNPUBLISHED).size, 0)
362+
assertEquals(group.messages(deliveryStatus = MessageDeliveryStatus.PUBLISHED).size, 3)
363+
364+
runBlocking { alixClient.conversations.syncGroups() }
365+
val sameGroup = runBlocking { alixClient.conversations.listGroups().last() }
366+
runBlocking { sameGroup.sync() }
367+
assertEquals(sameGroup.messages(deliveryStatus = MessageDeliveryStatus.PUBLISHED).size, 2)
368+
}
369+
345370
@Test
346371
fun testCanSendContentTypesToGroup() {
347372
Client.register(codec = ReactionCodec())
+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
Version: 4068715
1+
Version: 5b62701
22
Branch: main
3-
Date: 2024-04-06 04:27:39 +0000
3+
Date: 2024-04-18 03:59:02 +0000

library/src/main/java/org/xmtp/android/library/DecodedMessage.kt

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.xmtp.android.library
22

33
import org.xmtp.android.library.codecs.TextCodec
44
import org.xmtp.android.library.codecs.decoded
5+
import org.xmtp.android.library.messages.MessageDeliveryStatus
56
import org.xmtp.proto.message.contents.Content
67
import java.util.Date
78

@@ -12,6 +13,7 @@ data class DecodedMessage(
1213
var encodedContent: Content.EncodedContent,
1314
var senderAddress: String,
1415
var sent: Date,
16+
var deliveryStatus: MessageDeliveryStatus = MessageDeliveryStatus.PUBLISHED
1517
) {
1618
companion object {
1719
fun preview(client: Client, topic: String, body: String, senderAddress: String, sent: Date): DecodedMessage {

library/src/main/java/org/xmtp/android/library/Group.kt

+20-4
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import org.xmtp.android.library.codecs.EncodedContent
88
import org.xmtp.android.library.codecs.compress
99
import org.xmtp.android.library.libxmtp.MessageV3
1010
import org.xmtp.android.library.messages.DecryptedMessage
11+
import org.xmtp.android.library.messages.MessageDeliveryStatus
1112
import org.xmtp.android.library.messages.PagingInfoSortDirection
1213
import org.xmtp.android.library.messages.Topic
1314
import org.xmtp.proto.message.api.v1.MessageApiOuterClass
15+
import uniffi.xmtpv3.FfiDeliveryStatus
1416
import uniffi.xmtpv3.FfiGroup
1517
import uniffi.xmtpv3.FfiGroupMetadata
1618
import uniffi.xmtpv3.FfiListMessagesOptions
@@ -47,8 +49,8 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) {
4749
if (client.contacts.consentList.groupState(groupId = id) == ConsentState.UNKNOWN) {
4850
client.contacts.allowGroup(groupIds = listOf(id))
4951
}
50-
libXMTPGroup.send(contentBytes = encodedContent.toByteArray())
51-
return id.toHex()
52+
val messageId = libXMTPGroup.send(contentBytes = encodedContent.toByteArray())
53+
return messageId.toHex()
5254
}
5355

5456
fun <T> prepareMessage(content: T, options: SendOptions?): EncodedContent {
@@ -86,12 +88,19 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) {
8688
before: Date? = null,
8789
after: Date? = null,
8890
direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING,
91+
deliveryStatus: MessageDeliveryStatus = MessageDeliveryStatus.ALL,
8992
): List<DecodedMessage> {
9093
val messages = libXMTPGroup.findMessages(
9194
opts = FfiListMessagesOptions(
9295
sentBeforeNs = before?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
9396
sentAfterNs = after?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
94-
limit = limit?.toLong()
97+
limit = limit?.toLong(),
98+
deliveryStatus = when (deliveryStatus) {
99+
MessageDeliveryStatus.PUBLISHED -> FfiDeliveryStatus.PUBLISHED
100+
MessageDeliveryStatus.UNPUBLISHED -> FfiDeliveryStatus.UNPUBLISHED
101+
MessageDeliveryStatus.FAILED -> FfiDeliveryStatus.FAILED
102+
else -> null
103+
}
95104
)
96105
).mapNotNull {
97106
MessageV3(client, it).decodeOrNull()
@@ -108,12 +117,19 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) {
108117
before: Date? = null,
109118
after: Date? = null,
110119
direction: PagingInfoSortDirection = MessageApiOuterClass.SortDirection.SORT_DIRECTION_DESCENDING,
120+
deliveryStatus: MessageDeliveryStatus = MessageDeliveryStatus.ALL,
111121
): List<DecryptedMessage> {
112122
val messages = libXMTPGroup.findMessages(
113123
opts = FfiListMessagesOptions(
114124
sentBeforeNs = before?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
115125
sentAfterNs = after?.time?.nanoseconds?.toLong(DurationUnit.NANOSECONDS),
116-
limit = limit?.toLong()
126+
limit = limit?.toLong(),
127+
deliveryStatus = when (deliveryStatus) {
128+
MessageDeliveryStatus.PUBLISHED -> FfiDeliveryStatus.PUBLISHED
129+
MessageDeliveryStatus.UNPUBLISHED -> FfiDeliveryStatus.UNPUBLISHED
130+
MessageDeliveryStatus.FAILED -> FfiDeliveryStatus.FAILED
131+
else -> null
132+
}
117133
)
118134
).mapNotNull {
119135
MessageV3(client, it).decryptOrNull()

library/src/main/java/org/xmtp/android/library/libxmtp/MessageV3.kt

+11
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import org.xmtp.android.library.DecodedMessage
66
import org.xmtp.android.library.XMTPException
77
import org.xmtp.android.library.codecs.EncodedContent
88
import org.xmtp.android.library.messages.DecryptedMessage
9+
import org.xmtp.android.library.messages.MessageDeliveryStatus
910
import org.xmtp.android.library.messages.Topic
1011
import org.xmtp.android.library.toHex
12+
import uniffi.xmtpv3.FfiDeliveryStatus
1113
import uniffi.xmtpv3.FfiGroupMessageKind
1214
import uniffi.xmtpv3.FfiMessage
1315
import uniffi.xmtpv3.org.xmtp.android.library.codecs.ContentTypeGroupMembershipChange
@@ -27,6 +29,13 @@ data class MessageV3(val client: Client, private val libXMTPMessage: FfiMessage)
2729
val sentAt: Date
2830
get() = Date(libXMTPMessage.sentAtNs / 1_000_000)
2931

32+
val deliveryStatus: MessageDeliveryStatus
33+
get() = when (libXMTPMessage.deliveryStatus) {
34+
FfiDeliveryStatus.UNPUBLISHED -> MessageDeliveryStatus.UNPUBLISHED
35+
FfiDeliveryStatus.PUBLISHED -> MessageDeliveryStatus.PUBLISHED
36+
FfiDeliveryStatus.FAILED -> MessageDeliveryStatus.FAILED
37+
}
38+
3039
fun decode(): DecodedMessage {
3140
try {
3241
val decodedMessage = DecodedMessage(
@@ -36,6 +45,7 @@ data class MessageV3(val client: Client, private val libXMTPMessage: FfiMessage)
3645
encodedContent = EncodedContent.parseFrom(libXMTPMessage.content),
3746
senderAddress = senderAddress,
3847
sent = sentAt,
48+
deliveryStatus = deliveryStatus
3949
)
4050
if (decodedMessage.encodedContent.type == ContentTypeGroupMembershipChange && libXMTPMessage.kind != FfiGroupMessageKind.MEMBERSHIP_CHANGE) {
4151
throw XMTPException("Error decoding group membership change")
@@ -71,6 +81,7 @@ data class MessageV3(val client: Client, private val libXMTPMessage: FfiMessage)
7181
encodedContent = decode().encodedContent,
7282
senderAddress = senderAddress,
7383
sentAt = Date(),
84+
deliveryStatus = deliveryStatus
7485
)
7586
}
7687
}

library/src/main/java/org/xmtp/android/library/messages/DecryptedMessage.kt

+1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ data class DecryptedMessage(
99
var senderAddress: String,
1010
var sentAt: Date,
1111
var topic: String = "",
12+
var deliveryStatus: MessageDeliveryStatus = MessageDeliveryStatus.PUBLISHED
1213
)

library/src/main/java/org/xmtp/android/library/messages/Message.kt

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ package org.xmtp.android.library.messages
22

33
typealias Message = org.xmtp.proto.message.contents.MessageOuterClass.Message
44

5+
enum class MessageDeliveryStatus {
6+
ALL, PUBLISHED, UNPUBLISHED, FAILED
7+
}
8+
59
enum class MessageVersion(val rawValue: String) {
610
V1("v1"),
711
V2("v2");

library/src/main/java/xmtpv3.kt

+72-12
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,7 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
854854
if (lib.uniffi_xmtpv3_checksum_method_ffigroup_remove_members() != 1645.toShort()) {
855855
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
856856
}
857-
if (lib.uniffi_xmtpv3_checksum_method_ffigroup_send() != 55957.toShort()) {
857+
if (lib.uniffi_xmtpv3_checksum_method_ffigroup_send() != 2523.toShort()) {
858858
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
859859
}
860860
if (lib.uniffi_xmtpv3_checksum_method_ffigroup_stream() != 7482.toShort()) {
@@ -1505,7 +1505,7 @@ public interface FfiGroupInterface {
15051505
fun `listMembers`(): List<FfiGroupMember>@Throws(GenericException::class)
15061506
suspend fun `processStreamedGroupMessage`(`envelopeBytes`: ByteArray): FfiMessage@Throws(GenericException::class)
15071507
suspend fun `removeMembers`(`accountAddresses`: List<String>)@Throws(GenericException::class)
1508-
suspend fun `send`(`contentBytes`: ByteArray)@Throws(GenericException::class)
1508+
suspend fun `send`(`contentBytes`: ByteArray): ByteArray@Throws(GenericException::class)
15091509
suspend fun `stream`(`messageCallback`: FfiMessageCallback): FfiStreamCloser@Throws(GenericException::class)
15101510
suspend fun `sync`()
15111511
companion object
@@ -1676,20 +1676,19 @@ class FfiGroup(
16761676

16771677
@Throws(GenericException::class)
16781678
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
1679-
override suspend fun `send`(`contentBytes`: ByteArray) {
1679+
override suspend fun `send`(`contentBytes`: ByteArray) : ByteArray {
16801680
return uniffiRustCallAsync(
16811681
callWithPointer { thisPtr ->
16821682
_UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_send(
16831683
thisPtr,
16841684
FfiConverterByteArray.lower(`contentBytes`),
16851685
)
16861686
},
1687-
{ future, continuation -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_poll_void(future, continuation) },
1688-
{ future, continuation -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_complete_void(future, continuation) },
1689-
{ future -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_free_void(future) },
1687+
{ future, continuation -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_poll_rust_buffer(future, continuation) },
1688+
{ future, continuation -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_complete_rust_buffer(future, continuation) },
1689+
{ future -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_free_rust_buffer(future) },
16901690
// lift function
1691-
{ Unit },
1692-
1691+
{ FfiConverterByteArray.lift(it) },
16931692
// Error FFI converter
16941693
GenericException.ErrorHandler,
16951694
)
@@ -2484,7 +2483,8 @@ public object FfiConverterTypeFfiListConversationsOptions: FfiConverterRustBuffe
24842483
data class FfiListMessagesOptions (
24852484
var `sentBeforeNs`: Long?,
24862485
var `sentAfterNs`: Long?,
2487-
var `limit`: Long?
2486+
var `limit`: Long?,
2487+
var `deliveryStatus`: FfiDeliveryStatus?
24882488
) {
24892489

24902490
companion object
@@ -2496,19 +2496,22 @@ public object FfiConverterTypeFfiListMessagesOptions: FfiConverterRustBuffer<Ffi
24962496
FfiConverterOptionalLong.read(buf),
24972497
FfiConverterOptionalLong.read(buf),
24982498
FfiConverterOptionalLong.read(buf),
2499+
FfiConverterOptionalTypeFfiDeliveryStatus.read(buf),
24992500
)
25002501
}
25012502

25022503
override fun allocationSize(value: FfiListMessagesOptions) = (
25032504
FfiConverterOptionalLong.allocationSize(value.`sentBeforeNs`) +
25042505
FfiConverterOptionalLong.allocationSize(value.`sentAfterNs`) +
2505-
FfiConverterOptionalLong.allocationSize(value.`limit`)
2506+
FfiConverterOptionalLong.allocationSize(value.`limit`) +
2507+
FfiConverterOptionalTypeFfiDeliveryStatus.allocationSize(value.`deliveryStatus`)
25062508
)
25072509

25082510
override fun write(value: FfiListMessagesOptions, buf: ByteBuffer) {
25092511
FfiConverterOptionalLong.write(value.`sentBeforeNs`, buf)
25102512
FfiConverterOptionalLong.write(value.`sentAfterNs`, buf)
25112513
FfiConverterOptionalLong.write(value.`limit`, buf)
2514+
FfiConverterOptionalTypeFfiDeliveryStatus.write(value.`deliveryStatus`, buf)
25122515
}
25132516
}
25142517

@@ -2521,7 +2524,8 @@ data class FfiMessage (
25212524
var `convoId`: ByteArray,
25222525
var `addrFrom`: String,
25232526
var `content`: ByteArray,
2524-
var `kind`: FfiGroupMessageKind
2527+
var `kind`: FfiGroupMessageKind,
2528+
var `deliveryStatus`: FfiDeliveryStatus
25252529
) {
25262530

25272531
companion object
@@ -2536,6 +2540,7 @@ public object FfiConverterTypeFfiMessage: FfiConverterRustBuffer<FfiMessage> {
25362540
FfiConverterString.read(buf),
25372541
FfiConverterByteArray.read(buf),
25382542
FfiConverterTypeFfiGroupMessageKind.read(buf),
2543+
FfiConverterTypeFfiDeliveryStatus.read(buf),
25392544
)
25402545
}
25412546

@@ -2545,7 +2550,8 @@ public object FfiConverterTypeFfiMessage: FfiConverterRustBuffer<FfiMessage> {
25452550
FfiConverterByteArray.allocationSize(value.`convoId`) +
25462551
FfiConverterString.allocationSize(value.`addrFrom`) +
25472552
FfiConverterByteArray.allocationSize(value.`content`) +
2548-
FfiConverterTypeFfiGroupMessageKind.allocationSize(value.`kind`)
2553+
FfiConverterTypeFfiGroupMessageKind.allocationSize(value.`kind`) +
2554+
FfiConverterTypeFfiDeliveryStatus.allocationSize(value.`deliveryStatus`)
25492555
)
25502556

25512557
override fun write(value: FfiMessage, buf: ByteBuffer) {
@@ -2555,6 +2561,7 @@ public object FfiConverterTypeFfiMessage: FfiConverterRustBuffer<FfiMessage> {
25552561
FfiConverterString.write(value.`addrFrom`, buf)
25562562
FfiConverterByteArray.write(value.`content`, buf)
25572563
FfiConverterTypeFfiGroupMessageKind.write(value.`kind`, buf)
2564+
FfiConverterTypeFfiDeliveryStatus.write(value.`deliveryStatus`, buf)
25582565
}
25592566
}
25602567

@@ -2767,6 +2774,30 @@ public object FfiConverterTypeFfiV2SubscribeRequest: FfiConverterRustBuffer<FfiV
27672774

27682775

27692776

2777+
enum class FfiDeliveryStatus {
2778+
UNPUBLISHED,PUBLISHED,FAILED;
2779+
companion object
2780+
}
2781+
2782+
public object FfiConverterTypeFfiDeliveryStatus: FfiConverterRustBuffer<FfiDeliveryStatus> {
2783+
override fun read(buf: ByteBuffer) = try {
2784+
FfiDeliveryStatus.values()[buf.getInt() - 1]
2785+
} catch (e: IndexOutOfBoundsException) {
2786+
throw RuntimeException("invalid enum value, something is very wrong!!", e)
2787+
}
2788+
2789+
override fun allocationSize(value: FfiDeliveryStatus) = 4
2790+
2791+
override fun write(value: FfiDeliveryStatus, buf: ByteBuffer) {
2792+
buf.putInt(value.ordinal + 1)
2793+
}
2794+
}
2795+
2796+
2797+
2798+
2799+
2800+
27702801
enum class FfiGroupMessageKind {
27712802
APPLICATION,MEMBERSHIP_CHANGE;
27722803
companion object
@@ -3591,6 +3622,35 @@ public object FfiConverterOptionalTypeFfiPagingInfo: FfiConverterRustBuffer<FfiP
35913622

35923623

35933624

3625+
public object FfiConverterOptionalTypeFfiDeliveryStatus: FfiConverterRustBuffer<FfiDeliveryStatus?> {
3626+
override fun read(buf: ByteBuffer): FfiDeliveryStatus? {
3627+
if (buf.get().toInt() == 0) {
3628+
return null
3629+
}
3630+
return FfiConverterTypeFfiDeliveryStatus.read(buf)
3631+
}
3632+
3633+
override fun allocationSize(value: FfiDeliveryStatus?): Int {
3634+
if (value == null) {
3635+
return 1
3636+
} else {
3637+
return 1 + FfiConverterTypeFfiDeliveryStatus.allocationSize(value)
3638+
}
3639+
}
3640+
3641+
override fun write(value: FfiDeliveryStatus?, buf: ByteBuffer) {
3642+
if (value == null) {
3643+
buf.put(0)
3644+
} else {
3645+
buf.put(1)
3646+
FfiConverterTypeFfiDeliveryStatus.write(value, buf)
3647+
}
3648+
}
3649+
}
3650+
3651+
3652+
3653+
35943654
public object FfiConverterOptionalTypeGroupPermissions: FfiConverterRustBuffer<GroupPermissions?> {
35953655
override fun read(buf: ByteBuffer): GroupPermissions? {
35963656
if (buf.get().toInt() == 0) {
Binary file not shown.
Binary file not shown.
4.02 KB
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)