From 51acbaf840c9c185e730cf0922c36e5b32454365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noe=CC=81=20Malzieu?= Date: Thu, 15 Aug 2024 11:27:44 +0200 Subject: [PATCH 1/7] Add a test to stress test SDK parallelism --- example/src/tests/groupTests.ts | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index 78d6524a7..ef11ce3da 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -2107,6 +2107,50 @@ test('can create new installation without breaking group', async () => { return true }) +test('can create 10 groups in parallel', async () => { + const [client1, client2] = await createClients(2) + const groupsPromise: Promise[] = [] + + // Creating 9 groups in // works + for (let index = 0; index < 9; index++) { + groupsPromise.push(client1.conversations.newGroup([client2.address])) + } + console.log('Creating 9 groups...') + await Promise.race([ + Promise.all(groupsPromise), + new Promise((_, reject) => + setTimeout( + () => + reject( + new Error( + 'Creating 9 groups took more than 5 secons long to resolve' + ) + ), + 5000 + ) + ), + ]) + console.log('Created 9 groups') + + // Creating 10 groups in // never resolves + for (let index = 0; index < 10; index++) { + groupsPromise.push(client1.conversations.newGroup([client2.address])) + } + console.log('Creating 10 groups...') + await Promise.race([ + Promise.all(groupsPromise), + new Promise((_, reject) => + setTimeout( + () => reject(new Error('Creating 9 groups worked but not 10 groups')), + 5000 + ) + ), + ]) + console.log('Created 10 groups') + + return true +}) + // Commenting this out so it doesn't block people, but nice to have? // test('can stream messages for a long time', async () => { // const bo = await Client.createRandom({ env: 'local', enableV3: true }) From 68f8c138c36aa95ca699853b9c223c1d1bb321f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noe=CC=81=20Malzieu?= Date: Thu, 15 Aug 2024 14:27:00 +0200 Subject: [PATCH 2/7] Simplifying first test, adding a second one --- example/src/tests/groupTests.ts | 66 ++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index ef11ce3da..5ae25c013 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -2109,48 +2109,62 @@ test('can create new installation without breaking group', async () => { test('can create 10 groups in parallel', async () => { const [client1, client2] = await createClients(2) - const groupsPromise: Promise[] = [] + const groupsPromise9: Promise[] = [] // Creating 9 groups in // works for (let index = 0; index < 9; index++) { - groupsPromise.push(client1.conversations.newGroup([client2.address])) + groupsPromise9.push(client1.conversations.newGroup([client2.address])) } console.log('Creating 9 groups...') - await Promise.race([ - Promise.all(groupsPromise), - new Promise((_, reject) => - setTimeout( - () => - reject( - new Error( - 'Creating 9 groups took more than 5 secons long to resolve' - ) - ), - 5000 - ) - ), - ]) + await Promise.all(groupsPromise9) console.log('Created 9 groups') + const groupsPromise10: Promise[] = [] // Creating 10 groups in // never resolves for (let index = 0; index < 10; index++) { - groupsPromise.push(client1.conversations.newGroup([client2.address])) + groupsPromise10.push(client1.conversations.newGroup([client2.address])) } console.log('Creating 10 groups...') - await Promise.race([ - Promise.all(groupsPromise), - new Promise((_, reject) => - setTimeout( - () => reject(new Error('Creating 9 groups worked but not 10 groups')), - 5000 - ) - ), - ]) + await Promise.all(groupsPromise10) console.log('Created 10 groups') return true }) +test('can list many groups members in parallel', async () => { + const [client1, client2] = await createClients(2) + const groups: Group[] = [] + for (let index = 0; index < 50; index++) { + groups.push(await client1.conversations.newGroup([client2.address])) + console.log(`Created group ${index + 1}/${50}`) + } + try { + console.log('Listing 10 groups members...') + await Promise.all(groups.slice(0, 10).map((g) => g.members())) + console.log('Done listing 10 groups members!') + } catch (e) { + throw new Error(`Failed listing 10 groups members with ${e}`) + } + + try { + console.log('Listing 20 groups members...') + await Promise.all(groups.slice(0, 20).map((g) => g.members())) + console.log('Done listing 20 groups members!') + } catch (e) { + throw new Error(`Failed listing 20 groups members with ${e}`) + } + + try { + console.log('Listing 50 groups members...') + await Promise.all(groups.slice(0, 50).map((g) => g.members())) + console.log('Done listing 50 groups members!') + } catch (e) { + throw new Error(`Failed listing 50 groups members with ${e}`) + } + + return true +}) + // Commenting this out so it doesn't block people, but nice to have? // test('can stream messages for a long time', async () => { // const bo = await Client.createRandom({ env: 'local', enableV3: true }) From e21205b26ee8732ddb7c02aff7e5d63208b3d552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noe=CC=81=20Malzieu?= Date: Thu, 15 Aug 2024 15:08:04 +0200 Subject: [PATCH 3/7] Add a test showing the thread is locked even for other features --- example/src/tests/groupTests.ts | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index 5ae25c013..9a122c15c 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -2165,6 +2165,43 @@ test('can list many groups members in parallel', async () => { return true }) +test('whole RN thread should not be blocked during a pool timeout', async () => { + const [client1, client2] = await createClients(2) + const groupsPromise10: Promise[] = [] + // Creating 10 groups in // never resolves + for (let index = 0; index < 10; index++) { + groupsPromise10.push(client1.conversations.newGroup([client2.address])) + } + Promise.all(groupsPromise10).catch((e) => { + assert( + `${e}`.includes('timed out waiting for connection'), + `Unexpected error: ${e}` + ) + console.log('As expected, creating 10 groups resulted in a timeout') + }) + // Wait 1 sec, thread is blocked but it shouldn't + await new Promise((r) => setTimeout(r, 1000)) + console.log( + 'Calling canMessage which does not use the libxmtp database and should be fast' + ) + await Promise.race([ + client1.canMessage(client2.address), + new Promise((_, reject) => + setTimeout( + () => + reject( + new Error( + 'During the database lock, the rest of the RN thread should not be blocked' + ) + ), + 5000 + ) + ), + ]) + + return true +}) + // Commenting this out so it doesn't block people, but nice to have? // test('can stream messages for a long time', async () => { // const bo = await Client.createRandom({ env: 'local', enableV3: true }) From fb050c6afbca2fb793ed8450f0f4c5ab9703053b Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 19 Aug 2024 15:45:03 -0600 Subject: [PATCH 4/7] fix: fix the pool errors and get the tests passing --- android/build.gradle | 2 +- .../wrappers/GroupWrapper.kt | 3 ++- example/ios/Podfile.lock | 16 ++++++++-------- ios/Wrappers/GroupWrapper.swift | 1 + ios/XMTPReactNative.podspec | 2 +- src/lib/Group.ts | 18 ++++++------------ 6 files changed, 19 insertions(+), 23 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 74d53ae8e..87f42d112 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -98,7 +98,7 @@ repositories { dependencies { implementation project(':expo-modules-core') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" - implementation "org.xmtp:android:0.15.2" + implementation "org.xmtp:android:0.15.3" implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.facebook.react:react-native:0.71.3' implementation "com.daveanthonythomas.moshipack:moshipack:1.0.1" 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 d1e726fa4..998f479d7 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt @@ -13,11 +13,12 @@ class GroupWrapper { "clientAddress" to client.address, "id" to group.id, "createdAt" to group.createdAt.time, - "peerInboxIds" to group.peerInboxIds(), +// "members" to group.members().map { MemberWrapper.encode(it) }, "version" to "GROUP", "topic" to group.topic, "creatorInboxId" to group.creatorInboxId(), "isActive" to group.isActive(), + "addedByInboxId" to group.addedByInboxId(), "name" to group.name, "imageUrlSquare" to group.imageUrlSquare, "description" to group.description diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index da7545c93..ccf0b5f3d 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -56,7 +56,7 @@ PODS: - hermes-engine/Pre-built (= 0.71.14) - hermes-engine/Pre-built (0.71.14) - libevent (2.1.12) - - LibXMTP (0.5.7-beta0) + - LibXMTP (0.5.7-beta3) - Logging (1.0.0) - MessagePacker (0.4.7) - MMKV (1.3.9): @@ -449,16 +449,16 @@ PODS: - GenericJSON (~> 2.0) - Logging (~> 1.0.0) - secp256k1.swift (~> 0.1) - - XMTP (0.14.1): + - XMTP (0.14.6): - Connect-Swift (= 0.12.0) - GzipSwift - - LibXMTP (= 0.5.7-beta0) + - LibXMTP (= 0.5.7-beta3) - web3.swift - XMTPReactNative (0.1.0): - ExpoModulesCore - MessagePacker - secp256k1.swift - - XMTP (= 0.14.1) + - XMTP (= 0.14.6) - Yoga (1.14.0) DEPENDENCIES: @@ -711,7 +711,7 @@ SPEC CHECKSUMS: GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa hermes-engine: d7cc127932c89c53374452d6f93473f1970d8e88 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 - LibXMTP: d80a1a7c9c748fba81d80b95c62fd075a89224a2 + LibXMTP: b6b930f9d2394a560d7f83b02be6ccd789472422 Logging: 9ef4ecb546ad3169398d5a723bc9bea1c46bef26 MessagePacker: ab2fe250e86ea7aedd1a9ee47a37083edd41fd02 MMKV: 817ba1eea17421547e01e087285606eb270a8dcb @@ -763,10 +763,10 @@ SPEC CHECKSUMS: secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634 SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1 web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959 - XMTP: 593cf8bf4e9dc0737a6efc90a0e51fe7602101fd - XMTPReactNative: 1ca02155e4583791c8c99a244206ecf8e057abd2 + XMTP: 0f36b44b3922a5933e3487bf09671655e05dcb8d + XMTPReactNative: 71910c6588e526d85583c1f7aeb6c83816747aea Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9 -PODFILE CHECKSUM: 0e6fe50018f34e575d38dc6a1fdf1f99c9596cdd +PODFILE CHECKSUM: 95d6ace79946933ecf80684613842ee553dd76a2 COCOAPODS: 1.15.2 diff --git a/ios/Wrappers/GroupWrapper.swift b/ios/Wrappers/GroupWrapper.swift index 7461c22ee..023c8eb1b 100644 --- a/ios/Wrappers/GroupWrapper.swift +++ b/ios/Wrappers/GroupWrapper.swift @@ -20,6 +20,7 @@ struct GroupWrapper { "topic": group.topic, "creatorInboxId": try group.creatorInboxId(), "isActive": try group.isActive(), + "addedByInboxId": try group.addedByInboxId(), "name": try group.groupName(), "imageUrlSquare": try group.groupImageUrlSquare(), "description": try group.groupDescription() diff --git a/ios/XMTPReactNative.podspec b/ios/XMTPReactNative.podspec index 60724ec78..4d2a7f472 100644 --- a/ios/XMTPReactNative.podspec +++ b/ios/XMTPReactNative.podspec @@ -26,5 +26,5 @@ Pod::Spec.new do |s| s.source_files = "**/*.{h,m,swift}" s.dependency 'secp256k1.swift' s.dependency "MessagePacker" - s.dependency "XMTP", "= 0.14.5" + s.dependency "XMTP", "= 0.14.6" end diff --git a/src/lib/Group.ts b/src/lib/Group.ts index 9704cac8e..2b8f51af6 100644 --- a/src/lib/Group.ts +++ b/src/lib/Group.ts @@ -18,11 +18,12 @@ export type PermissionUpdateOption = 'allow' | 'deny' | 'admin' | 'super_admin' export interface GroupParams { id: string createdAt: number - peerInboxIds: InboxId[] + // members: Member[] creatorInboxId: InboxId topic: string name: string isActive: boolean + addedByInboxId: InboxId imageUrlSquare: string description: string } @@ -34,12 +35,13 @@ export class Group< client: XMTP.Client id: string createdAt: number - peerInboxIds: InboxId[] + // members: Member[] version = ConversationVersion.GROUP topic: string creatorInboxId: InboxId name: string isGroupActive: boolean + addedByInboxId: InboxId imageUrlSquare: string description: string // pinnedFrameUrl: string @@ -48,11 +50,12 @@ export class Group< this.client = client this.id = params.id this.createdAt = params.createdAt - this.peerInboxIds = params.peerInboxIds + // this.members = params.members this.topic = params.topic this.creatorInboxId = params.creatorInboxId this.name = params.name this.isGroupActive = params.isActive + this.addedByInboxId = params.addedByInboxId this.imageUrlSquare = params.imageUrlSquare this.description = params.description // this.pinnedFrameUrl = params.pinnedFrameUrl @@ -373,15 +376,6 @@ export class Group< return XMTP.isGroupActive(this.client.inboxId, this.id) } - /** - * Returns the inbox id that added you to the group. - * To get the latest added by inbox id from the network, call sync() first. - * @returns {Promise} A Promise that resolves to the inbox id that added you to the group. - */ - async addedByInboxId(): Promise { - return XMTP.addedByInboxId(this.client.inboxId, this.id) - } - /** * * @param inboxId From 7f685efb97e8f5ff8427d8645eb5ed289bc2c06f Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 19 Aug 2024 15:49:01 -0600 Subject: [PATCH 5/7] fix up the test --- example/src/tests/groupTests.ts | 84 ++------------------------------- 1 file changed, 4 insertions(+), 80 deletions(-) diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index 9a122c15c..19b6a694b 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -439,7 +439,7 @@ test('who added me to a group', async () => { await boClient.conversations.syncGroups() const boGroup = (await boClient.conversations.listGroups())[0] - const addedByInboxId = await boGroup.addedByInboxId() + const addedByInboxId = await boGroup.addedByInboxId assert( addedByInboxId === alixClient.inboxId, @@ -2107,98 +2107,22 @@ test('can create new installation without breaking group', async () => { return true }) -test('can create 10 groups in parallel', async () => { - const [client1, client2] = await createClients(2) - const groupsPromise9: Promise[] = [] - - // Creating 9 groups in // works - for (let index = 0; index < 9; index++) { - groupsPromise9.push(client1.conversations.newGroup([client2.address])) - } - console.log('Creating 9 groups...') - await Promise.all(groupsPromise9) - console.log('Created 9 groups') - - const groupsPromise10: Promise[] = [] - // Creating 10 groups in // never resolves - for (let index = 0; index < 10; index++) { - groupsPromise10.push(client1.conversations.newGroup([client2.address])) - } - console.log('Creating 10 groups...') - await Promise.all(groupsPromise10) - console.log('Created 10 groups') - - return true -}) - test('can list many groups members in parallel', async () => { - const [client1, client2] = await createClients(2) - const groups: Group[] = [] - for (let index = 0; index < 50; index++) { - groups.push(await client1.conversations.newGroup([client2.address])) - console.log(`Created group ${index + 1}/${50}`) - } + const [alix, bo] = await createClients(2) + const groups: Group[] = await createGroups(alix, [bo], 20, 0) + try { - console.log('Listing 10 groups members...') await Promise.all(groups.slice(0, 10).map((g) => g.members())) - console.log('Done listing 10 groups members!') } catch (e) { throw new Error(`Failed listing 10 groups members with ${e}`) } try { - console.log('Listing 20 groups members...') await Promise.all(groups.slice(0, 20).map((g) => g.members())) - console.log('Done listing 20 groups members!') } catch (e) { throw new Error(`Failed listing 20 groups members with ${e}`) } - try { - console.log('Listing 50 groups members...') - await Promise.all(groups.slice(0, 50).map((g) => g.members())) - console.log('Done listing 50 groups members!') - } catch (e) { - throw new Error(`Failed listing 50 groups members with ${e}`) - } - - return true -}) - -test('whole RN thread should not be blocked during a pool timeout', async () => { - const [client1, client2] = await createClients(2) - const groupsPromise10: Promise[] = [] - // Creating 10 groups in // never resolves - for (let index = 0; index < 10; index++) { - groupsPromise10.push(client1.conversations.newGroup([client2.address])) - } - Promise.all(groupsPromise10).catch((e) => { - assert( - `${e}`.includes('timed out waiting for connection'), - `Unexpected error: ${e}` - ) - console.log('As expected, creating 10 groups resulted in a timeout') - }) - // Wait 1 sec, thread is blocked but it shouldn't - await new Promise((r) => setTimeout(r, 1000)) - console.log( - 'Calling canMessage which does not use the libxmtp database and should be fast' - ) - await Promise.race([ - client1.canMessage(client2.address), - new Promise((_, reject) => - setTimeout( - () => - reject( - new Error( - 'During the database lock, the rest of the RN thread should not be blocked' - ) - ), - 5000 - ) - ), - ]) - return true }) From ed2fa9753de2e307efd9e6bc21935835be4ff9e2 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 19 Aug 2024 16:34:37 -0600 Subject: [PATCH 6/7] add support for getting members from the group and not loading again --- .../wrappers/GroupWrapper.kt | 2 +- example/src/tests/groupTests.ts | 29 ++------- ios/Wrappers/GroupWrapper.swift | 2 +- src/index.ts | 64 ++++++++++++------- src/lib/Group.ts | 13 ++-- 5 files changed, 58 insertions(+), 52 deletions(-) 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 998f479d7..78d82ba3a 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt @@ -13,7 +13,7 @@ class GroupWrapper { "clientAddress" to client.address, "id" to group.id, "createdAt" to group.createdAt.time, -// "members" to group.members().map { MemberWrapper.encode(it) }, + "members" to group.members().map { MemberWrapper.encode(it) }, "version" to "GROUP", "topic" to group.topic, "creatorInboxId" to group.creatorInboxId(), diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index 19b6a694b..6f4ab050e 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -452,7 +452,7 @@ test('can get members of a group', async () => { const [alixClient, boClient] = await createClients(2) const group = await alixClient.conversations.newGroup([boClient.address]) - const members = await group.members() + const members = group.members assert(members.length === 2, `Should be 2 members but was ${members.length}`) @@ -514,10 +514,7 @@ test('can message in a group', async () => { if (memberInboxIds.length !== 3) { throw new Error('num group members should be 3') } - const peerInboxIds = await alixGroup.peerInboxIds - if (peerInboxIds.length !== 2) { - throw new Error('num peer group members should be 2') - } + if ( !( memberInboxIds.includes(alixClient.inboxId) && @@ -528,15 +525,6 @@ test('can message in a group', async () => { throw new Error('missing address') } - if ( - !( - peerInboxIds.includes(boClient.inboxId) && - peerInboxIds.includes(caroClient.inboxId) - ) - ) { - throw new Error('should include self') - } - // alix can send messages await alixGroup.send('hello, world') await alixGroup.send('gm') @@ -2077,13 +2065,10 @@ test('can create new installation without breaking group', async () => { await client1Group?.sync() await client2Group?.sync() - assert( - (await client1Group?.members())?.length === 2, - `client 1 should see 2 members` - ) + assert(client1Group?.members?.length === 2, `client 1 should see 2 members`) assert( - (await client2Group?.members())?.length === 2, + (await client2Group?.membersList())?.length === 2, `client 2 should see 2 members` ) @@ -2100,7 +2085,7 @@ test('can create new installation without breaking group', async () => { await client1Group?.send('This message will break the group') assert( - (await client1Group?.members())?.length === 2, + client1Group?.members?.length === 2, `client 1 should still see the 2 members` ) @@ -2112,13 +2097,13 @@ test('can list many groups members in parallel', async () => { const groups: Group[] = await createGroups(alix, [bo], 20, 0) try { - await Promise.all(groups.slice(0, 10).map((g) => g.members())) + await Promise.all(groups.slice(0, 10).map((g) => g.membersList())) } catch (e) { throw new Error(`Failed listing 10 groups members with ${e}`) } try { - await Promise.all(groups.slice(0, 20).map((g) => g.members())) + await Promise.all(groups.slice(0, 20).map((g) => g.membersList())) } catch (e) { throw new Error(`Failed listing 20 groups members with ${e}`) } diff --git a/ios/Wrappers/GroupWrapper.swift b/ios/Wrappers/GroupWrapper.swift index 023c8eb1b..bab4d2082 100644 --- a/ios/Wrappers/GroupWrapper.swift +++ b/ios/Wrappers/GroupWrapper.swift @@ -15,7 +15,7 @@ struct GroupWrapper { "clientAddress": client.address, "id": group.id, "createdAt": UInt64(group.createdAt.timeIntervalSince1970 * 1000), - "peerInboxIds": try group.peerInboxIds, + "members": try group.members.compactMap { member in return try MemberWrapper.encode(member) }, "version": "GROUP", "topic": group.topic, "creatorInboxId": try group.creatorInboxId(), diff --git a/src/index.ts b/src/index.ts index c7eb2b4e3..9f27ef314 100644 --- a/src/index.ts +++ b/src/index.ts @@ -182,17 +182,19 @@ export async function createGroup< description, pinnedFrameUrl, } - return new Group( - client, - JSON.parse( - await XMTPModule.createGroup( - client.inboxId, - peerAddresses, - permissionLevel, - JSON.stringify(options) - ) + const group = JSON.parse( + await XMTPModule.createGroup( + client.inboxId, + peerAddresses, + permissionLevel, + JSON.stringify(options) ) ) + + const members = group['members'].map((mem: string) => { + return Member.from(mem) + }) + return new Group(client, group, members) } export async function createGroupCustomPermissions< @@ -212,24 +214,29 @@ export async function createGroupCustomPermissions< description, pinnedFrameUrl, } - return new Group( - client, - JSON.parse( - await XMTPModule.createGroupCustomPermissions( - client.inboxId, - peerAddresses, - JSON.stringify(permissionPolicySet), - JSON.stringify(options) - ) + const group = JSON.parse( + await XMTPModule.createGroupCustomPermissions( + client.inboxId, + peerAddresses, + JSON.stringify(permissionPolicySet), + JSON.stringify(options) ) ) + const members = group['members'].map((mem: string) => { + return Member.from(mem) + }) + return new Group(client, group, members) } export async function listGroups< ContentTypes extends DefaultContentTypes = DefaultContentTypes, >(client: Client): Promise[]> { return (await XMTPModule.listGroups(client.inboxId)).map((json: string) => { - return new Group(client, JSON.parse(json)) + const group = JSON.parse(json) + const members = group['members'].map((mem: string) => { + return Member.from(mem) + }) + return new Group(client, group, members) }) } @@ -309,8 +316,12 @@ export async function findGroup< client: Client, groupId: string ): Promise | undefined> { - const group = await XMTPModule.findGroup(client.inboxId, groupId) - return new Group(client, JSON.parse(group)) + const json = await XMTPModule.findGroup(client.inboxId, groupId) + const group = JSON.parse(json) + const members = group['members'].map((mem: string) => { + return Member.from(mem) + }) + return new Group(client, group, members) } export async function findV3Message< @@ -567,7 +578,10 @@ export async function listAll< return list.map((json: string) => { const jsonObj = JSON.parse(json) if (jsonObj.version === ConversationVersion.GROUP) { - return new Group(client, jsonObj) + const members = jsonObj.members.map((mem: string) => { + return Member.from(mem) + }) + return new Group(client, jsonObj, members) } else { return new Conversation(client, jsonObj) } @@ -1146,7 +1160,11 @@ export async function processWelcomeMessage< client.inboxId, encryptedMessage ) - return new Group(client, JSON.parse(json)) + const group = JSON.parse(json) + const members = group['members'].map((mem: string) => { + return Member.from(mem) + }) + return new Group(client, group, members) } export async function exportNativeLogs() { diff --git a/src/lib/Group.ts b/src/lib/Group.ts index 2b8f51af6..459bfd6d2 100644 --- a/src/lib/Group.ts +++ b/src/lib/Group.ts @@ -18,7 +18,6 @@ export type PermissionUpdateOption = 'allow' | 'deny' | 'admin' | 'super_admin' export interface GroupParams { id: string createdAt: number - // members: Member[] creatorInboxId: InboxId topic: string name: string @@ -35,7 +34,7 @@ export class Group< client: XMTP.Client id: string createdAt: number - // members: Member[] + members: Member[] version = ConversationVersion.GROUP topic: string creatorInboxId: InboxId @@ -46,11 +45,15 @@ export class Group< description: string // pinnedFrameUrl: string - constructor(client: XMTP.Client, params: GroupParams) { + constructor( + client: XMTP.Client, + params: GroupParams, + members: Member[] + ) { this.client = client this.id = params.id this.createdAt = params.createdAt - // this.members = params.members + this.members = members this.topic = params.topic this.creatorInboxId = params.creatorInboxId this.name = params.name @@ -628,7 +631,7 @@ export class Group< * @returns {Promise} A Promise that resolves to an array of Member objects. * To get the latest member list from the network, call sync() first. */ - async members(): Promise { + async membersList(): Promise { return await XMTP.listGroupMembers(this.client.inboxId, this.id) } } From 71867922aa1e4bf4fe4ecf643f2da10de72d74c5 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Mon, 19 Aug 2024 17:01:59 -0600 Subject: [PATCH 7/7] get all the tests passing --- example/src/tests/groupTests.ts | 11 +++++------ src/lib/Conversations.ts | 14 ++++++++++++-- src/lib/Group.ts | 1 + 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index 6f4ab050e..4df83a94e 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -950,6 +950,8 @@ test('can stream groups', async () => { throw Error('Unexpected num groups (should be 1): ' + groups.length) } + assert(groups[0].members.length == 2, "should be 2") + // bo creates a group with alix so a stream callback is fired // eslint-disable-next-line @typescript-eslint/no-unused-vars const boGroup = await boClient.conversations.newGroup([alixClient.address]) @@ -2035,12 +2037,9 @@ test('can create new installation without breaking group', async () => { 233, 120, 198, 96, 154, 65, 132, 17, 132, 96, 250, 40, 103, 35, 125, 64, 166, 83, 208, 224, 254, 44, 205, 227, 175, 49, 234, 129, 74, 252, 135, 145, ]) - const wallet1 = new Wallet( - '0xc54c62dd3ad018ef94f20f0722cae33919e65270ad74f2d1794291088800f788' - ) - const wallet2 = new Wallet( - '0x8d40c1c40473975cc6bbdc0465e70cc2e98f45f3c3474ca9b809caa9c4f53c0b' - ) + const wallet1 = Wallet.createRandom() + const wallet2 = Wallet.createRandom() + const client1 = await Client.create(wallet1, { env: 'local', appVersion: 'Testing/0.0.0', diff --git a/src/lib/Conversations.ts b/src/lib/Conversations.ts index 693df85ec..8d4cd8dce 100644 --- a/src/lib/Conversations.ts +++ b/src/lib/Conversations.ts @@ -8,6 +8,7 @@ import { } from './ConversationContainer' import { DecodedMessage } from './DecodedMessage' import { Group, GroupParams } from './Group' +import { Member } from './Member' import { CreateGroupOptions } from './types/CreateGroupOptions' import { EventTypes } from './types/EventTypes' import { PermissionPolicySet } from './types/PermissionPolicySet' @@ -149,7 +150,10 @@ export default class Conversations< return } this.known[group.id] = true - await callback(new Group(this.client, group)) + const members = group['members'].map((mem: string) => { + return Member.from(mem) + }) + await callback(new Group(this.client, group, members)) } ) this.subscriptions[EventTypes.Group] = groupsSubscription @@ -286,10 +290,16 @@ export default class Conversations< this.known[conversationContainer.topic] = true if (conversationContainer.version === ConversationVersion.GROUP) { + const members = conversationContainer['members'].map( + (mem: string) => { + return Member.from(mem) + } + ) return await callback( new Group( this.client, - conversationContainer as unknown as GroupParams + conversationContainer as unknown as GroupParams, + members ) ) } else { diff --git a/src/lib/Group.ts b/src/lib/Group.ts index 459bfd6d2..92621e4cf 100644 --- a/src/lib/Group.ts +++ b/src/lib/Group.ts @@ -18,6 +18,7 @@ export type PermissionUpdateOption = 'allow' | 'deny' | 'admin' | 'super_admin' export interface GroupParams { id: string createdAt: number + members: string[] creatorInboxId: InboxId topic: string name: string