Skip to content

Commit 5a3f9d0

Browse files
authored
Group Admin Updates (#185)
* add dates to example and a test around removing members * dump the latest libxmtp * stream all messages in libxmtp * add new group admin functions * dump the binaries * add tests for it
1 parent 6f8209f commit 5a3f9d0

File tree

7 files changed

+203
-3
lines changed

7 files changed

+203
-3
lines changed

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

+14
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ class GroupTest {
7979
runBlocking { alixGroup.sync() }
8080
assertEquals(alixGroup.memberAddresses().size, 3)
8181
assertEquals(boGroup.memberAddresses().size, 3)
82+
83+
assertEquals(boGroup.permissionLevel(), GroupPermissions.EVERYONE_IS_ADMIN)
84+
assertEquals(alixGroup.permissionLevel(), GroupPermissions.EVERYONE_IS_ADMIN)
85+
assertEquals(boGroup.adminAddress().lowercase(), boClient.address.lowercase())
86+
assertEquals(alixGroup.adminAddress().lowercase(), boClient.address.lowercase())
87+
assert(boGroup.isAdmin())
88+
assert(!alixGroup.isAdmin())
8289
}
8390

8491
@Test
@@ -115,6 +122,13 @@ class GroupTest {
115122
runBlocking { boGroup.sync() }
116123
assertEquals(alixGroup.memberAddresses().size, 2)
117124
assertEquals(boGroup.memberAddresses().size, 2)
125+
126+
assertEquals(boGroup.permissionLevel(), GroupPermissions.GROUP_CREATOR_IS_ADMIN)
127+
assertEquals(alixGroup.permissionLevel(), GroupPermissions.GROUP_CREATOR_IS_ADMIN)
128+
assertEquals(boGroup.adminAddress().lowercase(), boClient.address.lowercase())
129+
assertEquals(alixGroup.adminAddress().lowercase(), boClient.address.lowercase())
130+
assert(boGroup.isAdmin())
131+
assert(!alixGroup.isAdmin())
118132
}
119133

120134
@Test

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

+17
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import org.xmtp.android.library.messages.DecryptedMessage
1212
import org.xmtp.android.library.messages.PagingInfoSortDirection
1313
import org.xmtp.proto.message.api.v1.MessageApiOuterClass
1414
import uniffi.xmtpv3.FfiGroup
15+
import uniffi.xmtpv3.FfiGroupMetadata
1516
import uniffi.xmtpv3.FfiListMessagesOptions
1617
import uniffi.xmtpv3.FfiMessage
1718
import uniffi.xmtpv3.FfiMessageCallback
19+
import uniffi.xmtpv3.GroupPermissions
1820
import java.lang.Exception
1921
import java.util.Date
2022
import kotlin.time.Duration.Companion.nanoseconds
@@ -27,6 +29,9 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) {
2729
val createdAt: Date
2830
get() = Date(libXMTPGroup.createdAtNs() / 1_000_000)
2931

32+
private val metadata: FfiGroupMetadata
33+
get() = libXMTPGroup.groupMetadata()
34+
3035
fun send(text: String): String {
3136
return send(prepareMessage(content = text, options = null))
3237
}
@@ -133,6 +138,18 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) {
133138
return libXMTPGroup.isActive()
134139
}
135140

