From 78c26ebde82be4dc67fc30393f66038c0de78b56 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 20 Feb 2024 10:13:20 -0800 Subject: [PATCH] Group Admin Permissions (#182) --- .../org/xmtp/android/library/GroupTest.kt | 74 +++++++++++++++++-- .../java/org/xmtp/android/library/Client.kt | 3 +- .../org/xmtp/android/library/Conversations.kt | 8 +- .../java/org/xmtp/android/library/Group.kt | 13 +++- 4 files changed, 84 insertions(+), 14 deletions(-) diff --git a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt index 3f13423cd..08520629f 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt @@ -17,6 +17,7 @@ import org.xmtp.android.library.codecs.ReactionSchema import org.xmtp.android.library.messages.PrivateKey import org.xmtp.android.library.messages.PrivateKeyBuilder import org.xmtp.android.library.messages.walletAddress +import uniffi.xmtpv3.GroupPermissions @RunWith(AndroidJUnit4::class) class GroupTest { @@ -57,9 +58,63 @@ class GroupTest { } @Test - fun testCanCreateAGroup() { - val group = boClient.conversations.newGroup(listOf(alix.walletAddress)) - assert(group.id.isNotEmpty()) + fun testCanCreateAGroupWithDefaultPermissions() { + val boGroup = boClient.conversations.newGroup(listOf(alix.walletAddress)) + runBlocking { alixClient.conversations.syncGroups() } + val alixGroup = alixClient.conversations.listGroups().first() + assert(boGroup.id.isNotEmpty()) + assert(alixGroup.id.isNotEmpty()) + + alixGroup.addMembers(listOf(caro.walletAddress)) + runBlocking { boGroup.sync() } + assertEquals(alixGroup.memberAddresses().size, 3) + assertEquals(boGroup.memberAddresses().size, 3) + + alixGroup.removeMembers(listOf(caro.walletAddress)) + runBlocking { boGroup.sync() } + assertEquals(alixGroup.memberAddresses().size, 2) + assertEquals(boGroup.memberAddresses().size, 2) + + boGroup.addMembers(listOf(caro.walletAddress)) + runBlocking { alixGroup.sync() } + assertEquals(alixGroup.memberAddresses().size, 3) + assertEquals(boGroup.memberAddresses().size, 3) + } + + @Test + fun testCanCreateAGroupWithAdminPermissions() { + val boGroup = boClient.conversations.newGroup( + listOf(alix.walletAddress), + permissions = GroupPermissions.GROUP_CREATOR_IS_ADMIN + ) + runBlocking { alixClient.conversations.syncGroups() } + val alixGroup = alixClient.conversations.listGroups().first() + assert(boGroup.id.isNotEmpty()) + assert(alixGroup.id.isNotEmpty()) + + boGroup.addMembers(listOf(caro.walletAddress)) + runBlocking { alixGroup.sync() } + assertEquals(alixGroup.memberAddresses().size, 3) + assertEquals(boGroup.memberAddresses().size, 3) + + assertThrows(XMTPException::class.java) { + alixGroup.removeMembers(listOf(caro.walletAddress)) + } + runBlocking { boGroup.sync() } + assertEquals(alixGroup.memberAddresses().size, 3) + assertEquals(boGroup.memberAddresses().size, 3) + + boGroup.removeMembers(listOf(caro.walletAddress)) + runBlocking { alixGroup.sync() } + assertEquals(alixGroup.memberAddresses().size, 2) + assertEquals(boGroup.memberAddresses().size, 2) + + assertThrows(XMTPException::class.java) { + alixGroup.addMembers(listOf(caro.walletAddress)) + } + runBlocking { boGroup.sync() } + assertEquals(alixGroup.memberAddresses().size, 2) + assertEquals(boGroup.memberAddresses().size, 2) } @Test @@ -241,10 +296,12 @@ class GroupTest { @Test fun testCanStreamGroupMessages() = kotlinx.coroutines.test.runTest { val group = boClient.conversations.newGroup(listOf(alix.walletAddress.lowercase())) + alixClient.conversations.syncGroups() + val alixGroup = alixClient.conversations.listGroups().first() group.streamMessages().test { - group.send("hi") + alixGroup.send("hi") assertEquals("hi", awaitItem().body) - group.send("hi again") + alixGroup.send("hi again") assertEquals("hi again", awaitItem().body) } } @@ -252,11 +309,12 @@ class GroupTest { @Test fun testCanStreamDecryptedGroupMessages() = kotlinx.coroutines.test.runTest { val group = boClient.conversations.newGroup(listOf(alix.walletAddress)) - + alixClient.conversations.syncGroups() + val alixGroup = alixClient.conversations.listGroups().first() group.streamDecryptedMessages().test { - group.send("hi") + alixGroup.send("hi") assertEquals("hi", awaitItem().encodedContent.content.toStringUtf8()) - group.send("hi again") + alixGroup.send("hi again") assertEquals("hi again", awaitItem().encodedContent.content.toStringUtf8()) } } diff --git a/library/src/main/java/org/xmtp/android/library/Client.kt b/library/src/main/java/org/xmtp/android/library/Client.kt index b6b5925d7..fb48b8f4d 100644 --- a/library/src/main/java/org/xmtp/android/library/Client.kt +++ b/library/src/main/java/org/xmtp/android/library/Client.kt @@ -566,8 +566,7 @@ class Client() { fun canMessage(addresses: List): Boolean { return runBlocking { - libXMTPClient != null && !libXMTPClient!!.canMessage(addresses.map { it }) - .contains(false) + libXMTPClient != null && !libXMTPClient!!.canMessage(addresses).contains(false) } } diff --git a/library/src/main/java/org/xmtp/android/library/Conversations.kt b/library/src/main/java/org/xmtp/android/library/Conversations.kt index ef8605027..b171b7849 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversations.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversations.kt @@ -39,6 +39,7 @@ import uniffi.xmtpv3.FfiConversationCallback import uniffi.xmtpv3.FfiConversations import uniffi.xmtpv3.FfiGroup import uniffi.xmtpv3.FfiListConversationsOptions +import uniffi.xmtpv3.GroupPermissions import java.util.Date import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.DurationUnit @@ -91,7 +92,10 @@ data class Conversations( ) } - fun newGroup(accountAddresses: List): Group { + fun newGroup( + accountAddresses: List, + permissions: GroupPermissions = GroupPermissions.EVERYONE_IS_ADMIN, + ): Group { if (accountAddresses.isEmpty()) { throw XMTPException("Cannot start an empty group chat.") } @@ -105,7 +109,7 @@ data class Conversations( } val group = runBlocking { - libXMTPConversations?.createGroup(accountAddresses, permissions = null) + libXMTPConversations?.createGroup(accountAddresses, permissions = permissions) ?: throw XMTPException("Client does not support Groups") } return Group(client, group) diff --git a/library/src/main/java/org/xmtp/android/library/Group.kt b/library/src/main/java/org/xmtp/android/library/Group.kt index a425da20d..f43c4ad52 100644 --- a/library/src/main/java/org/xmtp/android/library/Group.kt +++ b/library/src/main/java/org/xmtp/android/library/Group.kt @@ -15,6 +15,7 @@ import uniffi.xmtpv3.FfiGroup import uniffi.xmtpv3.FfiListMessagesOptions import uniffi.xmtpv3.FfiMessage import uniffi.xmtpv3.FfiMessageCallback +import java.lang.Exception import java.util.Date import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.DurationUnit @@ -133,11 +134,19 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) { } fun addMembers(addresses: List) { - runBlocking { libXMTPGroup.addMembers(addresses) } + try { + runBlocking { libXMTPGroup.addMembers(addresses) } + } catch (e: Exception) { + throw XMTPException("User does not have permissions", e) + } } fun removeMembers(addresses: List) { - runBlocking { libXMTPGroup.removeMembers(addresses) } + try { + runBlocking { libXMTPGroup.removeMembers(addresses) } + } catch (e: Exception) { + throw XMTPException("User does not have permissions", e) + } } fun memberAddresses(): List {