diff --git a/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt index 49266715..bceb66c4 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ContactsTest.kt @@ -32,13 +32,13 @@ class ContactsTest { val fixtures = fixtures() val contacts = fixtures.bobClient.contacts - var result = contacts.isAllowed(fixtures.alice.walletAddress) + var result = runBlocking { contacts.isAllowed(fixtures.alice.walletAddress) } assert(!result) runBlocking { contacts.allow(listOf(fixtures.alice.walletAddress)) } - result = contacts.isAllowed(fixtures.alice.walletAddress) + result = runBlocking { contacts.isAllowed(fixtures.alice.walletAddress) } assert(result) } @@ -47,13 +47,13 @@ class ContactsTest { val fixtures = fixtures() val contacts = fixtures.bobClient.contacts - var result = contacts.isAllowed(fixtures.alice.walletAddress) + var result = runBlocking { contacts.isAllowed(fixtures.alice.walletAddress) } assert(!result) runBlocking { contacts.deny(listOf(fixtures.alice.walletAddress)) } - result = contacts.isDenied(fixtures.alice.walletAddress) + result = runBlocking { contacts.isDenied(fixtures.alice.walletAddress) } assert(result) } } diff --git a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt index ae1fe40e..06752cf4 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ConversationTest.kt @@ -803,72 +803,73 @@ class ConversationTest { @Test @Ignore("TODO: Fix Flaky Test") fun testCanHaveConsentState() { - val bobConversation = - runBlocking { bobClient.conversations.newConversation(alice.walletAddress, null) } - Thread.sleep(1000) - val isAllowed = bobConversation.consentState() == ConsentState.ALLOWED - // Conversations you start should start as allowed - assertTrue("Bob convo should be allowed", isAllowed) - assertTrue( - "Bob contacts should be allowed", - bobClient.contacts.isAllowed(alice.walletAddress) - ) - runBlocking { - bobClient.contacts.deny(listOf(alice.walletAddress)) - bobClient.contacts.refreshConsentList() - } - val isDenied = bobConversation.consentState() == ConsentState.DENIED - assertEquals(bobClient.contacts.consentList.entries.size, 1) - assertTrue("Bob Conversation should be denied", isDenied) + val bobConversation = + runBlocking { bobClient.conversations.newConversation(alice.walletAddress, null) } + Thread.sleep(1000) + val isAllowed = bobConversation.consentState() == ConsentState.ALLOWED + // Conversations you start should start as allowed + assertTrue("Bob convo should be allowed", isAllowed) + assertTrue( + "Bob contacts should be allowed", + bobClient.contacts.isAllowed(alice.walletAddress) + ) + + runBlocking { + bobClient.contacts.deny(listOf(alice.walletAddress)) + bobClient.contacts.refreshConsentList() + } + val isDenied = bobConversation.consentState() == ConsentState.DENIED + assertEquals(bobClient.contacts.consentList.entries.size, 1) + assertTrue("Bob Conversation should be denied", isDenied) - val aliceConversation = runBlocking { aliceClient.conversations.list()[0] } - val isUnknown = aliceConversation.consentState() == ConsentState.UNKNOWN + val aliceConversation = runBlocking { aliceClient.conversations.list()[0] } + val isUnknown = aliceConversation.consentState() == ConsentState.UNKNOWN - // Conversations started with you should start as unknown - assertTrue("Alice conversation should be unknown", isUnknown) + // Conversations started with you should start as unknown + assertTrue("Alice conversation should be unknown", isUnknown) - runBlocking { aliceClient.contacts.allow(listOf(bob.walletAddress)) } + runBlocking { aliceClient.contacts.allow(listOf(bob.walletAddress)) } - val isBobAllowed = aliceConversation.consentState() == ConsentState.ALLOWED - assertTrue("Bob should be allowed from alice conversation", isBobAllowed) + val isBobAllowed = aliceConversation.consentState() == ConsentState.ALLOWED + assertTrue("Bob should be allowed from alice conversation", isBobAllowed) - val aliceClient2 = runBlocking { Client().create(aliceWallet) } - val aliceConversation2 = runBlocking { aliceClient2.conversations.list()[0] } + val aliceClient2 = runBlocking { Client().create(aliceWallet) } + val aliceConversation2 = runBlocking { aliceClient2.conversations.list()[0] } - runBlocking { aliceClient2.contacts.refreshConsentList() } + runBlocking { aliceClient2.contacts.refreshConsentList() } - // Allow state should sync across clients - val isBobAllowed2 = aliceConversation2.consentState() == ConsentState.ALLOWED + // Allow state should sync across clients + val isBobAllowed2 = aliceConversation2.consentState() == ConsentState.ALLOWED - assertTrue("Bob should be allowed from conversation 2", isBobAllowed2) - } + assertTrue("Bob should be allowed from conversation 2", isBobAllowed2) + } - @Test - @Ignore("TODO: Fix Flaky Test") - fun testCanHaveImplicitConsentOnMessageSend() { - val bobConversation = - runBlocking { bobClient.conversations.newConversation(alice.walletAddress, null) } - Thread.sleep(1000) - val isAllowed = bobConversation.consentState() == ConsentState.ALLOWED + @Test + @Ignore("TODO: Fix Flaky Test") + fun testCanHaveImplicitConsentOnMessageSend() { + runBlocking { + val bobConversation = bobClient.conversations.newConversation(alice.walletAddress, null) + Thread.sleep(1000) + val isAllowed = bobConversation.consentState() == ConsentState.ALLOWED - // Conversations you start should start as allowed - assertTrue("Bob convo should be allowed", isAllowed) + // Conversations you start should start as allowed + assertTrue("Bob convo should be allowed", isAllowed) - val aliceConversation = runBlocking { aliceClient.conversations.list()[0] } - val isUnknown = aliceConversation.consentState() == ConsentState.UNKNOWN + val aliceConversation = aliceClient.conversations.list()[0] + val isUnknown = aliceConversation.consentState() == ConsentState.UNKNOWN - // Conversations you receive should start as unknown - assertTrue("Alice convo should be unknown", isUnknown) + // Conversations you receive should start as unknown + assertTrue("Alice convo should be unknown", isUnknown) - runBlocking { - aliceConversation.send(content = "hey bob") - aliceClient.contacts.refreshConsentList() - } - val isNowAllowed = aliceConversation.consentState() == ConsentState.ALLOWED + aliceConversation.send(content = "hey bob") + aliceClient.contacts.refreshConsentList() + val isNowAllowed = aliceConversation.consentState() == ConsentState.ALLOWED - // Conversations you send a message to get marked as allowed - assertTrue("Should now be allowed", isNowAllowed) + // Conversations you send a message to get marked as allowed + assertTrue("Should now be allowed", isNowAllowed) + } + } } @Test 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 2786e3e9..796f3f19 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt @@ -122,8 +122,13 @@ class GroupTest { assert(boGroup.id.isNotEmpty()) assert(alixGroup.id.isNotEmpty()) - assertEquals(boClient.contacts.consentList.groupState(boGroup.id), ConsentState.ALLOWED) - assertEquals(alixClient.contacts.consentList.groupState(alixGroup.id), ConsentState.UNKNOWN) + runBlocking { + assertEquals(boClient.contacts.consentList.groupState(boGroup.id), ConsentState.ALLOWED) + assertEquals( + alixClient.contacts.consentList.groupState(alixGroup.id), + ConsentState.UNKNOWN + ) + } runBlocking { boGroup.addMembers(listOf(caro.walletAddress)) @@ -424,12 +429,14 @@ class GroupTest { @Test fun testGroupStartsWithAllowedState() { - val group = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress)) } - runBlocking { group.send("howdy") } - runBlocking { group.send("gm") } - runBlocking { group.sync() } - assert(boClient.contacts.isGroupAllowed(group.id)) - assertEquals(boClient.contacts.consentList.groupState(group.id), ConsentState.ALLOWED) + runBlocking { + val group = boClient.conversations.newGroup(listOf(alix.walletAddress)) + group.send("howdy") + group.send("gm") + group.sync() + assert(boClient.contacts.isGroupAllowed(group.id)) + assertEquals(boClient.contacts.consentList.groupState(group.id), ConsentState.ALLOWED) + } } @Test @@ -771,58 +778,57 @@ class GroupTest { } @Test - fun testCanAllowGroup() { - val group = runBlocking { - boClient.conversations.newGroup( - listOf( - alix.walletAddress, - caro.walletAddress + fun testGroupConsent() { + runBlocking { + val group = + boClient.conversations.newGroup( + listOf( + alix.walletAddress, + caro.walletAddress + ) ) - ) - } - - var result = boClient.contacts.isGroupAllowed(group.id) - assert(result) + assert(boClient.contacts.isGroupAllowed(group.id)) + assertEquals(group.consentState(), ConsentState.ALLOWED) - runBlocking { boClient.contacts.allowGroups(listOf(group.id)) } - - result = boClient.contacts.isGroupAllowed(group.id) - assert(result) - } + boClient.contacts.denyGroups(listOf(group.id)) + assert(boClient.contacts.isGroupDenied(group.id)) + assertEquals(group.consentState(), ConsentState.DENIED) - @Test - fun testCanDenyGroup() { - val group = runBlocking { - boClient.conversations.newGroup( - listOf( - alix.walletAddress, - caro.walletAddress - ) - ) + group.updateConsentState(ConsentState.ALLOWED) + assert(boClient.contacts.isGroupAllowed(group.id)) + assertEquals(group.consentState(), ConsentState.ALLOWED) } - var result = boClient.contacts.isGroupAllowed(group.id) - assert(result) - - runBlocking { boClient.contacts.denyGroups(listOf(group.id)) } - - result = boClient.contacts.isGroupDenied(group.id) - assert(result) } @Test fun testCanAllowAndDenyInboxId() { - assert(!boClient.contacts.isInboxAllowed(alixClient.inboxId)) - assert(!boClient.contacts.isInboxDenied(alixClient.inboxId)) + runBlocking { + val boGroup = boClient.conversations.newGroup(listOf(alix.walletAddress)) + assert(!boClient.contacts.isInboxAllowed(alixClient.inboxId)) + assert(!boClient.contacts.isInboxDenied(alixClient.inboxId)) - runBlocking { boClient.contacts.allowInboxes(listOf(alixClient.inboxId)) } + boClient.contacts.allowInboxes(listOf(alixClient.inboxId)) + var alixMember = boGroup.members().firstOrNull { it.inboxId == alixClient.inboxId } + assertEquals(alixMember!!.consentState, ConsentState.ALLOWED) - assert(boClient.contacts.isInboxAllowed(alixClient.inboxId)) - assert(!boClient.contacts.isInboxDenied(alixClient.inboxId)) + assert(boClient.contacts.isInboxAllowed(alixClient.inboxId)) + assert(!boClient.contacts.isInboxDenied(alixClient.inboxId)) - runBlocking { boClient.contacts.denyInboxes(listOf(alixClient.inboxId)) } + boClient.contacts.denyInboxes(listOf(alixClient.inboxId)) + alixMember = boGroup.members().firstOrNull { it.inboxId == alixClient.inboxId } + assertEquals(alixMember!!.consentState, ConsentState.DENIED) - assert(!boClient.contacts.isInboxAllowed(alixClient.inboxId)) - assert(boClient.contacts.isInboxDenied(alixClient.inboxId)) + assert(!boClient.contacts.isInboxAllowed(alixClient.inboxId)) + assert(boClient.contacts.isInboxDenied(alixClient.inboxId)) + + boClient.contacts.allow(listOf(alixClient.address)) + alixMember = boGroup.members().firstOrNull { it.inboxId == alixClient.inboxId } + assertEquals(alixMember!!.consentState, ConsentState.ALLOWED) + assert(boClient.contacts.isInboxAllowed(alixClient.inboxId)) + assert(!boClient.contacts.isInboxDenied(alixClient.inboxId)) + assert(boClient.contacts.isAllowed(alixClient.address)) + assert(!boClient.contacts.isDenied(alixClient.address)) + } } @Test @@ -872,9 +878,9 @@ class GroupTest { } runBlocking { alixClient.conversations.syncGroups() } val alixGroup: Group = alixClient.findGroup(boGroup.id)!! - assert(!alixClient.contacts.isGroupAllowed(boGroup.id)) + runBlocking { assert(!alixClient.contacts.isGroupAllowed(boGroup.id)) } val preparedMessageId = runBlocking { alixGroup.prepareMessage("Test text") } - assert(alixClient.contacts.isGroupAllowed(boGroup.id)) + runBlocking { assert(alixClient.contacts.isGroupAllowed(boGroup.id)) } assertEquals(alixGroup.messages().size, 1) assertEquals(alixGroup.messages(deliveryStatus = MessageDeliveryStatus.PUBLISHED).size, 0) assertEquals(alixGroup.messages(deliveryStatus = MessageDeliveryStatus.UNPUBLISHED).size, 1) 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 50ff85fe..cc483309 100644 --- a/library/src/main/java/org/xmtp/android/library/Client.kt +++ b/library/src/main/java/org/xmtp/android/library/Client.kt @@ -90,9 +90,10 @@ class Client() { var logger: XMTPLogger = XMTPLogger() val libXMTPVersion: String = getVersionInfo() var installationId: String = "" - private var v3Client: FfiXmtpClient? = null + var v3Client: FfiXmtpClient? = null var dbPath: String = "" lateinit var inboxId: String + var hasV2Client: Boolean = true companion object { private const val TAG = "Client" diff --git a/library/src/main/java/org/xmtp/android/library/Contacts.kt b/library/src/main/java/org/xmtp/android/library/Contacts.kt index f64afb8c..dfe00e21 100644 --- a/library/src/main/java/org/xmtp/android/library/Contacts.kt +++ b/library/src/main/java/org/xmtp/android/library/Contacts.kt @@ -9,12 +9,56 @@ import org.xmtp.android.library.messages.Topic import org.xmtp.android.library.messages.walletAddress import org.xmtp.proto.message.api.v1.MessageApiOuterClass import org.xmtp.proto.message.contents.PrivatePreferences.PrivatePreferencesAction +import uniffi.xmtpv3.FfiConsentEntityType +import uniffi.xmtpv3.FfiConsentState import java.util.Date enum class ConsentState { ALLOWED, DENIED, - UNKNOWN, + UNKNOWN; + + companion object { + fun toFfiConsentState(option: ConsentState): FfiConsentState { + return when (option) { + ConsentState.ALLOWED -> FfiConsentState.ALLOWED + ConsentState.DENIED -> FfiConsentState.DENIED + else -> FfiConsentState.UNKNOWN + } + } + + fun fromFfiConsentState(option: FfiConsentState): ConsentState { + return when (option) { + FfiConsentState.ALLOWED -> ConsentState.ALLOWED + FfiConsentState.DENIED -> ConsentState.DENIED + else -> ConsentState.UNKNOWN + } + } + } +} + +enum class EntryType { + ADDRESS, + GROUP_ID, + INBOX_ID; + + companion object { + fun toFfiConsentEntityType(option: EntryType): FfiConsentEntityType { + return when (option) { + EntryType.ADDRESS -> FfiConsentEntityType.ADDRESS + EntryType.GROUP_ID -> FfiConsentEntityType.GROUP_ID + EntryType.INBOX_ID -> FfiConsentEntityType.INBOX_ID + } + } + + fun fromFfiConsentEntityType(option: FfiConsentEntityType): EntryType { + return when (option) { + FfiConsentEntityType.ADDRESS -> EntryType.ADDRESS + FfiConsentEntityType.GROUP_ID -> EntryType.GROUP_ID + FfiConsentEntityType.INBOX_ID -> EntryType.INBOX_ID + } + } + } } data class ConsentListEntry( @@ -22,12 +66,6 @@ data class ConsentListEntry( val entryType: EntryType, val consentType: ConsentState, ) { - enum class EntryType { - ADDRESS, - GROUP_ID, - INBOX_ID - } - companion object { fun address( address: String, @@ -125,80 +163,72 @@ class ConsentList( } suspend fun publish(entries: List) { - val payload = PrivatePreferencesAction.newBuilder().also { - entries.iterator().forEach { entry -> - when (entry.entryType) { - ConsentListEntry.EntryType.ADDRESS -> { - when (entry.consentType) { - ConsentState.ALLOWED -> - it.setAllowAddress( - PrivatePreferencesAction.AllowAddress.newBuilder() - .addWalletAddresses(entry.value), - ) - - ConsentState.DENIED -> - it.setDenyAddress( - PrivatePreferencesAction.DenyAddress.newBuilder() - .addWalletAddresses(entry.value), - ) - - ConsentState.UNKNOWN -> it.clearMessageType() - } - } - - ConsentListEntry.EntryType.GROUP_ID -> { - when (entry.consentType) { - ConsentState.ALLOWED -> - it.setAllowGroup( - PrivatePreferencesAction.AllowGroup.newBuilder() - .addGroupIds(entry.value), - ) - - ConsentState.DENIED -> - it.setDenyGroup( - PrivatePreferencesAction.DenyGroup.newBuilder() - .addGroupIds(entry.value), - ) - - ConsentState.UNKNOWN -> it.clearMessageType() - } - } - - ConsentListEntry.EntryType.INBOX_ID -> { - when (entry.consentType) { - ConsentState.ALLOWED -> - it.setAllowInboxId( - PrivatePreferencesAction.AllowInboxId.newBuilder() - .addInboxIds(entry.value), - ) - - ConsentState.DENIED -> - it.setDenyInboxId( - PrivatePreferencesAction.DenyInboxId.newBuilder() - .addInboxIds(entry.value), - ) - - ConsentState.UNKNOWN -> it.clearMessageType() - } + if (client.v3Client != null) { + setV3ConsentState(entries) + } + if (client.hasV2Client) { + val payload = PrivatePreferencesAction.newBuilder().also { + entries.iterator().forEach { entry -> + when (entry.entryType to entry.consentType) { + EntryType.ADDRESS to ConsentState.ALLOWED -> it.setAllowAddress( + PrivatePreferencesAction.AllowAddress.newBuilder() + .addWalletAddresses(entry.value) + ) + + EntryType.ADDRESS to ConsentState.DENIED -> it.setDenyAddress( + PrivatePreferencesAction.DenyAddress.newBuilder() + .addWalletAddresses(entry.value) + ) + + EntryType.GROUP_ID to ConsentState.ALLOWED -> it.setAllowGroup( + PrivatePreferencesAction.AllowGroup.newBuilder() + .addGroupIds(entry.value) + ) + + EntryType.GROUP_ID to ConsentState.DENIED -> it.setDenyGroup( + PrivatePreferencesAction.DenyGroup.newBuilder().addGroupIds(entry.value) + ) + + EntryType.INBOX_ID to ConsentState.ALLOWED -> it.setAllowInboxId( + PrivatePreferencesAction.AllowInboxId.newBuilder() + .addInboxIds(entry.value) + ) + + EntryType.INBOX_ID to ConsentState.DENIED -> it.setDenyInboxId( + PrivatePreferencesAction.DenyInboxId.newBuilder() + .addInboxIds(entry.value) + ) + + else -> it.clearMessageType() } } - } - }.build() + }.build() + + val message = + uniffi.xmtpv3.userPreferencesEncrypt( + publicKey.toByteArray(), + privateKey.toByteArray(), + payload.toByteArray(), + ) - val message = - uniffi.xmtpv3.userPreferencesEncrypt( - publicKey.toByteArray(), - privateKey.toByteArray(), - payload.toByteArray(), + val envelope = EnvelopeBuilder.buildFromTopic( + Topic.preferenceList(identifier), + Date(), + ByteArray(message.size) { message[it] }, ) - val envelope = EnvelopeBuilder.buildFromTopic( - Topic.preferenceList(identifier), - Date(), - ByteArray(message.size) { message[it] }, - ) + client.publish(listOf(envelope)) + } + } - client.publish(listOf(envelope)) + suspend fun setV3ConsentState(entries: List) { + entries.iterator().forEach { + client.v3Client?.setConsentState( + ConsentState.toFfiConsentState(it.consentType), + EntryType.toFfiConsentEntityType(it.entryType), + it.value + ) + } } fun allow(address: String): ConsentListEntry { @@ -243,21 +273,42 @@ class ConsentList( return entry } - fun state(address: String): ConsentState { + suspend fun state(address: String): ConsentState { + client.v3Client?.let { + return ConsentState.fromFfiConsentState( + it.getConsentState( + FfiConsentEntityType.ADDRESS, + address + ) + ) + } val entry = entries[ConsentListEntry.address(address).key] - return entry?.consentType ?: ConsentState.UNKNOWN } - fun groupState(groupId: String): ConsentState { + suspend fun groupState(groupId: String): ConsentState { + client.v3Client?.let { + return ConsentState.fromFfiConsentState( + it.getConsentState( + FfiConsentEntityType.GROUP_ID, + groupId + ) + ) + } val entry = entries[ConsentListEntry.groupId(groupId).key] - return entry?.consentType ?: ConsentState.UNKNOWN } - fun inboxIdState(inboxId: String): ConsentState { + suspend fun inboxIdState(inboxId: String): ConsentState { + client.v3Client?.let { + return ConsentState.fromFfiConsentState( + it.getConsentState( + FfiConsentEntityType.INBOX_ID, + inboxId + ) + ) + } val entry = entries[ConsentListEntry.inboxId(inboxId).key] - return entry?.consentType ?: ConsentState.UNKNOWN } } @@ -270,7 +321,8 @@ data class Contacts( ) { suspend fun refreshConsentList(): ConsentList { - consentList.load() + val entries = consentList.load() + consentList.setV3ConsentState(entries) return consentList } @@ -316,27 +368,27 @@ data class Contacts( consentList.publish(entries) } - fun isAllowed(address: String): Boolean { + suspend fun isAllowed(address: String): Boolean { return consentList.state(address) == ConsentState.ALLOWED } - fun isDenied(address: String): Boolean { + suspend fun isDenied(address: String): Boolean { return consentList.state(address) == ConsentState.DENIED } - fun isGroupAllowed(groupId: String): Boolean { + suspend fun isGroupAllowed(groupId: String): Boolean { return consentList.groupState(groupId) == ConsentState.ALLOWED } - fun isGroupDenied(groupId: String): Boolean { + suspend fun isGroupDenied(groupId: String): Boolean { return consentList.groupState(groupId) == ConsentState.DENIED } - fun isInboxAllowed(inboxId: String): Boolean { + suspend fun isInboxAllowed(inboxId: String): Boolean { return consentList.inboxIdState(inboxId) == ConsentState.ALLOWED } - fun isInboxDenied(inboxId: String): Boolean { + suspend fun isInboxDenied(inboxId: String): Boolean { return consentList.inboxIdState(inboxId) == ConsentState.DENIED } diff --git a/library/src/main/java/org/xmtp/android/library/Conversation.kt b/library/src/main/java/org/xmtp/android/library/Conversation.kt index dbb7f43e..b431967f 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversation.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversation.kt @@ -89,11 +89,11 @@ sealed class Conversation { } } - fun consentState(): ConsentState { + suspend fun consentState(): ConsentState { return when (this) { is V1 -> conversationV1.client.contacts.consentList.state(address = peerAddress) is V2 -> conversationV2.client.contacts.consentList.state(address = peerAddress) - is Group -> group.client.contacts.consentList.groupState(groupId = group.id) + is Group -> group.consentState() } } 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 f2cf5cc8..6eb221a2 100644 --- a/library/src/main/java/org/xmtp/android/library/Group.kt +++ b/library/src/main/java/org/xmtp/android/library/Group.kt @@ -66,8 +66,8 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) { } suspend fun send(encodedContent: EncodedContent): String { - if (client.contacts.consentList.groupState(groupId = id) == ConsentState.UNKNOWN) { - client.contacts.allowGroups(groupIds = listOf(id)) + if (consentState() == ConsentState.UNKNOWN) { + updateConsentState(ConsentState.ALLOWED) } val messageId = libXMTPGroup.send(contentBytes = encodedContent.toByteArray()) return messageId.toHex() @@ -100,8 +100,8 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) { } suspend fun prepareMessage(content: T, options: SendOptions? = null): String { - if (client.contacts.consentList.groupState(groupId = id) == ConsentState.UNKNOWN) { - client.contacts.allowGroups(groupIds = listOf(id)) + if (consentState() == ConsentState.UNKNOWN) { + updateConsentState(ConsentState.ALLOWED) } val encodeContent = encodeContent(content = content, options = options) return libXMTPGroup.sendOptimistic(encodeContent.toByteArray()).toHex() @@ -178,6 +178,23 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) { return MessageV3(client, message) } + suspend fun updateConsentState(state: ConsentState) { + if (client.hasV2Client) { + when (state) { + ConsentState.ALLOWED -> client.contacts.allowGroups(groupIds = listOf(id)) + ConsentState.DENIED -> client.contacts.denyGroups(groupIds = listOf(id)) + ConsentState.UNKNOWN -> Unit + } + } + + val consentState = ConsentState.toFfiConsentState(state) + libXMTPGroup.updateConsentState(consentState) + } + + fun consentState(): ConsentState { + return ConsentState.fromFfiConsentState(libXMTPGroup.consentState()) + } + fun isActive(): Boolean { return libXMTPGroup.isActive() } diff --git a/library/src/main/java/org/xmtp/android/library/libxmtp/Member.kt b/library/src/main/java/org/xmtp/android/library/libxmtp/Member.kt index 35c193fe..c502247a 100644 --- a/library/src/main/java/org/xmtp/android/library/libxmtp/Member.kt +++ b/library/src/main/java/org/xmtp/android/library/libxmtp/Member.kt @@ -1,5 +1,6 @@ package org.xmtp.android.library.libxmtp +import org.xmtp.android.library.ConsentState import uniffi.xmtpv3.FfiGroupMember import uniffi.xmtpv3.FfiPermissionLevel @@ -18,4 +19,7 @@ class Member(private val ffiMember: FfiGroupMember) { FfiPermissionLevel.ADMIN -> PermissionLevel.ADMIN FfiPermissionLevel.SUPER_ADMIN -> PermissionLevel.SUPER_ADMIN } + + val consentState: ConsentState + get() = ConsentState.fromFfiConsentState(ffiMember.consentState) }