141+
fun permissionLevel(): GroupPermissions {
142+
return metadata.policyType()
143+
}
144+
145+
fun isAdmin(): Boolean {
146+
return metadata.creatorAccountAddress().lowercase() == client.address.lowercase()
147+
}
148+
149+
fun adminAddress(): String {
150+
return metadata.creatorAccountAddress()
151+
}
152+
136153
fun addMembers(addresses: List<String>) {
137154
try {
138155
runBlocking { libXMTPGroup.addMembers(addresses) }

library/src/main/java/xmtpv3.kt

+172-3
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ internal interface _UniFFILib : Library {
401401
): Pointer
402402
fun uniffi_xmtpv3_fn_method_fficonversations_stream(`ptr`: Pointer,`callback`: Long,
403403
): Pointer
404+
fun uniffi_xmtpv3_fn_method_fficonversations_stream_all_messages(`ptr`: Pointer,`messageCallback`: Long,
405+
): Pointer
404406
fun uniffi_xmtpv3_fn_method_fficonversations_sync(`ptr`: Pointer,
405407
): Pointer
406408
fun uniffi_xmtpv3_fn_free_ffigroup(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
@@ -411,6 +413,8 @@ internal interface _UniFFILib : Library {
411413
): Long
412414
fun uniffi_xmtpv3_fn_method_ffigroup_find_messages(`ptr`: Pointer,`opts`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
413415
): RustBuffer.ByValue
416+
fun uniffi_xmtpv3_fn_method_ffigroup_group_metadata(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
417+
): Pointer
414418
fun uniffi_xmtpv3_fn_method_ffigroup_id(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
415419
): RustBuffer.ByValue
416420
fun uniffi_xmtpv3_fn_method_ffigroup_is_active(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
@@ -425,6 +429,14 @@ internal interface _UniFFILib : Library {
425429
): Pointer
426430
fun uniffi_xmtpv3_fn_method_ffigroup_sync(`ptr`: Pointer,
427431
): Pointer
432+
fun uniffi_xmtpv3_fn_free_ffigroupmetadata(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
433+
): Unit
434+
fun uniffi_xmtpv3_fn_method_ffigroupmetadata_conversation_type(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
435+
): RustBuffer.ByValue
436+
fun uniffi_xmtpv3_fn_method_ffigroupmetadata_creator_account_address(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
437+
): RustBuffer.ByValue
438+
fun uniffi_xmtpv3_fn_method_ffigroupmetadata_policy_type(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
439+
): RustBuffer.ByValue
428440
fun uniffi_xmtpv3_fn_free_ffistreamcloser(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
429441
): Unit
430442
fun uniffi_xmtpv3_fn_method_ffistreamcloser_end(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
@@ -647,6 +659,8 @@ internal interface _UniFFILib : Library {
647659
): Short
648660
fun uniffi_xmtpv3_checksum_method_fficonversations_stream(
649661
): Short
662+
fun uniffi_xmtpv3_checksum_method_fficonversations_stream_all_messages(
663+
): Short
650664
fun uniffi_xmtpv3_checksum_method_fficonversations_sync(
651665
): Short
652666
fun uniffi_xmtpv3_checksum_method_ffigroup_add_members(
@@ -655,6 +669,8 @@ internal interface _UniFFILib : Library {
655669
): Short
656670
fun uniffi_xmtpv3_checksum_method_ffigroup_find_messages(
657671
): Short
672+
fun uniffi_xmtpv3_checksum_method_ffigroup_group_metadata(
673+
): Short
658674
fun uniffi_xmtpv3_checksum_method_ffigroup_id(
659675
): Short
660676
fun uniffi_xmtpv3_checksum_method_ffigroup_is_active(
@@ -669,6 +685,12 @@ internal interface _UniFFILib : Library {
669685
): Short
670686
fun uniffi_xmtpv3_checksum_method_ffigroup_sync(
671687
): Short
688+
fun uniffi_xmtpv3_checksum_method_ffigroupmetadata_conversation_type(
689+
): Short
690+
fun uniffi_xmtpv3_checksum_method_ffigroupmetadata_creator_account_address(
691+
): Short
692+
fun uniffi_xmtpv3_checksum_method_ffigroupmetadata_policy_type(
693+
): Short
672694
fun uniffi_xmtpv3_checksum_method_ffistreamcloser_end(
673695
): Short
674696
fun uniffi_xmtpv3_checksum_method_ffistreamcloser_is_closed(
@@ -777,6 +799,9 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
777799
if (lib.uniffi_xmtpv3_checksum_method_fficonversations_stream() != 60583.toShort()) {
778800
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
779801
}
802+
if (lib.uniffi_xmtpv3_checksum_method_fficonversations_stream_all_messages() != 65211.toShort()) {
803+
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
804+
}
780805
if (lib.uniffi_xmtpv3_checksum_method_fficonversations_sync() != 62598.toShort()) {
781806
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
782807
}
@@ -789,6 +814,9 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
789814
if (lib.uniffi_xmtpv3_checksum_method_ffigroup_find_messages() != 61973.toShort()) {
790815
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
791816
}
817+
if (lib.uniffi_xmtpv3_checksum_method_ffigroup_group_metadata() != 3690.toShort()) {
818+
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
819+
}
792820
if (lib.uniffi_xmtpv3_checksum_method_ffigroup_id() != 35243.toShort()) {
793821
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
794822
}
@@ -810,6 +838,15 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
810838
if (lib.uniffi_xmtpv3_checksum_method_ffigroup_sync() != 9422.toShort()) {
811839
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
812840
}
841+
if (lib.uniffi_xmtpv3_checksum_method_ffigroupmetadata_conversation_type() != 37015.toShort()) {
842+
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
843+
}
844+
if (lib.uniffi_xmtpv3_checksum_method_ffigroupmetadata_creator_account_address() != 1906.toShort()) {
845+
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
846+
}
847+
if (lib.uniffi_xmtpv3_checksum_method_ffigroupmetadata_policy_type() != 22845.toShort()) {
848+
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
849+
}
813850
if (lib.uniffi_xmtpv3_checksum_method_ffistreamcloser_end() != 47211.toShort()) {
814851
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
815852
}
@@ -1259,6 +1296,7 @@ public interface FfiConversationsInterface {
12591296
suspend fun `createGroup`(`accountAddresses`: List<String>, `permissions`: GroupPermissions?): FfiGroup@Throws(GenericException::class)
12601297
suspend fun `list`(`opts`: FfiListConversationsOptions): List<FfiGroup>@Throws(GenericException::class)
12611298
suspend fun `stream`(`callback`: FfiConversationCallback): FfiStreamCloser@Throws(GenericException::class)
1299+
suspend fun `streamAllMessages`(`messageCallback`: FfiMessageCallback): FfiStreamCloser@Throws(GenericException::class)
12621300
suspend fun `sync`()
12631301
companion object
12641302
}
@@ -1342,6 +1380,26 @@ class FfiConversations(
13421380
)
13431381
}
13441382

1383+
@Throws(GenericException::class)
1384+
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
1385+
override suspend fun `streamAllMessages`(`messageCallback`: FfiMessageCallback) : FfiStreamCloser {
1386+
return uniffiRustCallAsync(
1387+
callWithPointer { thisPtr ->
1388+
_UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_fficonversations_stream_all_messages(
1389+
thisPtr,
1390+
FfiConverterTypeFfiMessageCallback.lower(`messageCallback`),
1391+
)
1392+
},
1393+
{ future, continuation -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_poll_pointer(future, continuation) },
1394+
{ future, continuation -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_complete_pointer(future, continuation) },
1395+
{ future -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_free_pointer(future) },
1396+
// lift function
1397+
{ FfiConverterTypeFfiStreamCloser.lift(it) },
1398+
// Error FFI converter
1399+
GenericException.ErrorHandler,
1400+
)
1401+
}
1402+
13451403
@Throws(GenericException::class)
13461404
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
13471405
override suspend fun `sync`() {
@@ -1398,7 +1456,8 @@ public interface FfiGroupInterface {
13981456
@Throws(GenericException::class)
13991457
suspend fun `addMembers`(`accountAddresses`: List<String>)
14001458
fun `createdAtNs`(): Long@Throws(GenericException::class)
1401-
fun `findMessages`(`opts`: FfiListMessagesOptions): List<FfiMessage>
1459+
fun `findMessages`(`opts`: FfiListMessagesOptions): List<FfiMessage>@Throws(GenericException::class)
1460+
fun `groupMetadata`(): FfiGroupMetadata
14021461
fun `id`(): ByteArray@Throws(GenericException::class)
14031462
fun `isActive`(): Boolean@Throws(GenericException::class)
14041463
fun `listMembers`(): List<FfiGroupMember>@Throws(GenericException::class)
@@ -1471,6 +1530,18 @@ class FfiGroup(
14711530
FfiConverterSequenceTypeFfiMessage.lift(it)
14721531
}
14731532

1533+
1534+
@Throws(GenericException::class)override fun `groupMetadata`(): FfiGroupMetadata =
1535+
callWithPointer {
1536+
rustCallWithError(GenericException) { _status ->
1537+
_UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_group_metadata(it,
1538+
1539+
_status)
1540+
}
1541+
}.let {
1542+
FfiConverterTypeFfiGroupMetadata.lift(it)
1543+
}
1544+
14741545
override fun `id`(): ByteArray =
14751546
callWithPointer {
14761547
rustCall() { _status ->
@@ -1621,6 +1692,98 @@ public object FfiConverterTypeFfiGroup: FfiConverter<FfiGroup, Pointer> {
16211692

16221693

16231694

1695+
public interface FfiGroupMetadataInterface {
1696+
1697+
fun `conversationType`(): String
1698+
fun `creatorAccountAddress`(): String@Throws(GenericException::class)
1699+
fun `policyType`(): GroupPermissions
1700+
companion object
1701+
}
1702+
1703+
class FfiGroupMetadata(
1704+
pointer: Pointer
1705+
) : FFIObject(pointer), FfiGroupMetadataInterface {
1706+
1707+
/**
1708+
* Disconnect the object from the underlying Rust object.
1709+
*
1710+
* It can be called more than once, but once called, interacting with the object
1711+
* causes an `IllegalStateException`.
1712+
*
1713+
* Clients **must** call this method once done with the object, or cause a memory leak.
1714+
*/
1715+
override protected fun freeRustArcPtr() {
1716+
rustCall() { status ->
1717+
_UniFFILib.INSTANCE.uniffi_xmtpv3_fn_free_ffigroupmetadata(this.pointer, status)
1718+
}
1719+
}
1720+
1721+
override fun `conversationType`(): String =
1722+
callWithPointer {
1723+
rustCall() { _status ->
1724+
_UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroupmetadata_conversation_type(it,
1725+
1726+
_status)
1727+
}
1728+
}.let {
1729+
FfiConverterString.lift(it)
1730+
}
1731+
1732+
override fun `creatorAccountAddress`(): String =
1733+
callWithPointer {
1734+
rustCall() { _status ->
1735+
_UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroupmetadata_creator_account_address(it,
1736+
1737+
_status)
1738+
}
1739+
}.let {
1740+
FfiConverterString.lift(it)
1741+
}
1742+
1743+
1744+
@Throws(GenericException::class)override fun `policyType`(): GroupPermissions =
1745+
callWithPointer {
1746+
rustCallWithError(GenericException) { _status ->
1747+
_UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroupmetadata_policy_type(it,
1748+
1749+
_status)
1750+
}
1751+
}.let {
1752+
FfiConverterTypeGroupPermissions.lift(it)
1753+
}
1754+
1755+
1756+
1757+
1758+
companion object
1759+
1760+
}
1761+
1762+
public object FfiConverterTypeFfiGroupMetadata: FfiConverter<FfiGroupMetadata, Pointer> {
1763+
override fun lower(value: FfiGroupMetadata): Pointer = value.callWithPointer { it }
1764+
1765+
override fun lift(value: Pointer): FfiGroupMetadata {
1766+
return FfiGroupMetadata(value)
1767+
}
1768+
1769+
override fun read(buf: ByteBuffer): FfiGroupMetadata {
1770+
// The Rust code always writes pointers as 8 bytes, and will
1771+
// fail to compile if they don't fit.
1772+
return lift(Pointer(buf.getLong()))
1773+
}
1774+
1775+
override fun allocationSize(value: FfiGroupMetadata) = 8
1776+
1777+
override fun write(value: FfiGroupMetadata, buf: ByteBuffer) {
1778+
// The Rust code always expects pointers written as 8 bytes,
1779+
// and will fail to compile if they don't fit.
1780+
buf.putLong(Pointer.nativeValue(lower(value)))
1781+
}
1782+
}
1783+
1784+
1785+
1786+
16241787
public interface FfiStreamCloserInterface {
16251788

16261789
fun `end`()
@@ -2547,6 +2710,7 @@ sealed class GenericException(message: String): Exception(message) {
25472710
class ApiException(message: String) : GenericException(message)
25482711
class GroupException(message: String) : GenericException(message)
25492712
class Signature(message: String) : GenericException(message)
2713+
class GroupMetadata(message: String) : GenericException(message)
25502714
class Generic(message: String) : GenericException(message)
25512715

25522716

@@ -2565,7 +2729,8 @@ public object FfiConverterTypeGenericError : FfiConverterRustBuffer<GenericExcep
25652729
4 -> GenericException.ApiException(FfiConverterString.read(buf))
25662730
5 -> GenericException.GroupException(FfiConverterString.read(buf))
25672731
6 -> GenericException.Signature(FfiConverterString.read(buf))
2568-
7 -> GenericException.Generic(FfiConverterString.read(buf))
2732+
7 -> GenericException.GroupMetadata(FfiConverterString.read(buf))
2733+
8 -> GenericException.Generic(FfiConverterString.read(buf))
25692734
else -> throw RuntimeException("invalid error enum value, something is very wrong!!")
25702735
}
25712736

@@ -2601,10 +2766,14 @@ public object FfiConverterTypeGenericError : FfiConverterRustBuffer<GenericExcep
26012766
buf.putInt(6)
26022767
Unit
26032768
}
2604-
is GenericException.Generic -> {
2769+
is GenericException.GroupMetadata -> {
26052770
buf.putInt(7)
26062771
Unit
26072772
}
2773+
is GenericException.Generic -> {
2774+
buf.putInt(8)
2775+
Unit
2776+
}
26082777
}.let { /* this makes the `when` an expression, which ensures it is exhaustive */ }
26092778
}
26102779

Binary file not shown.
Binary file not shown.
47.6 KB
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)