From 63873c2a1d4c228ba32ca4404bce1a580880a357 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 30 Sep 2024 14:43:42 -0600 Subject: [PATCH 1/6] add the fields to android --- .../modules/xmtpreactnativesdk/XMTPModule.kt | 26 ++++++++++++++++--- .../wrappers/GroupWrapper.kt | 10 +++++-- .../wrappers/MemberWrapper.kt | 9 ++++++- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index 8735ca218..157652609 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.withContext import org.json.JSONObject import org.xmtp.android.library.Client import org.xmtp.android.library.ClientOptions +import org.xmtp.android.library.ConsentState import org.xmtp.android.library.Conversation import org.xmtp.android.library.Group import org.xmtp.android.library.PreEventCallback @@ -372,7 +373,7 @@ class XMTPModule : Module() { } } - AsyncFunction("createOrBuild") Coroutine { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasAuthInboxCallback: Boolean?, dbEncryptionKey: List?, authParams: String -> + AsyncFunction("createOrBuild") Coroutine { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasAuthInboxCallback: Boolean?, dbEncryptionKey: List?, authParams: String -> withContext(Dispatchers.IO) { logV("createOrBuild") val reactSigner = ReactNativeSigner(module = this@XMTPModule, address = address) @@ -1029,7 +1030,10 @@ class XMTPModule : Module() { val client = clients[inboxId] ?: throw XMTPException("No client") client.conversations.syncAllGroups() // Expo Modules do not support UInt, so we need to convert to Int - val numGroupsSyncedInt: Int = client.conversations.syncAllGroups()?.toInt() ?: throw IllegalArgumentException("Value cannot be null") + val numGroupsSyncedInt: Int = + client.conversations.syncAllGroups()?.toInt() ?: throw IllegalArgumentException( + "Value cannot be null" + ) numGroupsSyncedInt } } @@ -1664,6 +1668,14 @@ class XMTPModule : Module() { client.contacts.isGroupDenied(groupId) } } + AsyncFunction("updateGroupConsent") Coroutine { inboxId: String, groupId: String, state: String -> + withContext(Dispatchers.IO) { + logV("updateGroupConsent") + val group = findGroup(inboxId, groupId) + + group?.updateConsentState(getConsentState(state)) + } + } AsyncFunction("exportNativeLogs") Coroutine { -> withContext(Dispatchers.IO) { @@ -1688,7 +1700,7 @@ class XMTPModule : Module() { // Helpers // - private suspend fun getPermissionOption(permissionString: String): PermissionOption { + private fun getPermissionOption(permissionString: String): PermissionOption { return when (permissionString) { "allow" -> PermissionOption.Allow "deny" -> PermissionOption.Deny @@ -1698,6 +1710,14 @@ class XMTPModule : Module() { } } + private fun getConsentState(stateString: String): ConsentState { + return when (stateString) { + "allowed" -> ConsentState.ALLOWED + "denied" -> ConsentState.DENIED + else -> ConsentState.UNKNOWN + } + } + private suspend fun findConversation( inboxId: String, topic: String, diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt index daaaa6969..343802809 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt @@ -2,13 +2,18 @@ package expo.modules.xmtpreactnativesdk.wrappers import com.google.gson.GsonBuilder import org.xmtp.android.library.Client +import org.xmtp.android.library.ConsentState import org.xmtp.android.library.Group -import org.xmtp.android.library.toHex class GroupWrapper { companion object { suspend fun encodeToObj(client: Client, group: Group): Map { + val consentString = when (group.consentState()) { + ConsentState.ALLOWED -> "allowed" + ConsentState.DENIED -> "denied" + ConsentState.UNKNOWN -> "unknown" + } return mapOf( "clientAddress" to client.address, "id" to group.id, @@ -21,7 +26,8 @@ class GroupWrapper { "addedByInboxId" to group.addedByInboxId(), "name" to group.name, "imageUrlSquare" to group.imageUrlSquare, - "description" to group.description + "description" to group.description, + "consentState" to consentString // "pinnedFrameUrl" to group.pinnedFrameUrl ) } diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/MemberWrapper.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/MemberWrapper.kt index dfd92f672..da5b0e706 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/MemberWrapper.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/MemberWrapper.kt @@ -1,6 +1,7 @@ package expo.modules.xmtpreactnativesdk.wrappers import com.google.gson.GsonBuilder +import org.xmtp.android.library.ConsentState import org.xmtp.android.library.libxmtp.Member import org.xmtp.android.library.libxmtp.PermissionLevel @@ -12,10 +13,16 @@ class MemberWrapper { PermissionLevel.ADMIN -> "admin" PermissionLevel.SUPER_ADMIN -> "super_admin" } + val consentString = when (member.consentState) { + ConsentState.ALLOWED -> "allowed" + ConsentState.DENIED -> "denied" + ConsentState.UNKNOWN -> "unknown" + } return mapOf( "inboxId" to member.inboxId, "addresses" to member.addresses, - "permissionLevel" to permissionString + "permissionLevel" to permissionString, + "consentState" to consentString ) } From 56b3fa25d9b0e8ff0770235ec6e7c5527051b44a Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 30 Sep 2024 15:01:40 -0600 Subject: [PATCH 2/6] add the RN side --- src/index.ts | 8 ++++++++ src/lib/Group.ts | 8 ++++++-- src/lib/Member.ts | 17 ++++++++++++++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 0485a95f1..b1cbfc675 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1192,6 +1192,14 @@ export async function isGroupDenied( return XMTPModule.isGroupDenied(inboxId, groupId) } +export async function updateGroupConsent( + inboxId: string, + groupId: string, + state: string +): Promise { + return XMTPModule.updateGroupConsent(inboxId, groupId, state) +} + export async function allowInboxes( inboxId: string, inboxIds: string[] diff --git a/src/lib/Group.ts b/src/lib/Group.ts index 92621e4cf..0149ef573 100644 --- a/src/lib/Group.ts +++ b/src/lib/Group.ts @@ -1,4 +1,5 @@ import { InboxId } from './Client' +import { ConsentState } from './ConsentListEntry' import { ConversationVersion, ConversationContainer, @@ -26,6 +27,7 @@ export interface GroupParams { addedByInboxId: InboxId imageUrlSquare: string description: string + consentState: ConsentState } export class Group< @@ -44,6 +46,7 @@ export class Group< addedByInboxId: InboxId imageUrlSquare: string description: string + consentState: ConsentState // pinnedFrameUrl: string constructor( @@ -62,6 +65,7 @@ export class Group< this.addedByInboxId = params.addedByInboxId this.imageUrlSquare = params.imageUrlSquare this.description = params.description + this.consentState = params.consentState // this.pinnedFrameUrl = params.pinnedFrameUrl } @@ -609,8 +613,8 @@ export class Group< } } - async consentState(): Promise<'allowed' | 'denied' | 'unknown'> { - return await XMTP.groupConsentState(this.client.inboxId, this.id) + async updateConsent(state: ConsentState): Promise { + return await XMTP.updateGroupConsent(this.client.inboxId, this.id, state) } /** diff --git a/src/lib/Member.ts b/src/lib/Member.ts index 932800a45..443034194 100644 --- a/src/lib/Member.ts +++ b/src/lib/Member.ts @@ -1,22 +1,33 @@ import { InboxId } from './Client' +import { ConsentState } from './ConsentListEntry' + +export type PermissionLevel = 'member' | 'admin' | 'super_admin' export class Member { inboxId: InboxId addresses: string[] - permissionLevel: 'member' | 'admin' | 'super_admin' + permissionLevel: PermissionLevel + consentState: ConsentState constructor( inboxId: InboxId, addresses: string[], - permissionLevel: 'member' | 'admin' | 'super_admin' + permissionLevel: PermissionLevel, + consentState: ConsentState ) { this.inboxId = inboxId this.addresses = addresses this.permissionLevel = permissionLevel + this.consentState = consentState } static from(json: string): Member { const entry = JSON.parse(json) - return new Member(entry.inboxId, entry.addresses, entry.permissionLevel) + return new Member( + entry.inboxId, + entry.addresses, + entry.permissionLevel, + entry.consentState + ) } } From e13c47e28a347149c0968b47ee3fa2c826b39e42 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 30 Sep 2024 15:26:27 -0600 Subject: [PATCH 3/6] add the iOS side --- ios/Wrappers/GroupWrapper.swift | 11 ++++++++++- ios/Wrappers/MemberWrapper.swift | 10 ++++++++++ ios/XMTPModule.swift | 23 +++++++++++++++++++++-- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/ios/Wrappers/GroupWrapper.swift b/ios/Wrappers/GroupWrapper.swift index 76f827dd1..5f8d738ef 100644 --- a/ios/Wrappers/GroupWrapper.swift +++ b/ios/Wrappers/GroupWrapper.swift @@ -11,6 +11,14 @@ import XMTP // Wrapper around XMTP.Group to allow passing these objects back into react native. struct GroupWrapper { static func encodeToObj(_ group: XMTP.Group, client: XMTP.Client) async throws -> [String: Any] { + let consentString = switch try group.consentState() { + case .allowed: + "allowed" + case .denied: + "denied" + case .unknown: + "unknown" + } return [ "clientAddress": client.address, "id": group.id, @@ -23,7 +31,8 @@ struct GroupWrapper { "addedByInboxId": try group.addedByInboxId(), "name": try group.groupName(), "imageUrlSquare": try group.groupImageUrlSquare(), - "description": try group.groupDescription() + "description": try group.groupDescription(), + "consentState": consentString // "pinnedFrameUrl": try group.groupPinnedFrameUrl() ] } diff --git a/ios/Wrappers/MemberWrapper.swift b/ios/Wrappers/MemberWrapper.swift index 5bd59d184..57de10526 100644 --- a/ios/Wrappers/MemberWrapper.swift +++ b/ios/Wrappers/MemberWrapper.swift @@ -11,6 +11,15 @@ import XMTP // Wrapper around XMTP.Member to allow passing these objects back into react native. struct MemberWrapper { static func encodeToObj(_ member: XMTP.Member) throws -> [String: Any] { + let consentString = switch member.consentState { + case .allowed: + "allowed" + case .denied: + "denied" + case .unknown: + "unknown" + } + let permissionString = switch member.permissionLevel { case .Member: "member" @@ -23,6 +32,7 @@ struct MemberWrapper { "inboxId": member.inboxId, "addresses": member.addresses, "permissionLevel": permissionString, + "consentString": consentString ] } diff --git a/ios/XMTPModule.swift b/ios/XMTPModule.swift index 3f2c9e365..7dba0d4fc 100644 --- a/ios/XMTPModule.swift +++ b/ios/XMTPModule.swift @@ -1583,10 +1583,18 @@ public class XMTPModule: Module { AsyncFunction("isGroupDenied") { (inboxId: String, groupId: String) -> Bool in guard let client = await clientsManager.getClient(key: inboxId) else { - throw Error.invalidString + throw Error.noClient } return try await client.contacts.isGroupDenied(groupId: groupId) } + + AsyncFunction("updateGroupConsent") { (inboxId: String, groupId: String, state: String) in + guard let group = try await findGroup(inboxId: inboxId, id: groupId) else { + throw Error.conversationNotFound(groupId) + } + + try await group.updateConsentState(state: getConsentState(state: state)) + } AsyncFunction("exportNativeLogs") { () -> String in var logOutput = "" @@ -1630,7 +1638,7 @@ public class XMTPModule: Module { // Helpers // - private func getPermissionOption(permission: String) async throws -> PermissionOption { + private func getPermissionOption(permission: String) throws -> PermissionOption { switch permission { case "allow": return .allow @@ -1644,6 +1652,17 @@ public class XMTPModule: Module { throw Error.invalidPermissionOption } } + + private func getConsentState(state: String) throws -> ConsentState { + switch state { + case "allowed": + return .allowed + case "denied": + return .denied + default: + throw .unknown + } + } func createClientConfig(env: String, appVersion: String?, preEnableIdentityCallback: PreEventCallback? = nil, preCreateIdentityCallback: PreEventCallback? = nil, preAuthenticateToInboxCallback: PreEventCallback? = nil, enableV3: Bool = false, dbEncryptionKey: Data? = nil, dbDirectory: String? = nil, historySyncUrl: String? = nil) -> XMTP.ClientOptions { // Ensure that all codecs have been registered. From c8b31808278dca3e69f38919956dc8f9cfc90c12 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 30 Sep 2024 15:54:32 -0600 Subject: [PATCH 4/6] write v3 tests for it --- example/src/tests/v3OnlyTests.ts | 119 +++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/example/src/tests/v3OnlyTests.ts b/example/src/tests/v3OnlyTests.ts index 17579a7b3..5a6988ae6 100644 --- a/example/src/tests/v3OnlyTests.ts +++ b/example/src/tests/v3OnlyTests.ts @@ -93,6 +93,125 @@ test('can send message', async () => { return true }) +test('can group consent', async () => { + const [alixV2, boV3, caroV2V3] = await createV3TestingClients() + const group = await boV3.conversations.newGroup([caroV2V3.address]) + + const isAllowed = await boV3.contacts.isGroupAllowed(group.id) + assert(isAllowed === true, `isAllowed should be true but was ${isAllowed}`) + let groupState = await group.consentState + assert( + groupState === 'allowed', + `group state should be allowed but was ${groupState}` + ) + + await boV3.contacts.denyGroups([group.id]) + + const isDenied = await boV3.contacts.isGroupDenied(group.id) + assert(isDenied === true, `isDenied should be true but was ${isDenied}`) + groupState = await group.consentState + assert( + groupState === 'denied', + `group state should be denied but was ${groupState}` + ) + + await group.updateConsent('allowed') + + const isAllowed2 = await boV3.contacts.isGroupAllowed(group.id) + assert(isAllowed2 === true, `isAllowed2 should be true but was ${isAllowed2}`) + groupState = await group.consentState + assert( + groupState === 'allowed', + `group state should be allowed but was ${groupState}` + ) + + return true +}) + +test('can allow and deny inbox ids', async () => { + const [alixV2, boV3, caroV2V3] = await createV3TestingClients() + const boGroup = await boV3.conversations.newGroup([caroV2V3.address]) + + let isInboxAllowed = await boV3.contacts.isInboxAllowed(caroV2V3.inboxId) + let isInboxDenied = await boV3.contacts.isInboxDenied(caroV2V3.inboxId) + assert( + isInboxAllowed === false, + `isInboxAllowed should be false but was ${isInboxAllowed}` + ) + assert( + isInboxDenied === false, + `isInboxDenied should be false but was ${isInboxDenied}` + ) + + await boV3.contacts.allowInboxes([caroV2V3.inboxId]) + + let caroMember = boGroup.members.find( + (member) => member.inboxId === caroV2V3.inboxId + ) + assert( + caroMember?.consentState === 'allowed', + `caroMember should be allowed but was ${caroMember?.consentState}` + ) + + isInboxAllowed = await boV3.contacts.isInboxAllowed(caroV2V3.inboxId) + isInboxDenied = await boV3.contacts.isInboxDenied(caroV2V3.inboxId) + assert( + isInboxAllowed === true, + `isInboxAllowed should be true but was ${isInboxAllowed}` + ) + assert( + isInboxDenied === true, + `isInboxDenied should be true but was ${isInboxDenied}` + ) + + let isAddressAllowed = await boV3.contacts.isAllowed(caroV2V3.address) + let isAddressDenied = await boV3.contacts.isDenied(caroV2V3.address) + assert( + isAddressAllowed === true, + `isAddressAllowed should be true but was ${isAddressAllowed}` + ) + assert( + isAddressDenied === false, + `isAddressDenied should be false but was ${isAddressDenied}` + ) + + await boV3.contacts.denyInboxes([caroV2V3.inboxId]) + + caroMember = boGroup.members.find( + (member) => member.inboxId === caroV2V3.inboxId + ) + assert( + caroMember?.consentState === 'denied', + `caroMember should be denied but was ${caroMember?.consentState}` + ) + + isInboxAllowed = await boV3.contacts.isInboxAllowed(caroV2V3.inboxId) + isInboxDenied = await boV3.contacts.isInboxDenied(caroV2V3.inboxId) + assert( + isInboxAllowed === false, + `isInboxAllowed should be false but was ${isInboxAllowed}` + ) + assert( + isInboxDenied === true, + `isInboxDenied should be true but was ${isInboxDenied}` + ) + + await boV3.contacts.allow([alixV2.address]) + + isAddressAllowed = await boV3.contacts.isAllowed(alixV2.address) + isAddressDenied = await boV3.contacts.isDenied(alixV2.address) + assert( + isAddressAllowed === true, + `isAddressAllowed should be true but was ${isAddressAllowed}` + ) + assert( + isAddressDenied === false, + `isAddressDenied should be false but was ${isAddressDenied}` + ) + + return true +}) + test('can stream all messages', async () => { const [alixV2, boV3, caroV2V3] = await createV3TestingClients() const conversation = await alixV2.conversations.newConversation( From e13c1bfd0972ac406c720c1ff9d62601ec67e3c2 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 30 Sep 2024 16:20:51 -0600 Subject: [PATCH 5/6] fix: v3 consent works --- .../modules/xmtpreactnativesdk/XMTPModule.kt | 2 +- .../wrappers/GroupWrapper.kt | 9 ++----- .../wrappers/MemberWrapper.kt | 8 +------ .../project.pbxproj | 2 ++ example/src/tests/v3OnlyTests.ts | 24 +++++++++---------- ios/Wrappers/GroupWrapper.swift | 10 +------- ios/Wrappers/MemberWrapper.swift | 11 +-------- ios/XMTPModule.swift | 4 ++-- src/lib/Group.ts | 8 +++++-- 9 files changed, 28 insertions(+), 50 deletions(-) diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index 157652609..bffa998a1 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -1614,7 +1614,7 @@ class XMTPModule : Module() { withContext(Dispatchers.IO) { val group = findGroup(inboxId, groupId) ?: throw XMTPException("no group found for $groupId") - consentStateToString(Conversation.Group(group).consentState()) + consentStateToString(group.consentState()) } } diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt index 343802809..0336e5f70 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt @@ -1,19 +1,14 @@ package expo.modules.xmtpreactnativesdk.wrappers import com.google.gson.GsonBuilder +import expo.modules.xmtpreactnativesdk.wrappers.ConsentWrapper.Companion.consentStateToString import org.xmtp.android.library.Client -import org.xmtp.android.library.ConsentState import org.xmtp.android.library.Group class GroupWrapper { companion object { suspend fun encodeToObj(client: Client, group: Group): Map { - val consentString = when (group.consentState()) { - ConsentState.ALLOWED -> "allowed" - ConsentState.DENIED -> "denied" - ConsentState.UNKNOWN -> "unknown" - } return mapOf( "clientAddress" to client.address, "id" to group.id, @@ -27,7 +22,7 @@ class GroupWrapper { "name" to group.name, "imageUrlSquare" to group.imageUrlSquare, "description" to group.description, - "consentState" to consentString + "consentState" to consentStateToString(group.consentState()) // "pinnedFrameUrl" to group.pinnedFrameUrl ) } diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/MemberWrapper.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/MemberWrapper.kt index da5b0e706..b241292d2 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/MemberWrapper.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/MemberWrapper.kt @@ -1,7 +1,6 @@ package expo.modules.xmtpreactnativesdk.wrappers import com.google.gson.GsonBuilder -import org.xmtp.android.library.ConsentState import org.xmtp.android.library.libxmtp.Member import org.xmtp.android.library.libxmtp.PermissionLevel @@ -13,16 +12,11 @@ class MemberWrapper { PermissionLevel.ADMIN -> "admin" PermissionLevel.SUPER_ADMIN -> "super_admin" } - val consentString = when (member.consentState) { - ConsentState.ALLOWED -> "allowed" - ConsentState.DENIED -> "denied" - ConsentState.UNKNOWN -> "unknown" - } return mapOf( "inboxId" to member.inboxId, "addresses" to member.addresses, "permissionLevel" to permissionString, - "consentState" to consentString + "consentState" to ConsentWrapper.consentStateToString(member.consentState) ) } diff --git a/example/ios/xmtpreactnativesdkexample.xcodeproj/project.pbxproj b/example/ios/xmtpreactnativesdkexample.xcodeproj/project.pbxproj index 2d41f01a2..3388e7aa2 100644 --- a/example/ios/xmtpreactnativesdkexample.xcodeproj/project.pbxproj +++ b/example/ios/xmtpreactnativesdkexample.xcodeproj/project.pbxproj @@ -294,10 +294,12 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-xmtpreactnativesdkexample/Pods-xmtpreactnativesdkexample-frameworks.sh", "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenSSL-Universal/OpenSSL.framework/OpenSSL", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/example/src/tests/v3OnlyTests.ts b/example/src/tests/v3OnlyTests.ts index 5a6988ae6..9108f6ddf 100644 --- a/example/src/tests/v3OnlyTests.ts +++ b/example/src/tests/v3OnlyTests.ts @@ -99,7 +99,7 @@ test('can group consent', async () => { const isAllowed = await boV3.contacts.isGroupAllowed(group.id) assert(isAllowed === true, `isAllowed should be true but was ${isAllowed}`) - let groupState = await group.consentState + let groupState = await group.state assert( groupState === 'allowed', `group state should be allowed but was ${groupState}` @@ -109,7 +109,7 @@ test('can group consent', async () => { const isDenied = await boV3.contacts.isGroupDenied(group.id) assert(isDenied === true, `isDenied should be true but was ${isDenied}`) - groupState = await group.consentState + groupState = await group.consentState() assert( groupState === 'denied', `group state should be denied but was ${groupState}` @@ -119,7 +119,7 @@ test('can group consent', async () => { const isAllowed2 = await boV3.contacts.isGroupAllowed(group.id) assert(isAllowed2 === true, `isAllowed2 should be true but was ${isAllowed2}`) - groupState = await group.consentState + groupState = await group.consentState() assert( groupState === 'allowed', `group state should be allowed but was ${groupState}` @@ -145,7 +145,7 @@ test('can allow and deny inbox ids', async () => { await boV3.contacts.allowInboxes([caroV2V3.inboxId]) - let caroMember = boGroup.members.find( + let caroMember = (await boGroup.membersList()).find( (member) => member.inboxId === caroV2V3.inboxId ) assert( @@ -157,11 +157,11 @@ test('can allow and deny inbox ids', async () => { isInboxDenied = await boV3.contacts.isInboxDenied(caroV2V3.inboxId) assert( isInboxAllowed === true, - `isInboxAllowed should be true but was ${isInboxAllowed}` + `isInboxAllowed2 should be true but was ${isInboxAllowed}` ) assert( - isInboxDenied === true, - `isInboxDenied should be true but was ${isInboxDenied}` + isInboxDenied === false, + `isInboxDenied2 should be false but was ${isInboxDenied}` ) let isAddressAllowed = await boV3.contacts.isAllowed(caroV2V3.address) @@ -177,7 +177,7 @@ test('can allow and deny inbox ids', async () => { await boV3.contacts.denyInboxes([caroV2V3.inboxId]) - caroMember = boGroup.members.find( + caroMember = (await boGroup.membersList()).find( (member) => member.inboxId === caroV2V3.inboxId ) assert( @@ -189,11 +189,11 @@ test('can allow and deny inbox ids', async () => { isInboxDenied = await boV3.contacts.isInboxDenied(caroV2V3.inboxId) assert( isInboxAllowed === false, - `isInboxAllowed should be false but was ${isInboxAllowed}` + `isInboxAllowed3 should be false but was ${isInboxAllowed}` ) assert( isInboxDenied === true, - `isInboxDenied should be true but was ${isInboxDenied}` + `isInboxDenied3 should be true but was ${isInboxDenied}` ) await boV3.contacts.allow([alixV2.address]) @@ -202,11 +202,11 @@ test('can allow and deny inbox ids', async () => { isAddressDenied = await boV3.contacts.isDenied(alixV2.address) assert( isAddressAllowed === true, - `isAddressAllowed should be true but was ${isAddressAllowed}` + `isAddressAllowed2 should be true but was ${isAddressAllowed}` ) assert( isAddressDenied === false, - `isAddressDenied should be false but was ${isAddressDenied}` + `isAddressDenied2 should be false but was ${isAddressDenied}` ) return true diff --git a/ios/Wrappers/GroupWrapper.swift b/ios/Wrappers/GroupWrapper.swift index 5f8d738ef..3d80b8a70 100644 --- a/ios/Wrappers/GroupWrapper.swift +++ b/ios/Wrappers/GroupWrapper.swift @@ -11,14 +11,6 @@ import XMTP // Wrapper around XMTP.Group to allow passing these objects back into react native. struct GroupWrapper { static func encodeToObj(_ group: XMTP.Group, client: XMTP.Client) async throws -> [String: Any] { - let consentString = switch try group.consentState() { - case .allowed: - "allowed" - case .denied: - "denied" - case .unknown: - "unknown" - } return [ "clientAddress": client.address, "id": group.id, @@ -32,7 +24,7 @@ struct GroupWrapper { "name": try group.groupName(), "imageUrlSquare": try group.groupImageUrlSquare(), "description": try group.groupDescription(), - "consentState": consentString + "consentState": ConsentWrapper.consentStateToString(state: try group.consentState()) // "pinnedFrameUrl": try group.groupPinnedFrameUrl() ] } diff --git a/ios/Wrappers/MemberWrapper.swift b/ios/Wrappers/MemberWrapper.swift index 57de10526..02e29e1ea 100644 --- a/ios/Wrappers/MemberWrapper.swift +++ b/ios/Wrappers/MemberWrapper.swift @@ -11,15 +11,6 @@ import XMTP // Wrapper around XMTP.Member to allow passing these objects back into react native. struct MemberWrapper { static func encodeToObj(_ member: XMTP.Member) throws -> [String: Any] { - let consentString = switch member.consentState { - case .allowed: - "allowed" - case .denied: - "denied" - case .unknown: - "unknown" - } - let permissionString = switch member.permissionLevel { case .Member: "member" @@ -32,7 +23,7 @@ struct MemberWrapper { "inboxId": member.inboxId, "addresses": member.addresses, "permissionLevel": permissionString, - "consentString": consentString + "consentState": ConsentWrapper.consentStateToString(state: member.consentState) ] } diff --git a/ios/XMTPModule.swift b/ios/XMTPModule.swift index 7dba0d4fc..08edd2df5 100644 --- a/ios/XMTPModule.swift +++ b/ios/XMTPModule.swift @@ -1525,7 +1525,7 @@ public class XMTPModule: Module { guard let group = try await findGroup(inboxId: inboxId, id: groupId) else { throw Error.conversationNotFound("no group found for \(groupId)") } - return try ConsentWrapper.consentStateToString(state: await XMTP.Conversation.group(group).consentState()) + return try ConsentWrapper.consentStateToString(state: await group.consentState()) } AsyncFunction("consentList") { (inboxId: String) -> [String] in @@ -1660,7 +1660,7 @@ public class XMTPModule: Module { case "denied": return .denied default: - throw .unknown + return .unknown } } diff --git a/src/lib/Group.ts b/src/lib/Group.ts index 0149ef573..f94fe2e72 100644 --- a/src/lib/Group.ts +++ b/src/lib/Group.ts @@ -46,7 +46,7 @@ export class Group< addedByInboxId: InboxId imageUrlSquare: string description: string - consentState: ConsentState + state: ConsentState // pinnedFrameUrl: string constructor( @@ -65,7 +65,7 @@ export class Group< this.addedByInboxId = params.addedByInboxId this.imageUrlSquare = params.imageUrlSquare this.description = params.description - this.consentState = params.consentState + this.state = params.consentState // this.pinnedFrameUrl = params.pinnedFrameUrl } @@ -613,6 +613,10 @@ export class Group< } } + async consentState(): Promise { + return await XMTP.groupConsentState(this.client.inboxId, this.id) + } + async updateConsent(state: ConsentState): Promise { return await XMTP.updateGroupConsent(this.client.inboxId, this.id, state) } From 26a8bd353306e6217db9a5fe7c1e4c596eab3b34 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Tue, 1 Oct 2024 17:04:09 -0600 Subject: [PATCH 6/6] add a few more consent tests --- example/src/tests/groupTests.ts | 170 +++++++++++++++++++++++--------- 1 file changed, 121 insertions(+), 49 deletions(-) diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index 54a3bb5c5..be6550d6a 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -1834,67 +1834,139 @@ test('creating a group should allow group', async () => { return true }) -test('can allow a group', async () => { +test('can group consent', async () => { const [alix, bo] = await createClients(2) - const alixGroup = await alix.conversations.newGroup([bo.address]) - const startConsent = await bo.contacts.isGroupAllowed(alixGroup.id) - if (startConsent) { - throw Error('Group should not be allowed') - } - await bo.contacts.allowGroups([alixGroup.id]) - const isAllowed = await bo.contacts.isGroupAllowed(alixGroup.id) - if (!isAllowed) { - throw Error('Group should be allowed') - } + const group = await bo.conversations.newGroup([alix.address]) + let isAllowed = await alix.contacts.isGroupAllowed(group.id) + assert( + isAllowed === false, + `alix group should NOT be allowed but was ${isAllowed}` + ) - return true -}) + isAllowed = await bo.contacts.isGroupAllowed(group.id) + assert( + isAllowed === true, + `bo group should be allowed but was ${isAllowed}` + ) + assert( + await group.state === 'allowed', + `the group should have a consent state of allowed but was ${await group.state}` + ) + + await bo.contacts.denyGroups([group.id]) + let isDenied = await bo.contacts.isGroupDenied(group.id) + assert( + isDenied === true, + `bo group should be denied but was ${isDenied}` + ) + assert( + await group.consentState() === 'denied', + `the group should have a consent state of denied but was ${await group.consentState()}` + ) -test('can deny a group', async () => { - const [alix, bo] = await createClients(2) - const alixGroup = await alix.conversations.newGroup([bo.address]) - const startConsent = await bo.contacts.isGroupDenied(alixGroup.id) - if (startConsent) { - throw Error('Group should be unknown') - } - await bo.contacts.denyGroups([alixGroup.id]) - await bo.conversations.syncGroups() - const boGroups = await bo.conversations.listGroups() - const isDenied = await bo.contacts.isGroupDenied(alixGroup.id) - const isGroupDenied = await boGroups[0].isDenied() - if (!isDenied || !isGroupDenied) { - throw Error('Group should be denied') - } - await bo.contacts.allowGroups([alixGroup.id]) - const isAllowed = await bo.contacts.isGroupAllowed(alixGroup.id) - if (!isAllowed) { - throw Error('Group should be allowed') - } + await group.updateConsent('allowed') + isAllowed = await bo.contacts.isGroupAllowed(group.id) + assert( + isAllowed === true, + `bo group should be allowed2 but was ${isAllowed}` + ) + assert( + await group.consentState() === 'allowed', + `the group should have a consent state2 of allowed but was ${await group.consentState()}` + ) return true }) test('can allow and deny a inbox id', async () => { const [alix, bo] = await createClients(2) - const startConsent = await bo.contacts.isInboxAllowed(alix.inboxId) - if (startConsent) { - throw Error('inbox id should be unknown') - } - await bo.contacts.denyInboxes([alix.inboxId]) - const isDenied = await bo.contacts.isInboxDenied(alix.inboxId) - if (!isDenied) { - throw Error('inbox id should be denied') - } + const boGroup = await bo.conversations.newGroup([alix.address]) + + let isInboxAllowed = await bo.contacts.isInboxAllowed(alix.inboxId) + let isInboxDenied = await bo.contacts.isInboxDenied(alix.inboxId) + assert( + isInboxAllowed === false, + `isInboxAllowed should be false but was ${isInboxAllowed}` + ) + assert( + isInboxDenied === false, + `isInboxDenied should be false but was ${isInboxDenied}` + ) + await bo.contacts.allowInboxes([alix.inboxId]) - const isAllowed = await bo.contacts.isInboxAllowed(alix.inboxId) - if (!isAllowed) { - throw Error('inbox id should be allowed') - } - const consentList = await bo.contacts.consentList() + let alixMember = (await boGroup.membersList()).find( + (member) => member.inboxId === alix.inboxId + ) + assert( + alixMember?.consentState === 'allowed', + `alixMember should be allowed but was ${alixMember?.consentState}` + ) + + isInboxAllowed = await bo.contacts.isInboxAllowed(alix.inboxId) + isInboxDenied = await bo.contacts.isInboxDenied(alix.inboxId) + assert( + isInboxAllowed === true, + `isInboxAllowed2 should be true but was ${isInboxAllowed}` + ) + assert( + isInboxDenied === false, + `isInboxDenied2 should be false but was ${isInboxDenied}` + ) + + let isAddressAllowed = await bo.contacts.isAllowed(alix.address) + let isAddressDenied = await bo.contacts.isDenied(alix.address) + assert( + isAddressAllowed === true, + `isAddressAllowed should be true but was ${isAddressAllowed}` + ) + assert( + isAddressDenied === false, + `isAddressDenied should be false but was ${isAddressDenied}` + ) + + await bo.contacts.denyInboxes([alix.inboxId]) + + alixMember = (await boGroup.membersList()).find( + (member) => member.inboxId === alix.inboxId + ) + assert( + alixMember?.consentState === 'denied', + `alixMember should be denied but was ${alixMember?.consentState}` + ) + + isInboxAllowed = await bo.contacts.isInboxAllowed(alix.inboxId) + isInboxDenied = await bo.contacts.isInboxDenied(alix.inboxId) + assert( + isInboxAllowed === false, + `isInboxAllowed3 should be false but was ${isInboxAllowed}` + ) + assert( + isInboxDenied === true, + `isInboxDenied3 should be true but was ${isInboxDenied}` + ) + + await bo.contacts.allow([alix.address]) + + isAddressAllowed = await bo.contacts.isAllowed(alix.address) + isAddressDenied = await bo.contacts.isDenied(alix.address) + assert( + isAddressAllowed === true, + `isAddressAllowed2 should be true but was ${isAddressAllowed}` + ) + assert( + isAddressDenied === false, + `isAddressDenied2 should be false but was ${isAddressDenied}` + ) + isInboxAllowed = await bo.contacts.isInboxAllowed(alix.inboxId) + isInboxDenied = await bo.contacts.isInboxDenied(alix.inboxId) + assert( + isInboxAllowed === true, + `isInboxAllowed4 should be false but was ${isInboxAllowed}` + ) assert( - consentList[0].entryType === 'inbox_id', - `the message should have a type of inbox_id but was ${consentList[0].entryType}` + isInboxDenied === false, + `isInboxDenied4 should be true but was ${isInboxDenied}` ) return true