diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt
index 9aef8223a..b7479673d 100644
--- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt
+++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt
@@ -172,14 +172,12 @@ class XMTPModule : Module() {
dbEncryptionKey.foldIndexed(ByteArray(dbEncryptionKey.size)) { i, a, v ->
a.apply { set(i, v.toByte()) }
}
-
val historySyncUrl = authOptions.historySyncUrl
?: when (authOptions.environment) {
"production" -> "https://message-history.production.ephemera.network/"
"local" -> "http://0.0.0.0:5558"
else -> "https://message-history.dev.ephemera.network/"
}
-
return ClientOptions(
api = apiEnvironments(authOptions.environment, authOptions.appVersion),
preAuthenticateToInboxCallback = preAuthenticateToInboxCallback,
@@ -533,9 +531,9 @@ class XMTPModule : Module() {
}
}
- AsyncFunction("findDm") Coroutine { inboxId: String, peerAddress: String ->
+ AsyncFunction("findDmByAddress") Coroutine { inboxId: String, peerAddress: String ->
withContext(Dispatchers.IO) {
- logV("findDm")
+ logV("findDmByAddress")
val client = clients[inboxId] ?: throw XMTPException("No client")
val dm = client.findDm(peerAddress)
dm?.let {
diff --git a/example/src/LaunchScreen.tsx b/example/src/LaunchScreen.tsx
index 8e9ac0f1a..6038d2d1f 100644
--- a/example/src/LaunchScreen.tsx
+++ b/example/src/LaunchScreen.tsx
@@ -173,16 +173,6 @@ export default function LaunchScreen(
}
/>
-
- Enable Groups:
-
-
External Wallet:
diff --git a/example/src/TestScreen.tsx b/example/src/TestScreen.tsx
index 275bded58..f8eded841 100644
--- a/example/src/TestScreen.tsx
+++ b/example/src/TestScreen.tsx
@@ -2,6 +2,7 @@ import { useRoute } from '@react-navigation/native'
import React, { useEffect, useState } from 'react'
import { View, Text, Button, ScrollView } from 'react-native'
+import { clientTests } from './tests/clientTests'
import { conversationTests } from './tests/conversationTests'
import { dmTests } from './tests/dmTests'
import { groupPerformanceTests } from './tests/groupPerformanceTests'
@@ -106,9 +107,10 @@ function TestView({
export enum TestCategory {
all = 'all',
- conversation = 'conversation',
- group = 'group',
+ client = 'client',
dm = 'dm',
+ group = 'group',
+ conversation = 'conversation',
restartStreans = 'restartStreams',
groupPermissions = 'groupPermissions',
groupPerformance = 'groupPerformance',
@@ -121,6 +123,7 @@ export default function TestScreen(): JSX.Element {
testSelection: TestCategory
}
const allTests = [
+ ...clientTests,
...dmTests,
...groupTests,
...conversationTests,
@@ -133,6 +136,10 @@ export default function TestScreen(): JSX.Element {
activeTests = allTests
title = 'All Unit Tests'
break
+ case TestCategory.client:
+ activeTests = clientTests
+ title = 'Client Unit Tests'
+ break
case TestCategory.dm:
activeTests = dmTests
title = 'Dm Unit Tests'
diff --git a/example/src/tests/clientTests.ts b/example/src/tests/clientTests.ts
new file mode 100644
index 000000000..a88831b3f
--- /dev/null
+++ b/example/src/tests/clientTests.ts
@@ -0,0 +1,283 @@
+import { Test, assert, createClients, delayToPropogate } from './test-utils'
+import { Client, Conversation, ConversationId, ConversationVersion } from '../../../src/index'
+import { Wallet } from 'ethers'
+import RNFS from 'react-native-fs'
+
+export const clientTests: Test[] = []
+let counter = 1
+function test(name: string, perform: () => Promise) {
+ clientTests.push({
+ name: String(counter++) + '. ' + name,
+ run: perform,
+ })
+}
+
+
+test('can make a client', async () => {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const keyBytes = new Uint8Array([
+ 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 client = await Client.createRandom({
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: keyBytes,
+ })
+
+ const inboxId = await Client.getOrCreateInboxId(client.address, 'local')
+
+ assert(
+ client.inboxId === inboxId,
+ `inboxIds should match but were ${client.inboxId} and ${inboxId}`
+ )
+ return true
+})
+
+test('can revoke all other installations', async () => {
+ const keyBytes = new Uint8Array([
+ 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 alixWallet = Wallet.createRandom()
+
+ // create a v3 client
+ const alix = await Client.create(alixWallet, {
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: keyBytes,
+ })
+
+ await alix.deleteLocalDatabase()
+
+ const alix2 = await Client.create(alixWallet, {
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: keyBytes,
+ })
+
+ const alix2Build = await Client.build(alix2.address, {
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: keyBytes,
+ })
+
+ await alix2.deleteLocalDatabase()
+
+ const alix3 = await Client.create(alixWallet, {
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: keyBytes,
+ })
+
+ const inboxState2 = await alix3.inboxState(true)
+ assert(
+ inboxState2.installations.length === 3,
+ `installations length should be 3 but was ${inboxState2.installations.length}`
+ )
+
+ await alix3.revokeAllOtherInstallations(alixWallet)
+
+ const inboxState3 = await alix3.inboxState(true)
+ assert(
+ inboxState3.installations.length === 1,
+ `installations length should be 1 but was ${inboxState3.installations.length}`
+ )
+
+ assert(
+ inboxState3.installations[0].createdAt !== undefined,
+ `installations createdAt should not be undefined`
+ )
+ return true
+})
+
+test('calls preAuthenticateToInboxCallback when supplied', async () => {
+ let isCallbackCalled = 0
+ let isPreAuthCalled = false
+ const preAuthenticateToInboxCallback = () => {
+ isCallbackCalled++
+ isPreAuthCalled = true
+ }
+
+ const keyBytes = new Uint8Array([
+ 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,
+ ])
+
+ await Client.createRandom({
+ env: 'local',
+ preAuthenticateToInboxCallback,
+ dbEncryptionKey: keyBytes,
+ })
+
+ assert(
+ isCallbackCalled === 1,
+ `callback should be called 1 times but was ${isCallbackCalled}`
+ )
+
+ if (!isPreAuthCalled) {
+ throw new Error('preAuthenticateToInboxCallback not called')
+ }
+
+ return true
+})
+
+test('can delete a local database', async () => {
+ let [client, anotherClient] = await createClients(2)
+
+ await client.conversations.newGroup([anotherClient.address])
+ await client.conversations.sync()
+ assert(
+ (await client.conversations.listGroups()).length === 1,
+ `should have a group size of 1 but was ${
+ (await client.conversations.listGroups()).length
+ }`
+ )
+
+ assert(
+ client.dbPath !== '',
+ `client dbPath should be set but was ${client.dbPath}`
+ )
+ await client.deleteLocalDatabase()
+ client = await Client.createRandom({
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: new Uint8Array([
+ 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,
+ ]),
+ })
+ await client.conversations.sync()
+ assert(
+ (await client.conversations.listGroups()).length === 0,
+ `should have a group size of 0 but was ${
+ (await client.conversations.listGroups()).length
+ }`
+ )
+
+ return true
+})
+
+test('can make a client with encryption key and database directory', async () => {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const dbDirPath = `${RNFS.DocumentDirectoryPath}/xmtp_db`
+ const directoryExists = await RNFS.exists(dbDirPath)
+ if (!directoryExists) {
+ await RNFS.mkdir(dbDirPath)
+ }
+ const key = new Uint8Array([
+ 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 client = await Client.createRandom({
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: key,
+ dbDirectory: dbDirPath,
+ })
+
+ const anotherClient = await Client.createRandom({
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: key,
+ })
+
+ await client.conversations.newGroup([anotherClient.address])
+ assert(
+ (await client.conversations.listGroups()).length === 1,
+ `should have a group size of 1 but was ${
+ (await client.conversations.listGroups()).length
+ }`
+ )
+
+ const clientFromBundle = await Client.build(client.address, {
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: key,
+ dbDirectory: dbDirPath,
+ })
+
+ assert(
+ clientFromBundle.address === client.address,
+ `clients dont match ${client.address} and ${clientFromBundle.address}`
+ )
+
+ assert(
+ (await clientFromBundle.conversations.listGroups()).length === 1,
+ `should have a group size of 1 but was ${
+ (await clientFromBundle.conversations.listGroups()).length
+ }`
+ )
+ return true
+})
+
+test('can drop a local database', async () => {
+ const [client, anotherClient] = await createClients(2)
+
+ const group = await client.conversations.newGroup([anotherClient.address])
+ await client.conversations.sync()
+ assert(
+ (await client.conversations.listGroups()).length === 1,
+ `should have a group size of 1 but was ${
+ (await client.conversations.listGroups()).length
+ }`
+ )
+
+ await client.dropLocalDatabaseConnection()
+
+ try {
+ await group.send('hi')
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ } catch (error) {
+ await client.reconnectLocalDatabase()
+ await group.send('hi')
+ return true
+ }
+ throw new Error('should throw when local database not connected')
+})
+
+test('can drop client from memory', async () => {
+ const [client, anotherClient] = await createClients(2)
+ await client.dropLocalDatabaseConnection()
+ await anotherClient.dropLocalDatabaseConnection()
+
+ await client.reconnectLocalDatabase()
+ await Client.dropClient(anotherClient.inboxId)
+ try {
+ await anotherClient.reconnectLocalDatabase()
+ return false
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ } catch (error) {
+ // We cannot reconnect anotherClient because it was successfully dropped
+ return true
+ }
+})
+
+test('can get a inboxId from an address', async () => {
+ const [alix, bo] = await createClients(2)
+
+ const boInboxId = await alix.findInboxIdFromAddress(bo.address)
+ assert(boInboxId === bo.inboxId, `${boInboxId} should match ${bo.inboxId}`)
+ return true
+})
+
+test('production client creation does not error', async () => {
+ const key = new Uint8Array([
+ 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,
+ ])
+
+ try {
+ await Client.createRandom({
+ env: 'production',
+ appVersion: 'Testing/0.0.0',
+ dbEncryptionKey: key,
+ })
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ } catch (error) {
+ throw error
+ }
+ return true
+})
diff --git a/example/src/tests/conversationTests.ts b/example/src/tests/conversationTests.ts
index 11b896e20..c9fdf552c 100644
--- a/example/src/tests/conversationTests.ts
+++ b/example/src/tests/conversationTests.ts
@@ -276,3 +276,139 @@ test('can stream all groups and conversations', async () => {
return true
})
+
+test('can streamAll from multiple clients', async () => {
+ const [alix, bo, caro] = await createClients(3)
+
+ // Setup stream alls
+ const allBoConversations: any[] = []
+ const allAliConversations: any[] = []
+
+ await bo.conversations.stream(async (conversation) => {
+ allBoConversations.push(conversation)
+ })
+ await alix.conversations.stream(async (conversation) => {
+ allAliConversations.push(conversation)
+ })
+
+ // Start Caro starts a new conversation.
+ await caro.conversations.newConversation(alix.address)
+ await delayToPropogate()
+ if (allBoConversations.length !== 0) {
+ throw Error(
+ 'Unexpected all conversations count for Bo ' +
+ allBoConversations.length +
+ ' and Alix had ' +
+ allAliConversations.length
+ )
+ }
+ if (allAliConversations.length !== 1) {
+ throw Error(
+ 'Unexpected all conversations count ' + allAliConversations.length
+ )
+ }
+ return true
+})
+
+test('can streamAll from multiple clients - swapped orderring', async () => {
+ const [alix, bo, caro] = await createClients(3)
+
+ // Setup stream alls
+ const allBoConversations: any[] = []
+ const allAliConversations: any[] = []
+
+ await alix.conversations.stream(async (conversation) => {
+ allAliConversations.push(conversation)
+ })
+
+ await bo.conversations.stream(async (conversation) => {
+ allBoConversations.push(conversation)
+ })
+
+ // Start Caro starts a new conversation.
+ await caro.conversations.newConversation(alix.address)
+ await delayToPropogate()
+ if (allBoConversations.length !== 0) {
+ throw Error(
+ 'Unexpected all conversations count for Bo ' +
+ allBoConversations.length +
+ ' and Alix had ' +
+ allAliConversations.length
+ )
+ }
+ if (allAliConversations.length !== 1) {
+ throw Error(
+ 'Unexpected all conversations count ' + allAliConversations.length
+ )
+ }
+ return true
+})
+
+test('can streamAllMessages from multiple clients', async () => {
+ const [alix, bo, caro] = await createClients(3)
+
+ // Setup stream
+ const allBoMessages: any[] = []
+ const allAliMessages: any[] = []
+
+ await bo.conversations.streamAllMessages(async (conversation) => {
+ allBoMessages.push(conversation)
+ })
+ await alix.conversations.streamAllMessages(async (conversation) => {
+ allAliMessages.push(conversation)
+ })
+
+ // Start Caro starts a new conversation.
+ const caroConversation = await caro.conversations.newConversation(
+ alix.address
+ )
+ await caroConversation.send({ text: `Message` })
+ await delayToPropogate()
+ if (allBoMessages.length !== 0) {
+ throw Error('Unexpected all messages count for Bo ' + allBoMessages.length)
+ }
+
+ if (allAliMessages.length !== 1) {
+ throw Error(
+ 'Unexpected all conversations count for Ali ' + allAliMessages.length
+ )
+ }
+
+ return true
+})
+
+test('can streamAllMessages from multiple clients - swapped', async () => {
+ const [alix, bo, caro] = await createClients(3)
+
+ // Setup stream
+ const allBoMessages: any[] = []
+ const allAliMessages: any[] = []
+ const caroGroup = await caro.conversations.newGroup([alix.address])
+
+ await alix.conversations.streamAllMessages(async (conversation) => {
+ allAliMessages.push(conversation)
+ })
+ await bo.conversations.streamAllMessages(async (conversation) => {
+ allBoMessages.push(conversation)
+ })
+
+ // Start Caro starts a new conversation.
+ const caroConvo = await caro.conversations.newConversation(alix.address)
+ await delayToPropogate()
+ await caroConvo.send({ text: `Message` })
+ await caroGroup.send({ text: `Message` })
+ await delayToPropogate()
+ if (allBoMessages.length !== 0) {
+ throw Error(
+ 'Unexpected all conversations count for Bo ' + allBoMessages.length
+ )
+ }
+
+ if (allAliMessages.length !== 2) {
+ throw Error(
+ 'Unexpected all conversations count for Ali ' + allAliMessages.length
+ )
+ }
+
+ return true
+})
diff --git a/example/src/tests/createdAtTests.ts b/example/src/tests/createdAtTests.ts
index cdcbed6b8..b1e7573c6 100644
--- a/example/src/tests/createdAtTests.ts
+++ b/example/src/tests/createdAtTests.ts
@@ -27,7 +27,7 @@ test('group createdAt matches listGroups', async () => {
const boGroup = await bo.conversations.newGroup([alix.address])
// Fetch groups using listGroups method
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
const alixGroups = await alix.conversations.listGroups()
const first = 0
@@ -73,7 +73,7 @@ test('group createdAt matches listAll', async () => {
const boGroup = await bo.conversations.newGroup([alix.address])
// Fetch groups using listGroups method
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
const alixGroups = await alix.conversations.list()
assert(alixGroups.length === 2, 'alix should have two groups')
diff --git a/example/src/tests/groupPerformanceTests.ts b/example/src/tests/groupPerformanceTests.ts
index cc70d39f4..92a2fceac 100644
--- a/example/src/tests/groupPerformanceTests.ts
+++ b/example/src/tests/groupPerformanceTests.ts
@@ -82,7 +82,7 @@ async function beforeAll(
test('test compare V3 dms', async () => {
await beforeAll(0, 0, 50, true)
let start = Date.now()
- await alixClient.conversations.syncConversations()
+ await alixClient.conversations.sync()
let end = Date.now()
console.log(`Davon synced ${50} Dms in ${end - start}ms`)
@@ -94,7 +94,7 @@ test('test compare V3 dms', async () => {
await createDms(alixClient, await createClients(5), 1)
start = Date.now()
- await alixClient.conversations.syncConversations()
+ await alixClient.conversations.sync()
end = Date.now()
console.log(`Davon synced ${dms.length} Dms in ${end - start}ms`)
@@ -140,7 +140,7 @@ test('testing large group listings with ordering', async () => {
)
start = Date.now()
- await alixClient.conversations.syncConversations()
+ await alixClient.conversations.sync()
end = Date.now()
console.log(`Alix synced ${groups.length} groups in ${end - start}ms`)
assert(
@@ -149,7 +149,7 @@ test('testing large group listings with ordering', async () => {
)
start = Date.now()
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
end = Date.now()
console.log(`Bo synced ${groups.length} groups in ${end - start}ms`)
@@ -201,7 +201,7 @@ test('testing large group listings', async () => {
)
start = Date.now()
- await alixClient.conversations.syncConversations()
+ await alixClient.conversations.sync()
end = Date.now()
console.log(`Alix synced ${groups.length} groups in ${end - start}ms`)
assert(
@@ -210,7 +210,7 @@ test('testing large group listings', async () => {
)
start = Date.now()
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
end = Date.now()
console.log(`Bo synced ${groups.length} groups in ${end - start}ms`)
assert(
@@ -252,7 +252,7 @@ test('testing large message listings', async () => {
'syncing 2000 self messages should take less than a .1 second'
)
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroup = await boClient.conversations.findGroup(alixGroup.id)
start = Date.now()
await boGroup!.sync()
@@ -297,7 +297,7 @@ test('testing large member listings', async () => {
'syncing 50 members should take less than a .1 second'
)
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroup = await boClient.conversations.findGroup(alixGroup.id)
start = Date.now()
await boGroup!.sync()
@@ -368,7 +368,7 @@ test('testing sending message in large group', async () => {
'sending a message should take less than a .2 second'
)
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroup = await boClient.conversations.findGroup(alixGroup.id)
start = Date.now()
await boGroup!.prepareMessage({ text: `Bo message` })
diff --git a/example/src/tests/groupPermissionsTests.ts b/example/src/tests/groupPermissionsTests.ts
index 7522f980f..b3fa476c7 100644
--- a/example/src/tests/groupPermissionsTests.ts
+++ b/example/src/tests/groupPermissionsTests.ts
@@ -55,7 +55,7 @@ test('super admin can add a new admin', async () => {
assert(!boIsSuperAdmin, `bo should not be a super admin`)
// Verify that bo can not add a new admin
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
const boGroup = (await bo.conversations.listGroups())[0]
try {
await boGroup.addAdmin(caro.inboxId)
@@ -67,7 +67,7 @@ test('super admin can add a new admin', async () => {
// Alix adds bo as an admin
await alixGroup.addAdmin(bo.inboxId)
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
const alixGroupIsAdmin = await alixGroup.isAdmin(bo.inboxId)
assert(alixGroupIsAdmin, `alix should be an admin`)
@@ -98,7 +98,7 @@ test('in admin only group, members can not update group name unless they are an
)
// Verify that bo can not update the group name
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
const boGroup = (await bo.conversations.listGroups())[0]
try {
await boGroup.updateGroupName("bo's group")
@@ -135,7 +135,7 @@ test('in admin only group, members can update group name once they are an admin'
)
// Verify that bo can not update the group name
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
const boGroup = (await bo.conversations.listGroups())[0]
try {
await boGroup.updateGroupName("bo's group")
@@ -147,7 +147,7 @@ test('in admin only group, members can update group name once they are an admin'
// Alix adds bo as an admin
await alixGroup.addAdmin(bo.inboxId)
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
const alixGroupIsAdmin = await alixGroup.isAdmin(bo.inboxId)
assert(alixGroupIsAdmin, `alix should be an admin`)
@@ -190,12 +190,12 @@ test('in admin only group, members can not update group name after admin status
// Alix adds bo as an admin
await alixGroup.addAdmin(bo.inboxId)
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
let boIsAdmin = await alixGroup.isAdmin(bo.inboxId)
assert(boIsAdmin, `bo should be an admin`)
// Now bo can update the group name
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
const boGroup = (await bo.conversations.listGroups())[0]
await boGroup.sync()
await boGroup.updateGroupName("bo's group")
@@ -208,7 +208,7 @@ test('in admin only group, members can not update group name after admin status
// Now alix removed bo as an admin
await alixGroup.removeAdmin(bo.inboxId)
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
boIsAdmin = await alixGroup.isAdmin(bo.inboxId)
assert(!boIsAdmin, `bo should not be an admin`)
@@ -251,7 +251,7 @@ test('can not remove a super admin from a group', async () => {
`number of members should be 2 but was ${numMembers}`
)
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
const boGroup = (await bo.conversations.listGroups())[0]
await boGroup.sync()
@@ -327,7 +327,7 @@ test('can commit after invalid permissions commit', async () => {
[alix.address, caro.address],
{ permissionLevel: 'all_members' }
)
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
const alixGroup = (await alix.conversations.listGroups())[0]
// Verify that Alix cannot add an admin
@@ -373,7 +373,7 @@ test('group with All Members policy has remove function that is admin only', asy
[alix.address, caro.address],
{ permissionLevel: 'all_members' }
)
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
const alixGroup = (await alix.conversations.listGroups())[0]
// Verify that Alix cannot remove a member
@@ -429,7 +429,7 @@ test('can update group permissions', async () => {
)
// Verify that alix can not update the group description
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
const alixGroup = (await alix.conversations.listGroups())[0]
try {
await alixGroup.updateGroupDescription('new description 2')
@@ -479,7 +479,7 @@ test('can update group pinned frame', async () => {
)
// Verify that alix can not update the group pinned frame
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
const alixGroup = (await alix.conversations.listGroups())[0]
try {
await alixGroup.updateGroupPinnedFrameUrl('new pinned frame')
@@ -540,7 +540,7 @@ test('can create a group with custom permissions', async () => {
)
// Verify that bo can read the correct permissions
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
const alixGroup = (await alix.conversations.listGroups())[0]
const permissions = await alixGroup.permissionPolicySet()
assert(
diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts
index 6cb5d4d77..b5f37cd51 100644
--- a/example/src/tests/groupTests.ts
+++ b/example/src/tests/groupTests.ts
@@ -1,5 +1,4 @@
import { Wallet } from 'ethers'
-import RNFS from 'react-native-fs'
import { DecodedMessage } from 'xmtp-react-native-sdk/lib/DecodedMessage'
import {
@@ -13,7 +12,6 @@ import {
Client,
Conversation,
Group,
- ConversationVersion,
GroupUpdatedContent,
GroupUpdatedCodec,
ConsentListEntry,
@@ -25,283 +23,6 @@ function test(name: string, perform: () => Promise) {
groupTests.push({ name: String(counter++) + '. ' + name, run: perform })
}
-test('can make a MLS V3 client', async () => {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const keyBytes = new Uint8Array([
- 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 client = await Client.createRandom({
- env: 'local',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: keyBytes,
- })
-
- const inboxId = await Client.getOrCreateInboxId(client.address, 'local')
-
- assert(
- client.inboxId === inboxId,
- `inboxIds should match but were ${client.inboxId} and ${inboxId}`
- )
- return true
-})
-
-test('can revoke all other installations', async () => {
- const keyBytes = new Uint8Array([
- 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 alixWallet = Wallet.createRandom()
-
- // create a v3 client
- const alix = await Client.create(alixWallet, {
- env: 'local',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: keyBytes,
- })
-
- await alix.deleteLocalDatabase()
-
- const alix2 = await Client.create(alixWallet, {
- env: 'local',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: keyBytes,
- })
-
- const alix2Build = await Client.build(alix2.address, {
- env: 'local',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: keyBytes,
- })
-
- await alix2.deleteLocalDatabase()
-
- const alix3 = await Client.create(alixWallet, {
- env: 'local',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: keyBytes,
- })
-
- const inboxState2 = await alix3.inboxState(true)
- assert(
- inboxState2.installations.length === 3,
- `installations length should be 3 but was ${inboxState2.installations.length}`
- )
-
- await alix3.revokeAllOtherInstallations(alixWallet)
-
- const inboxState3 = await alix3.inboxState(true)
- assert(
- inboxState3.installations.length === 1,
- `installations length should be 1 but was ${inboxState3.installations.length}`
- )
-
- assert(
- inboxState3.installations[0].createdAt !== undefined,
- `installations createdAt should not be undefined`
- )
- return true
-})
-
-test('calls preAuthenticateToInboxCallback when supplied', async () => {
- let isCallbackCalled = 0
- let isPreAuthCalled = false
- const preAuthenticateToInboxCallback = () => {
- isCallbackCalled++
- isPreAuthCalled = true
- }
- const preEnableIdentityCallback = () => {
- isCallbackCalled++
- }
- const preCreateIdentityCallback = () => {
- isCallbackCalled++
- }
- const keyBytes = new Uint8Array([
- 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,
- ])
-
- await Client.createRandom({
- env: 'local',
- enableV3: true,
- preEnableIdentityCallback,
- preCreateIdentityCallback,
- preAuthenticateToInboxCallback,
- dbEncryptionKey: keyBytes,
- })
-
- assert(
- isCallbackCalled === 3,
- `callback should be called 3 times but was ${isCallbackCalled}`
- )
-
- if (!isPreAuthCalled) {
- throw new Error('preAuthenticateToInboxCallback not called')
- }
-
- return true
-})
-
-test('can delete a local database', async () => {
- let [client, anotherClient] = await createClients(2)
-
- await client.conversations.newGroup([anotherClient.address])
- await client.conversations.syncConversations()
- assert(
- (await client.conversations.listGroups()).length === 1,
- `should have a group size of 1 but was ${
- (await client.conversations.listGroups()).length
- }`
- )
-
- assert(
- client.dbPath !== '',
- `client dbPath should be set but was ${client.dbPath}`
- )
- await client.deleteLocalDatabase()
- client = await Client.createRandom({
- env: 'local',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: new Uint8Array([
- 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,
- ]),
- })
- await client.conversations.syncConversations()
- assert(
- (await client.conversations.listGroups()).length === 0,
- `should have a group size of 0 but was ${
- (await client.conversations.listGroups()).length
- }`
- )
-
- return true
-})
-
-test('can make a MLS V3 client with encryption key and database directory', async () => {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const dbDirPath = `${RNFS.DocumentDirectoryPath}/xmtp_db`
- const directoryExists = await RNFS.exists(dbDirPath)
- if (!directoryExists) {
- await RNFS.mkdir(dbDirPath)
- }
- const key = new Uint8Array([
- 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 client = await Client.createRandom({
- env: 'local',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: key,
- dbDirectory: dbDirPath,
- })
-
- const anotherClient = await Client.createRandom({
- env: 'local',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: key,
- })
-
- await client.conversations.newGroup([anotherClient.address])
- assert(
- (await client.conversations.listGroups()).length === 1,
- `should have a group size of 1 but was ${
- (await client.conversations.listGroups()).length
- }`
- )
-
- const clientFromBundle = await Client.build(client.address, {
- env: 'local',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: key,
- dbDirectory: dbDirPath,
- })
-
- assert(
- clientFromBundle.address === client.address,
- `clients dont match ${client.address} and ${clientFromBundle.address}`
- )
-
- assert(
- (await clientFromBundle.conversations.listGroups()).length === 1,
- `should have a group size of 1 but was ${
- (await clientFromBundle.conversations.listGroups()).length
- }`
- )
- return true
-})
-
-test('can drop a local database', async () => {
- const [client, anotherClient] = await createClients(2)
-
- const group = await client.conversations.newGroup([anotherClient.address])
- await client.conversations.syncConversations()
- assert(
- (await client.conversations.listGroups()).length === 1,
- `should have a group size of 1 but was ${
- (await client.conversations.listGroups()).length
- }`
- )
-
- await client.dropLocalDatabaseConnection()
-
- try {
- await group.send('hi')
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- } catch (error) {
- await client.reconnectLocalDatabase()
- await group.send('hi')
- return true
- }
- throw new Error('should throw when local database not connected')
-})
-
-test('can drop client from memory', async () => {
- const [client, anotherClient] = await createClients(2)
- await client.dropLocalDatabaseConnection()
- await anotherClient.dropLocalDatabaseConnection()
-
- await client.reconnectLocalDatabase()
- await Client.dropClient(anotherClient.inboxId)
- try {
- await anotherClient.reconnectLocalDatabase()
- return false
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- } catch (error) {
- // We cannot reconnect anotherClient because it was successfully dropped
- return true
- }
-})
-
-test('can get a inboxId from an address', async () => {
- const [alix, bo] = await createClients(2)
-
- const boInboxId = await alix.findInboxIdFromAddress(bo.address)
- assert(boInboxId === bo.inboxId, `${boInboxId} should match ${bo.inboxId}`)
- return true
-})
-
-test('production MLS V3 client creation does not error', async () => {
- const key = new Uint8Array([
- 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,
- ])
-
- try {
- await Client.createRandom({
- env: 'production',
- appVersion: 'Testing/0.0.0',
- dbEncryptionKey: key,
- })
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- } catch (error) {
- throw error
- }
- return true
-})
-
test('can cancel streams', async () => {
const [alix, bo] = await createClients(2)
let messageCallbacks = 0
@@ -376,7 +97,7 @@ test('group message delivery status', async () => {
`the message should have a delivery status of PUBLISHED but was ${alixMessages2[0].deliveryStatus}`
)
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroup = (await boClient.conversations.listGroups())[0]
await boGroup.sync()
const boMessages: DecodedMessage[] = await boGroup.messages()
@@ -398,7 +119,7 @@ test('can find a group by id', async () => {
const [alixClient, boClient] = await createClients(2)
const alixGroup = await alixClient.conversations.newGroup([boClient.address])
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroup = await boClient.conversations.findGroup(alixGroup.id)
assert(
@@ -413,7 +134,7 @@ test('can find a message by id', async () => {
const alixGroup = await alixClient.conversations.newGroup([boClient.address])
const alixMessageId = await alixGroup.send('Hello')
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroup = await boClient.conversations.findGroup(alixGroup.id)
await boGroup?.sync()
const boMessage = await boClient.conversations.findMessage(alixMessageId)
@@ -429,7 +150,7 @@ test('who added me to a group', async () => {
const [alixClient, boClient] = await createClients(2)
await alixClient.conversations.newGroup([boClient.address])
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroup = (await boClient.conversations.listGroups())[0]
const addedByInboxId = await boGroup.addedByInboxId
@@ -475,7 +196,6 @@ test('can get members of a group', async () => {
})
test('can message in a group', async () => {
- // Create three MLS enabled Clients
const [alixClient, boClient, caroClient] = await createClients(3)
// alix's num groups start at 0
@@ -491,7 +211,7 @@ test('can message in a group', async () => {
])
// alix's num groups == 1
- await alixClient.conversations.syncConversations()
+ await alixClient.conversations.sync()
alixGroups = await alixClient.conversations.listGroups()
if (alixGroups.length !== 1) {
throw new Error('num groups should be 1')
@@ -522,7 +242,7 @@ test('can message in a group', async () => {
await alixGroup.send('gm')
// bo's num groups == 1
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroups = await boClient.conversations.listGroups()
if (boGroups.length !== 1) {
throw new Error(
@@ -549,7 +269,7 @@ test('can message in a group', async () => {
await boGroups[0].send('hey guys!')
// caro's num groups == 1
- await caroClient.conversations.syncConversations()
+ await caroClient.conversations.sync()
const caroGroups = await caroClient.conversations.listGroups()
if (caroGroups.length !== 1) {
throw new Error(
@@ -584,19 +304,23 @@ test('unpublished messages handling', async () => {
// Create a new group with Bob and Alice
const boGroup = await boClient.conversations.newGroup([alixClient.address])
+ assert(
+ (await boGroup.consentState()) === 'allowed',
+ 'consent should be allowed'
+ )
// Sync Alice's client to get the new group
- await alixClient.conversations.syncConversations()
+ await alixClient.conversations.sync()
const alixGroup = await alixClient.conversations.findGroup(boGroup.id)
if (!alixGroup) {
throw new Error(`Group not found for id: ${boGroup.id}`)
}
// Check if the group is allowed initially
- let isGroupAllowed = await alixClient.preferences.conversationIdConsentState(
+ const alixGroupState = await alixClient.preferences.conversationConsentState(
boGroup.id
)
- if (isGroupAllowed !== 'allowed') {
+ if (alixGroupState !== 'unknown') {
throw new Error('Group should not be allowed initially')
}
@@ -604,7 +328,7 @@ test('unpublished messages handling', async () => {
const preparedMessageId = await alixGroup.prepareMessage('Test text')
// Check if the group is allowed after preparing the message
- isGroupAllowed = await alixClient.preferences.conversationIdConsentState(
+ const isGroupAllowed = await alixClient.preferences.conversationConsentState(
boGroup.id
)
if (isGroupAllowed === 'allowed') {
@@ -662,7 +386,7 @@ test('can add members to a group', async () => {
const alixGroup = await alixClient.conversations.newGroup([boClient.address])
// alix's num groups == 1
- await alixClient.conversations.syncConversations()
+ await alixClient.conversations.sync()
alixGroups = await alixClient.conversations.listGroups()
if (alixGroups.length !== 1) {
throw new Error('num groups should be 1')
@@ -688,7 +412,7 @@ test('can add members to a group', async () => {
await alixGroup.send('gm')
// bo's num groups == 1
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
boGroups = await boClient.conversations.listGroups()
if (boGroups.length !== 1) {
throw new Error(
@@ -699,7 +423,7 @@ test('can add members to a group', async () => {
await alixGroup.addMembers([caroClient.address])
// caro's num groups == 1
- await caroClient.conversations.syncConversations()
+ await caroClient.conversations.sync()
caroGroups = await caroClient.conversations.listGroups()
if (caroGroups.length !== 1) {
throw new Error(
@@ -748,7 +472,7 @@ test('can remove members from a group', async () => {
])
// alix's num groups == 1
- await alixClient.conversations.syncConversations()
+ await alixClient.conversations.sync()
alixGroups = await alixClient.conversations.listGroups()
if (alixGroups.length !== 1) {
throw new Error('num groups should be 1')
@@ -774,7 +498,7 @@ test('can remove members from a group', async () => {
await alixGroup.send('gm')
// bo's num groups == 1
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
boGroups = await boClient.conversations.listGroups()
if (boGroups.length !== 1) {
throw new Error(
@@ -783,7 +507,7 @@ test('can remove members from a group', async () => {
}
// caro's num groups == 1
- await caroClient.conversations.syncConversations()
+ await caroClient.conversations.sync()
caroGroups = await caroClient.conversations.listGroups()
if (caroGroups.length !== 1) {
throw new Error(
@@ -888,7 +612,8 @@ test('can stream groups', async () => {
const cancelstream = await alixClient.conversations.stream(
async (group: Conversation) => {
groups.push(group)
- }
+ },
+ 'groups'
)
// caro creates a group with alix, so stream callback is fired
@@ -912,7 +637,7 @@ test('can stream groups', async () => {
}
// * Note alix creating a group does not trigger alix conversations
- // group stream. Workaround is to syncConversations after you create and list manually
+ // group stream. Workaround is to sync after you create and list manually
// See https://github.com/xmtp/libxmtp/issues/504
// alix creates a group
@@ -1017,7 +742,7 @@ test('can list groups', async () => {
})
const boGroups = await boClient.conversations.listGroups()
- await alixClient.conversations.syncConversations()
+ await alixClient.conversations.sync()
const alixGroups = await alixClient.conversations.listGroups()
assert(
@@ -1056,91 +781,6 @@ test('can list groups', async () => {
return true
})
-test('can list all groups and conversations', async () => {
- const [alixClient, boClient, caroClient] = await createClients(3)
-
- // Add one group and one conversation
- const boGroup = await boClient.conversations.newGroup([alixClient.address])
- const alixConversation = await alixClient.conversations.newConversation(
- caroClient.address
- )
-
- const listedContainers = await alixClient.conversations.list()
-
- // Verify information in listed containers is correct
- // BUG - List All returns in Chronological order on iOS
- // and reverse Chronological order on Android
- const first = 0
- const second = 1
- if (
- listedContainers[first].topic !== boGroup.topic ||
- listedContainers[first].version !== ConversationVersion.GROUP ||
- listedContainers[second].createdAt !== alixConversation.createdAt
- ) {
- throw Error('Listed containers should match streamed containers')
- }
-
- return true
-})
-
-test('can stream all groups and conversations', async () => {
- const [alixClient, boClient, caroClient] = await createClients(3)
-
- // Start streaming groups and conversations
- const containers: Conversation[] = []
- const cancelStream = await alixClient.conversations.stream(
- async (Conversation: Conversation) => {
- containers.push(Conversation)
- }
- )
-
- // bo creates a group with alix, so stream callback is fired
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const boGroup = await boClient.conversations.newGroup([alixClient.address])
- await delayToPropogate()
- if ((containers.length as number) !== 1) {
- throw Error('Unexpected num groups (should be 1): ' + containers.length)
- }
-
- // bo creates a v2 Conversation with alix so a stream callback is fired
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const boConversation = await boClient.conversations.newConversation(
- alixClient.address
- )
- await delayToPropogate()
- if ((containers.length as number) !== 2) {
- throw Error('Unexpected num groups (should be 2): ' + containers.length)
- }
-
- // * Note alix creating a v2 Conversation does trigger alix conversations
- // stream.
-
- // alix creates a V2 Conversationgroup
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const alixConversation = await alixClient.conversations.newConversation(
- caroClient.address
- )
- await delayToPropogate()
- if (containers.length !== 3) {
- throw Error('Expected group length 3 but it is: ' + containers.length)
- }
-
- cancelStream()
- await delayToPropogate()
-
- // Creating a group should no longer trigger stream groups
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const caroConversation = await caroClient.conversations.newGroup([
- alixClient.address,
- ])
- await delayToPropogate()
- if ((containers.length as number) !== 3) {
- throw Error('Unexpected num groups (should be 3): ' + containers.length)
- }
-
- return true
-})
-
test('can stream groups and messages', async () => {
const [alixClient, boClient] = await createClients(2)
@@ -1210,7 +850,7 @@ test('can stream group messages', async () => {
)
// bo's num groups == 1
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroup = (await boClient.conversations.listGroups())[0]
for (let i = 0; i < 5; i++) {
@@ -1241,62 +881,6 @@ test('can stream group messages', async () => {
return true
})
-test('can stream all messages', async () => {
- const [alix, bo, caro] = await createClients(3)
-
- await delayToPropogate()
-
- // Record message stream across all conversations
- const allMessages: DecodedMessage[] = []
- await alix.conversations.streamAllMessages(async (message) => {
- allMessages.push(message)
- })
-
- // Start bo starts a new conversation.
- const boConvo = await bo.conversations.newConversation(alix.address)
- await delayToPropogate()
-
- for (let i = 0; i < 5; i++) {
- await boConvo.send({ text: `Message ${i}` })
- await delayToPropogate()
- }
-
- const count = allMessages.length
- if (count !== 5) {
- throw Error('Unexpected all messages count ' + allMessages.length)
- }
-
- const caroConvo = await caro.conversations.newConversation(alix.address)
- const caroGroup = await caro.conversations.newGroup([alix.address])
- await delayToPropogate()
- for (let i = 0; i < 5; i++) {
- await caroConvo.send({ text: `Message ${i}` })
- await caroGroup.send({ text: `Message ${i}` })
- await delayToPropogate()
- }
-
- if (allMessages.length !== 10) {
- throw Error('Unexpected all messages count ' + allMessages.length)
- }
-
- alix.conversations.cancelStreamAllMessages()
-
- await alix.conversations.streamAllMessages(async (message) => {
- allMessages.push(message)
- })
-
- for (let i = 0; i < 5; i++) {
- await boConvo.send({ text: `Message ${i}` })
- await caroGroup.send({ text: `Message ${i}` })
- await delayToPropogate()
- }
- if (allMessages.length <= 15) {
- throw Error('Unexpected all messages count ' + allMessages.length)
- }
-
- return true
-})
-
test('can make a group with metadata', async () => {
const [alix, bo] = await createClients(2)
bo.register(new GroupUpdatedCodec())
@@ -1329,7 +913,7 @@ test('can make a group with metadata', async () => {
await alixGroup.updateGroupImageUrlSquare('newurl.com')
await alixGroup.updateGroupDescription('a new group description')
await alixGroup.sync()
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
const boGroups = await bo.conversations.listGroups()
const boGroup = boGroups[0]
await boGroup.sync()
@@ -1425,7 +1009,7 @@ test('can paginate group messages', async () => {
await alixGroup.send('hello, world')
await alixGroup.send('gm')
- await boClient.conversations.syncConversations()
+ await boClient.conversations.sync()
const boGroups = await boClient.conversations.listGroups()
if (boGroups.length !== 1) {
throw new Error(
@@ -1462,10 +1046,10 @@ test('can stream all group messages', async () => {
const allMessages: DecodedMessage[] = []
// If we don't call syncConversations here, the streamAllGroupMessages will not
// stream the first message. Feels like a bug.
- await alix.conversations.syncConversations()
+ await alix.conversations.sync()
await alix.conversations.streamAllMessages(async (message) => {
allMessages.push(message)
- })
+ }, 'groups')
for (let i = 0; i < 5; i++) {
await boGroup.send({ text: `Message ${i}` })
@@ -1504,251 +1088,21 @@ test('can stream all group messages', async () => {
return true
})
-test('can streamAll from multiple clients', async () => {
- const [alix, bo, caro] = await createClients(3)
-
- // Setup stream alls
- const allBoConversations: any[] = []
- const allAliConversations: any[] = []
-
- await bo.conversations.stream(async (conversation) => {
- allBoConversations.push(conversation)
- })
- await alix.conversations.stream(async (conversation) => {
- allAliConversations.push(conversation)
- })
-
- // Start Caro starts a new conversation.
- await caro.conversations.newConversation(alix.address)
- await delayToPropogate()
- if (allBoConversations.length !== 0) {
- throw Error(
- 'Unexpected all conversations count for Bo ' +
- allBoConversations.length +
- ' and Alix had ' +
- allAliConversations.length
- )
- }
- if (allAliConversations.length !== 1) {
- throw Error(
- 'Unexpected all conversations count ' + allAliConversations.length
- )
- }
- return true
-})
-
-test('can streamAll from multiple clients - swapped orderring', async () => {
- const [alix, bo, caro] = await createClients(3)
-
- // Setup stream alls
- const allBoConversations: any[] = []
- const allAliConversations: any[] = []
-
- await alix.conversations.stream(async (conversation) => {
- allAliConversations.push(conversation)
- })
-
- await bo.conversations.stream(async (conversation) => {
- allBoConversations.push(conversation)
- })
-
- // Start Caro starts a new conversation.
- await caro.conversations.newConversation(alix.address)
- await delayToPropogate()
- if (allBoConversations.length !== 0) {
- throw Error(
- 'Unexpected all conversations count for Bo ' +
- allBoConversations.length +
- ' and Alix had ' +
- allAliConversations.length
- )
- }
- if (allAliConversations.length !== 1) {
- throw Error(
- 'Unexpected all conversations count ' + allAliConversations.length
- )
- }
- return true
-})
-
-test('can streamAllMessages from multiple clients', async () => {
- const [alix, bo, caro] = await createClients(3)
-
- // Setup stream
- const allBoMessages: any[] = []
- const allAliMessages: any[] = []
-
- await bo.conversations.streamAllMessages(async (conversation) => {
- allBoMessages.push(conversation)
- })
- await alix.conversations.streamAllMessages(async (conversation) => {
- allAliMessages.push(conversation)
- })
-
- // Start Caro starts a new conversation.
- const caroConversation = await caro.conversations.newConversation(
- alix.address
- )
- await caroConversation.send({ text: `Message` })
- await delayToPropogate()
- if (allBoMessages.length !== 0) {
- throw Error('Unexpected all messages count for Bo ' + allBoMessages.length)
- }
-
- if (allAliMessages.length !== 1) {
- throw Error(
- 'Unexpected all conversations count for Ali ' + allAliMessages.length
- )
- }
-
- return true
-})
-
-test('can streamAllMessages from multiple clients - swapped', async () => {
- const [alix, bo, caro] = await createClients(3)
-
- // Setup stream
- const allBoMessages: any[] = []
- const allAliMessages: any[] = []
- const caroGroup = await caro.conversations.newGroup([alix.address])
-
- await alix.conversations.streamAllMessages(async (conversation) => {
- allAliMessages.push(conversation)
- })
- await bo.conversations.streamAllMessages(async (conversation) => {
- allBoMessages.push(conversation)
- })
-
- // Start Caro starts a new conversation.
- const caroConvo = await caro.conversations.newConversation(alix.address)
- await delayToPropogate()
- await caroConvo.send({ text: `Message` })
- await caroGroup.send({ text: `Message` })
- await delayToPropogate()
- if (allBoMessages.length !== 0) {
- throw Error(
- 'Unexpected all conversations count for Bo ' + allBoMessages.length
- )
- }
-
- if (allAliMessages.length !== 2) {
- throw Error(
- 'Unexpected all conversations count for Ali ' + allAliMessages.length
- )
- }
-
- return true
-})
-
-test('can stream all group Messages from multiple clients', async () => {
- const [alix, bo, caro] = await createClients(3)
-
- // Setup stream
- const allAlixMessages: DecodedMessage[] = []
- const allBoMessages: DecodedMessage[] = []
- const alixGroup = await caro.conversations.newGroup([alix.address])
- const boGroup = await caro.conversations.newGroup([bo.address])
-
- await alixGroup.streamMessages(async (message) => {
- allAlixMessages.push(message)
- })
- await boGroup.streamMessages(async (message) => {
- allBoMessages.push(message)
- })
-
- // Start Caro starts a new conversation.
- await delayToPropogate()
- await alixGroup.send({ text: `Message` })
- await delayToPropogate()
- if (allBoMessages.length !== 0) {
- throw Error('Unexpected all messages count for Bo ' + allBoMessages.length)
- }
-
- if (allAlixMessages.length !== 1) {
- throw Error(
- 'Unexpected all messages count for Ali ' + allAlixMessages.length
- )
- }
-
- await alix.conversations.syncConversations()
- const alixConv = (await alix.conversations.listGroups())[0]
- await alixConv.send({ text: `Message` })
- await delayToPropogate()
- if (allBoMessages.length !== 0) {
- throw Error('Unexpected all messages count for Bo ' + allBoMessages.length)
- }
- // @ts-ignore-next-line
- if (allAlixMessages.length !== 2) {
- throw Error(
- 'Unexpected all messages count for Ali ' + allAlixMessages.length
- )
- }
-
- return true
-})
-
-test('can stream all group Messages from multiple clients - swapped', async () => {
- const [alix, bo, caro] = await createClients(3)
-
- // Setup stream
- const allAlixMessages: DecodedMessage[] = []
- const allBoMessages: DecodedMessage[] = []
- const alixGroup = await caro.conversations.newGroup([alix.address])
- const boGroup = await caro.conversations.newGroup([bo.address])
-
- await boGroup.streamMessages(async (message) => {
- allBoMessages.push(message)
- })
- await alixGroup.streamMessages(async (message) => {
- allAlixMessages.push(message)
- })
-
- // Start Caro starts a new conversation.
- await delayToPropogate()
- await alixGroup.send({ text: `Message` })
- await delayToPropogate()
- if (allBoMessages.length !== 0) {
- throw Error('Unexpected all messages count for Bo ' + allBoMessages.length)
- }
-
- if (allAlixMessages.length !== 1) {
- throw Error(
- 'Unexpected all messages count for Ali ' + allAlixMessages.length
- )
- }
-
- await alix.conversations.syncConversations()
- const alixConv = (await alix.conversations.listGroups())[0]
- await alixConv.send({ text: `Message` })
- await delayToPropogate()
- if (allBoMessages.length !== 0) {
- throw Error('Unexpected all messages count for Bo ' + allBoMessages.length)
- }
- // @ts-ignore-next-line
- if (allAlixMessages.length !== 2) {
- throw Error(
- 'Unexpected all messages count for Ali ' + allAlixMessages.length
- )
- }
-
- return true
-})
-
test('creating a group should allow group', async () => {
const [alix, bo] = await createClients(2)
const group = await alix.conversations.newGroup([bo.address])
- const consent = await alix.preferences.conversationIdConsentState(group.id)
+ await alix.conversations.sync()
+ const consent = await alix.preferences.conversationConsentState(group.id)
const groupConsent = await group.consentState()
- if (!consent || !groupConsent) {
+ if (consent !== groupConsent) {
throw Error('Group should be allowed')
}
- const state = await group.consentState()
assert(
- state === 'allowed',
- `the message should have a consent state of allowed but was ${state}`
+ groupConsent === 'allowed',
+ `the message should have a consent state of allowed but was ${groupConsent}`
)
return true
@@ -1757,13 +1111,14 @@ test('creating a group should allow group', async () => {
test('can group consent', async () => {
const [alix, bo] = await createClients(2)
const group = await bo.conversations.newGroup([alix.address])
- let isAllowed = await alix.preferences.conversationIdConsentState(group.id)
+ await alix.conversations.sync()
+ let isAllowed = await alix.preferences.conversationConsentState(group.id)
assert(
isAllowed !== 'allowed',
`alix group should NOT be allowed but was ${isAllowed}`
)
- isAllowed = await bo.preferences.conversationIdConsentState(group.id)
+ isAllowed = await bo.preferences.conversationConsentState(group.id)
assert(
isAllowed === 'allowed',
`bo group should be allowed but was ${isAllowed}`
@@ -1776,7 +1131,7 @@ test('can group consent', async () => {
await bo.preferences.setConsentState(
new ConsentListEntry(group.id, 'group_id', 'denied')
)
- const isDenied = await bo.preferences.conversationIdConsentState(group.id)
+ const isDenied = await bo.preferences.conversationConsentState(group.id)
assert(isDenied === 'denied', `bo group should be denied but was ${isDenied}`)
assert(
(await group.consentState()) === 'denied',
@@ -1784,7 +1139,7 @@ test('can group consent', async () => {
)
await group.updateConsent('allowed')
- isAllowed = await bo.preferences.conversationIdConsentState(group.id)
+ isAllowed = await bo.preferences.conversationConsentState(group.id)
assert(
isAllowed === 'allowed',
`bo group should be allowed2 but was ${isAllowed}`
@@ -1877,7 +1232,7 @@ test('sync function behaves as expected', async () => {
let boGroups = await bo.conversations.listGroups()
assert(boGroups.length === 0, 'num groups for bo is 0 until we sync')
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
boGroups = await bo.conversations.listGroups()
assert(boGroups.length === 1, 'num groups for bo is 1')
@@ -1890,7 +1245,7 @@ test('sync function behaves as expected', async () => {
let numMessages = (await boGroups[0].messages()).length
assert(numMessages === 0, 'num members should be 1')
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
// Num messages is still 0 because we didnt sync the group itself
numMessages = (await boGroups[0].messages()).length
@@ -1907,7 +1262,7 @@ test('sync function behaves as expected', async () => {
numMembers = (await boGroups[0].memberInboxIds()).length
assert(numMembers === 2, 'num members should be 2')
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
// Even though we synced the groups, we need to sync the group itself to see the new member
numMembers = (await boGroups[0].memberInboxIds()).length
@@ -1923,11 +1278,11 @@ test('sync function behaves as expected', async () => {
bo.address,
caro.address,
])
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
boGroups = await bo.conversations.listGroups()
assert(boGroups.length === 2, 'num groups for bo is 2')
- // Even before syncing the group, syncConversations will return the initial number of members
+ // Even before syncing the group, sync will return the initial number of members
numMembers = (await boGroups[1].memberInboxIds()).length
assert(numMembers === 3, 'num members should be 3')
@@ -1953,7 +1308,7 @@ test('can read and update group name', async () => {
'group name should be "Test name update 1"'
)
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
const boGroup = (await bo.conversations.listGroups())[0]
groupName = await boGroup.groupName()
@@ -1969,7 +1324,7 @@ test('can read and update group name', async () => {
)
await alixGroup.addMembers([caro.address])
- await caro.conversations.syncConversations()
+ await caro.conversations.sync()
const caroGroup = (await caro.conversations.listGroups())[0]
await caroGroup.sync()
@@ -2002,7 +1357,7 @@ test('can list groups does not fork', async () => {
console.log('sent group message')
// #endregion
// #region sync groups
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
// #endregion
const boGroups = await bo.conversations.listGroups()
assert(boGroups.length === 1, 'bo should have 1 group')
@@ -2057,70 +1412,6 @@ test('can list groups does not fork', async () => {
return true
})
-test('can create new installation without breaking group', async () => {
- const keyBytes = new Uint8Array([
- 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 = Wallet.createRandom()
- const wallet2 = Wallet.createRandom()
-
- const client1 = await Client.create(wallet1, {
- env: 'local',
- appVersion: 'Testing/0.0.0',
- enableV3: true,
- dbEncryptionKey: keyBytes,
- })
- const client2 = await Client.create(wallet2, {
- env: 'local',
- appVersion: 'Testing/0.0.0',
- enableV3: true,
- dbEncryptionKey: keyBytes,
- })
-
- const group = await client1.conversations.newGroup([wallet2.address])
-
- await client1.conversations.syncConversations()
- await client2.conversations.syncConversations()
-
- const client1Group = await client1.conversations.findGroup(group.id)
- const client2Group = await client2.conversations.findGroup(group.id)
-
- await client1Group?.sync()
- await client2Group?.sync()
-
- const members1 = await client1Group?.members()
- assert(
- members1?.length === 2,
- `client 1 should see 2 members but was ${members1?.length}`
- )
-
- const members2 = await client2Group?.members()
- assert(
- members2?.length === 2,
- `client 2 should see 2 members but was ${members2?.length}`
- )
-
- await client2.deleteLocalDatabase()
-
- // Recreating a client with wallet 2 (new installation!)
- await Client.create(wallet2, {
- env: 'local',
- appVersion: 'Testing/0.0.0',
- enableV3: true,
- dbEncryptionKey: keyBytes,
- })
-
- await client1Group?.send('This message will break the group')
- const members3 = await client1Group?.members()
- assert(
- members3?.length === 2,
- `client 1 should still see the 2 members but was ${members3?.length}`
- )
-
- return true
-})
-
test('can list many groups members in parallel', async () => {
const [alix, bo] = await createClients(2)
const groups: Group[] = await createGroups(alix, [bo], 20)
@@ -2145,7 +1436,7 @@ test('can sync all groups', async () => {
const groups: Group[] = await createGroups(alix, [bo], 50)
const alixGroup = groups[0]
- await bo.conversations.syncConversations()
+ await bo.conversations.sync()
const boGroup = await bo.conversations.findGroup(alixGroup.id)
await alixGroup.send('hi')
assert(
@@ -2184,7 +1475,6 @@ test('can sync all groups', async () => {
})
test('only streams groups that can be decrypted', async () => {
- // Create three MLS enabled Clients
const [alixClient, boClient, caroClient] = await createClients(3)
const alixGroups: Conversation[] = []
const boGroups: Conversation[] = []
@@ -2192,13 +1482,13 @@ test('only streams groups that can be decrypted', async () => {
await alixClient.conversations.stream(async (group: Conversation) => {
alixGroups.push(group)
- })
+ }, 'groups')
await boClient.conversations.stream(async (group: Conversation) => {
boGroups.push(group)
- })
+ }, 'groups')
await caroClient.conversations.stream(async (group: Conversation) => {
caroGroups.push(group)
- })
+ }, 'groups')
await alixClient.conversations.newGroup([boClient.address])
@@ -2246,6 +1536,70 @@ test('can stream groups and messages', async () => {
return true
})
+test('can create new installation without breaking group', async () => {
+ const keyBytes = new Uint8Array([
+ 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 = Wallet.createRandom()
+ const wallet2 = Wallet.createRandom()
+
+ const client1 = await Client.create(wallet1, {
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ enableV3: true,
+ dbEncryptionKey: keyBytes,
+ })
+ const client2 = await Client.create(wallet2, {
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ enableV3: true,
+ dbEncryptionKey: keyBytes,
+ })
+
+ const group = await client1.conversations.newGroup([wallet2.address])
+
+ await client1.conversations.sync()
+ await client2.conversations.sync()
+
+ const client1Group = await client1.conversations.findGroup(group.id)
+ const client2Group = await client2.conversations.findGroup(group.id)
+
+ await client1Group?.sync()
+ await client2Group?.sync()
+
+ const members1 = await client1Group?.members()
+ assert(
+ members1?.length === 2,
+ `client 1 should see 2 members but was ${members1?.length}`
+ )
+
+ const members2 = await client2Group?.members()
+ assert(
+ members2?.length === 2,
+ `client 2 should see 2 members but was ${members2?.length}`
+ )
+
+ await client2.deleteLocalDatabase()
+
+ // Recreating a client with wallet 2 (new installation!)
+ await Client.create(wallet2, {
+ env: 'local',
+ appVersion: 'Testing/0.0.0',
+ enableV3: true,
+ dbEncryptionKey: keyBytes,
+ })
+
+ await client1Group?.send('This message will break the group')
+ const members3 = await client1Group?.members()
+ assert(
+ members3?.length === 2,
+ `client 1 should still see the 2 members but was ${members3?.length}`
+ )
+
+ 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 })
diff --git a/ios/XMTPModule.swift b/ios/XMTPModule.swift
index a4968e1fd..c83ee6ae7 100644
--- a/ios/XMTPModule.swift
+++ b/ios/XMTPModule.swift
@@ -545,7 +545,7 @@ public class XMTPModule: Module {
}
}
- AsyncFunction("findDm") {
+ AsyncFunction("findDmByAddress") {
(inboxId: String, peerAddress: String) -> String? in
guard let client = await clientsManager.getClient(key: inboxId)
else {
diff --git a/src/index.ts b/src/index.ts
index 61053c613..df5f074b2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -73,6 +73,10 @@ export async function requestMessageHistorySync(inboxId: InboxId) {
return XMTPModule.requestMessageHistorySync(inboxId)
}
+export async function revokeAllOtherInstallations(inboxId: InboxId) {
+ return XMTPModule.revokeAllOtherInstallations(inboxId)
+}
+
export async function getInboxState(
inboxId: InboxId,
refreshFromNetwork: boolean
@@ -81,8 +85,8 @@ export async function getInboxState(
return InboxState.from(inboxState)
}
-export async function revokeAllOtherInstallations(inboxId: InboxId) {
- return XMTPModule.revokeAllOtherInstallations(inboxId)
+export function preAuthenticateToInboxCallbackCompleted() {
+ XMTPModule.preAuthenticateToInboxCallbackCompleted()
}
export async function receiveSignature(requestID: string, signature: string) {
@@ -171,74 +175,42 @@ export async function dropClient(inboxId: InboxId) {
return await XMTPModule.dropClient(inboxId)
}
-export async function findOrCreateDm<
- ContentTypes extends DefaultContentTypes = DefaultContentTypes,
->(
- client: Client,
- peerAddress: Address
-): Promise> {
- const dm = JSON.parse(
- await XMTPModule.findOrCreateDm(client.inboxId, peerAddress)
- )
- return new Dm(client, dm)
+export async function canMessage(
+ inboxId: InboxId,
+ peerAddresses: Address[]
+): Promise<{ [key: Address]: boolean }> {
+ return await XMTPModule.canMessage(inboxId, peerAddresses)
}
-export async function createGroup<
- ContentTypes extends DefaultContentTypes = DefaultContentTypes,
->(
- client: Client,
- peerAddresses: Address[],
- permissionLevel: 'all_members' | 'admin_only' = 'all_members',
- name: string = '',
- imageUrlSquare: string = '',
- description: string = '',
- pinnedFrameUrl: string = ''
-): Promise> {
- const options: CreateGroupParams = {
- name,
- imageUrlSquare,
- description,
- pinnedFrameUrl,
- }
- const group = JSON.parse(
- await XMTPModule.createGroup(
- client.inboxId,
- peerAddresses,
- permissionLevel,
- JSON.stringify(options)
- )
- )
-
- return new Group(client, group)
+export async function getOrCreateInboxId(
+ address: Address,
+ environment: XMTPEnvironment
+): Promise {
+ return await XMTPModule.getOrCreateInboxId(getAddress(address), environment)
}
-export async function createGroupCustomPermissions<
- ContentTypes extends DefaultContentTypes = DefaultContentTypes,
->(
- client: Client,
- peerAddresses: Address[],
- permissionPolicySet: PermissionPolicySet,
- name: string = '',
- imageUrlSquare: string = '',
- description: string = '',
- pinnedFrameUrl: string = ''
-): Promise> {
- const options: CreateGroupParams = {
- name,
- imageUrlSquare,
- description,
- pinnedFrameUrl,
- }
- const group = JSON.parse(
- await XMTPModule.createGroupCustomPermissions(
- client.inboxId,
- peerAddresses,
- JSON.stringify(permissionPolicySet),
- JSON.stringify(options)
- )
+export async function encryptAttachment(
+ inboxId: InboxId,
+ file: DecryptedLocalAttachment
+): Promise {
+ const fileJson = JSON.stringify(file)
+ const encryptedFileJson = await XMTPModule.encryptAttachment(
+ inboxId,
+ fileJson
)
+ return JSON.parse(encryptedFileJson)
+}
- return new Group(client, group)
+export async function decryptAttachment(
+ inboxId: InboxId,
+ encryptedFile: EncryptedLocalAttachment
+): Promise {
+ const encryptedFileJson = JSON.stringify(encryptedFile)
+ const fileJson = await XMTPModule.decryptAttachment(
+ inboxId,
+ encryptedFileJson
+ )
+ return JSON.parse(fileJson)
}
export async function listGroups<
@@ -316,54 +288,6 @@ export async function listConversations<
})
}
-export async function listMemberInboxIds<
- ContentTypes extends DefaultContentTypes = DefaultContentTypes,
->(client: Client, id: ConversationId): Promise {
- return XMTPModule.listMemberInboxIds(client.inboxId, id)
-}
-
-export async function listPeerInboxId<
- ContentTypes extends DefaultContentTypes = DefaultContentTypes,
->(client: Client, dmId: ConversationId): Promise {
- return XMTPModule.listPeerInboxId(client.inboxId, dmId)
-}
-
-export async function listConversationMembers(
- inboxId: InboxId,
- id: ConversationId
-): Promise {
- const members = await XMTPModule.listConversationMembers(inboxId, id)
-
- return members.map((json: string) => {
- return Member.from(json)
- })
-}
-
-export async function prepareMessage(
- inboxId: InboxId,
- conversationId: ConversationId,
- content: any
-): Promise {
- const contentJson = JSON.stringify(content)
- return await XMTPModule.prepareMessage(inboxId, conversationId, contentJson)
-}
-
-export async function sendMessage(
- inboxId: InboxId,
- conversationId: ConversationId,
- content: any
-): Promise {
- const contentJson = JSON.stringify(content)
- return await XMTPModule.sendMessage(inboxId, conversationId, contentJson)
-}
-
-export async function publishPreparedMessages(
- inboxId: InboxId,
- conversationId: ConversationId
-) {
- return await XMTPModule.publishPreparedMessages(inboxId, conversationId)
-}
-
export async function conversationMessages<
ContentTypes extends DefaultContentTypes = DefaultContentTypes,
>(
@@ -387,6 +311,16 @@ export async function conversationMessages<
})
}
+export async function findMessage<
+ ContentTypes extends DefaultContentTypes = DefaultContentTypes,
+>(
+ client: Client,
+ messageId: MessageId
+): Promise | undefined> {
+ const message = await XMTPModule.findMessage(client.inboxId, messageId)
+ return DecodedMessage.from(message, client)
+}
+
export async function findGroup<
ContentTypes extends DefaultContentTypes = DefaultContentTypes,
>(
@@ -440,13 +374,13 @@ export async function findConversationByTopic<
}
}
-export async function findDm<
+export async function findDmByAddress<
ContentTypes extends DefaultContentTypes = DefaultContentTypes,
>(
client: Client,
address: Address
): Promise | undefined> {
- const json = await XMTPModule.findDm(client.inboxId, address)
+ const json = await XMTPModule.findDmByAddress(client.inboxId, address)
const dm = JSON.parse(json)
if (!dm || Object.keys(dm).length === 0) {
return undefined
@@ -455,14 +389,122 @@ export async function findDm<
return new Dm(client, dm)
}
-export async function findMessage<
- ContentTypes extends DefaultContentTypes = DefaultContentTypes,
->(
- client: Client,
- messageId: MessageId
-): Promise | undefined> {
- const message = await XMTPModule.findMessage(client.inboxId, messageId)
- return DecodedMessage.from(message, client)
+export async function sendMessage(
+ inboxId: InboxId,
+ conversationId: ConversationId,
+ content: any
+): Promise {
+ const contentJson = JSON.stringify(content)
+ return await XMTPModule.sendMessage(inboxId, conversationId, contentJson)
+}
+
+export async function publishPreparedMessages(
+ inboxId: InboxId,
+ conversationId: ConversationId
+) {
+ return await XMTPModule.publishPreparedMessages(inboxId, conversationId)
+}
+
+export async function prepareMessage(
+ inboxId: InboxId,
+ conversationId: ConversationId,
+ content: any
+): Promise {
+ const contentJson = JSON.stringify(content)
+ return await XMTPModule.prepareMessage(inboxId, conversationId, contentJson)
+}
+
+export async function findOrCreateDm<
+ ContentTypes extends DefaultContentTypes = DefaultContentTypes,
+>(
+ client: Client,
+ peerAddress: Address
+): Promise> {
+ const dm = JSON.parse(
+ await XMTPModule.findOrCreateDm(client.inboxId, peerAddress)
+ )
+ return new Dm(client, dm)
+}
+
+export async function createGroup<
+ ContentTypes extends DefaultContentTypes = DefaultContentTypes,
+>(
+ client: Client,
+ peerAddresses: Address[],
+ permissionLevel: 'all_members' | 'admin_only' = 'all_members',
+ name: string = '',
+ imageUrlSquare: string = '',
+ description: string = '',
+ pinnedFrameUrl: string = ''
+): Promise> {
+ const options: CreateGroupParams = {
+ name,
+ imageUrlSquare,
+ description,
+ pinnedFrameUrl,
+ }
+ const group = JSON.parse(
+ await XMTPModule.createGroup(
+ client.inboxId,
+ peerAddresses,
+ permissionLevel,
+ JSON.stringify(options)
+ )
+ )
+
+ return new Group(client, group)
+}
+
+export async function createGroupCustomPermissions<
+ ContentTypes extends DefaultContentTypes = DefaultContentTypes,
+>(
+ client: Client,
+ peerAddresses: Address[],
+ permissionPolicySet: PermissionPolicySet,
+ name: string = '',
+ imageUrlSquare: string = '',
+ description: string = '',
+ pinnedFrameUrl: string = ''
+): Promise> {
+ const options: CreateGroupParams = {
+ name,
+ imageUrlSquare,
+ description,
+ pinnedFrameUrl,
+ }
+ const group = JSON.parse(
+ await XMTPModule.createGroupCustomPermissions(
+ client.inboxId,
+ peerAddresses,
+ JSON.stringify(permissionPolicySet),
+ JSON.stringify(options)
+ )
+ )
+
+ return new Group(client, group)
+}
+
+export async function listMemberInboxIds<
+ ContentTypes extends DefaultContentTypes = DefaultContentTypes,
+>(client: Client, id: ConversationId): Promise {
+ return XMTPModule.listMemberInboxIds(client.inboxId, id)
+}
+
+export async function dmPeerInboxId<
+ ContentTypes extends DefaultContentTypes = DefaultContentTypes,
+>(client: Client, dmId: ConversationId): Promise {
+ return XMTPModule.dmPeerInboxId(client.inboxId, dmId)
+}
+
+export async function listConversationMembers(
+ inboxId: InboxId,
+ id: ConversationId
+): Promise {
+ const members = await XMTPModule.listConversationMembers(inboxId, id)
+
+ return members.map((json: string) => {
+ return Member.from(json)
+ })
}
export async function syncConversations(inboxId: InboxId) {
@@ -509,19 +551,19 @@ export async function removeGroupMembersByInboxId(
return XMTPModule.removeGroupMembersByInboxId(inboxId, id, inboxIds)
}
-export function groupDescription(
+export function groupName(
inboxId: InboxId,
id: ConversationId
): string | PromiseLike {
- return XMTPModule.groupDescription(inboxId, id)
+ return XMTPModule.groupName(inboxId, id)
}
-export function updateGroupDescription(
+export function updateGroupName(
inboxId: InboxId,
id: ConversationId,
- description: string
+ groupName: string
): Promise {
- return XMTPModule.updateGroupDescription(inboxId, id, description)
+ return XMTPModule.updateGroupName(inboxId, id, groupName)
}
export function groupImageUrlSquare(
@@ -539,19 +581,19 @@ export function updateGroupImageUrlSquare(
return XMTPModule.updateGroupImageUrlSquare(inboxId, id, imageUrlSquare)
}
-export function groupName(
+export function groupDescription(
inboxId: InboxId,
id: ConversationId
): string | PromiseLike {
- return XMTPModule.groupName(inboxId, id)
+ return XMTPModule.groupDescription(inboxId, id)
}
-export function updateGroupName(
+export function updateGroupDescription(
inboxId: InboxId,
id: ConversationId,
- groupName: string
+ description: string
): Promise {
- return XMTPModule.updateGroupName(inboxId, id, groupName)
+ return XMTPModule.updateGroupDescription(inboxId, id, description)
}
export function groupPinnedFrameUrl(
@@ -569,133 +611,7 @@ export function updateGroupPinnedFrameUrl(
return XMTPModule.updateGroupPinnedFrameUrl(inboxId, id, pinnedFrameUrl)
}
-export async function canMessage(
- inboxId: InboxId,
- peerAddresses: Address[]
-): Promise<{ [key: Address]: boolean }> {
- return await XMTPModule.canMessage(inboxId, peerAddresses)
-}
-
-export async function getOrCreateInboxId(
- address: Address,
- environment: XMTPEnvironment
-): Promise {
- return await XMTPModule.getOrCreateInboxId(getAddress(address), environment)
-}
-
-export async function encryptAttachment(
- file: DecryptedLocalAttachment
-): Promise {
- const fileJson = JSON.stringify(file)
- const encryptedFileJson = await XMTPModule.encryptAttachment(
- inboxId,
- fileJson
- )
- return JSON.parse(encryptedFileJson)
-}
-
-export async function decryptAttachment(
- encryptedFile: EncryptedLocalAttachment
-): Promise {
- const encryptedFileJson = JSON.stringify(encryptedFile)
- const fileJson = await XMTPModule.decryptAttachment(
- inboxId,
- encryptedFileJson
- )
- return JSON.parse(fileJson)
-}
-
-export function subscribeToConversations(
- inboxId: InboxId,
- type: ConversationType
-) {
- return XMTPModule.subscribeToConversations(inboxId, type)
-}
-
-export function subscribeToAllMessages(
- inboxId: InboxId,
- type: ConversationType
-) {
- return XMTPModule.subscribeToAllMessages(inboxId, type)
-}
-
-export async function subscribeToMessages(
- inboxId: InboxId,
- id: ConversationId
-) {
- return await XMTPModule.subscribeToMessages(inboxId, id)
-}
-
-export function unsubscribeFromConversations(inboxId: InboxId) {
- return XMTPModule.unsubscribeFromConversations(inboxId)
-}
-
-export function unsubscribeFromAllMessages(inboxId: InboxId) {
- return XMTPModule.unsubscribeFromAllMessages(inboxId)
-}
-
-export async function unsubscribeFromMessages(
- inboxId: InboxId,
- id: ConversationId
-) {
- return await XMTPModule.unsubscribeFromMessages(inboxId, id)
-}
-
-export function registerPushToken(pushServer: string, token: string) {
- return XMTPModule.registerPushToken(pushServer, token)
-}
-
-export function subscribePushTopics(topics: ConversationTopic[]) {
- return XMTPModule.subscribePushTopics(topics)
-}
-
-export async function conversationConsentState(
- inboxId: InboxId,
- conversationId: ConversationId
-): Promise {
- return await XMTPModule.conversationV3ConsentState(inboxId, conversationId)
-}
-
-export async function consentConversationIdState(
- inboxId: InboxId,
- conversationId: ConversationId
-): Promise {
- return await XMTPModule.consentConversationIdState(inboxId, conversationId)
-}
-
-export async function consentInboxIdState(
- inboxId: InboxId,
- peerInboxId: InboxId
-): Promise {
- return await XMTPModule.consentInboxIdState(inboxId, peerInboxId)
-}
-
-export async function consentAddressState(
- inboxId: InboxId,
- address: Address
-): Promise {
- return await XMTPModule.consentAddressState(inboxId, address)
-}
-
-export async function setConsentState(
- inboxId: InboxId,
- value: string,
- entryType: ConsentListEntryType,
- consentType: ConsentState
-): Promise {
- return await XMTPModule.setConsentState(
- inboxId,
- value,
- entryType,
- consentType
- )
-}
-
-export function preAuthenticateToInboxCallbackCompleted() {
- XMTPModule.preAuthenticateToInboxCallbackCompleted()
-}
-
-export async function isGroupActive(
+export function isGroupActive(
inboxId: InboxId,
id: ConversationId
): Promise {
@@ -882,14 +798,6 @@ export async function permissionPolicySet(
return JSON.parse(json)
}
-export async function updateConversationConsent(
- inboxId: InboxId,
- conversationId: ConversationId,
- state: ConsentState
-): Promise {
- return XMTPModule.updateConversationConsent(inboxId, conversationId, state)
-}
-
export async function processMessage<
ContentTypes extends DefaultContentTypes = DefaultContentTypes,
>(
@@ -907,7 +815,7 @@ export async function processWelcomeMessage<
client: Client,
encryptedMessage: string
): Promise>> {
- const json = await XMTPModule.processConversationWelcomeMessage(
+ const json = await XMTPModule.processWelcomeMessage(
client.inboxId,
encryptedMessage
)
@@ -920,6 +828,97 @@ export async function processWelcomeMessage<
}
}
+export async function setConsentState(
+ inboxId: InboxId,
+ value: string,
+ entryType: ConsentListEntryType,
+ consentType: ConsentState
+): Promise {
+ return await XMTPModule.setConsentState(
+ inboxId,
+ value,
+ entryType,
+ consentType
+ )
+}
+
+export async function consentAddressState(
+ inboxId: InboxId,
+ address: Address
+): Promise {
+ return await XMTPModule.consentAddressState(inboxId, address)
+}
+export async function consentInboxIdState(
+ inboxId: InboxId,
+ peerInboxId: InboxId
+): Promise {
+ return await XMTPModule.consentInboxIdState(inboxId, peerInboxId)
+}
+export async function consentConversationIdState(
+ inboxId: InboxId,
+ conversationId: ConversationId
+): Promise {
+ return await XMTPModule.consentConversationIdState(inboxId, conversationId)
+}
+export async function conversationConsentState(
+ inboxId: InboxId,
+ conversationId: ConversationId
+): Promise {
+ return await XMTPModule.conversationConsentState(inboxId, conversationId)
+}
+
+export async function updateConversationConsent(
+ inboxId: InboxId,
+ conversationId: ConversationId,
+ state: ConsentState
+): Promise {
+ return XMTPModule.updateConversationConsent(inboxId, conversationId, state)
+}
+
+export function subscribeToConversations(
+ inboxId: InboxId,
+ type: ConversationType
+) {
+ return XMTPModule.subscribeToConversations(inboxId, type)
+}
+
+export function subscribeToAllMessages(
+ inboxId: InboxId,
+ type: ConversationType
+) {
+ return XMTPModule.subscribeToAllMessages(inboxId, type)
+}
+
+export async function subscribeToMessages(
+ inboxId: InboxId,
+ id: ConversationId
+) {
+ return await XMTPModule.subscribeToMessages(inboxId, id)
+}
+
+export function unsubscribeFromConversations(inboxId: InboxId) {
+ return XMTPModule.unsubscribeFromConversations(inboxId)
+}
+
+export function unsubscribeFromAllMessages(inboxId: InboxId) {
+ return XMTPModule.unsubscribeFromAllMessages(inboxId)
+}
+
+export async function unsubscribeFromMessages(
+ inboxId: InboxId,
+ id: ConversationId
+) {
+ return await XMTPModule.unsubscribeFromMessages(inboxId, id)
+}
+
+export function registerPushToken(pushServer: string, token: string) {
+ return XMTPModule.registerPushToken(pushServer, token)
+}
+
+export function subscribePushTopics(topics: ConversationTopic[]) {
+ return XMTPModule.subscribePushTopics(topics)
+}
+
export async function exportNativeLogs() {
return XMTPModule.exportNativeLogs()
}
diff --git a/src/lib/Client.ts b/src/lib/Client.ts
index d141cbf57..086d3d360 100644
--- a/src/lib/Client.ts
+++ b/src/lib/Client.ts
@@ -34,7 +34,7 @@ export class Client<
installationId: string
dbPath: string
conversations: Conversations
- zpreferences: PrivatePreferences
+ preferences: PrivatePreferences
codecRegistry: { [key: string]: XMTPModule.ContentCodec }
private static signSubscription: Subscription | null = null
private static authSubscription: Subscription | null = null
@@ -174,13 +174,13 @@ export class Client<
Boolean(authInboxSubscription),
options.dbDirectory,
options.historySyncUrl,
- signer.walletType(),
- signer.getChainId(),
- signer.getBlockNumber()
+ signer.walletType?.(),
+ signer.getChainId?.(),
+ signer.getBlockNumber?.()
)
})().catch((error) => {
this.removeAllSubscriptions(authInboxSubscription)
- console.error('ERROR in create: ', error)
+ console.error('ERROR in create: ', error.message)
})
})
}
@@ -434,7 +434,7 @@ export class Client<
if (!file.fileUri?.startsWith('file://')) {
throw new Error('the attachment must be a local file:// uri')
}
- return await XMTPModule.encryptAttachment(file)
+ return await XMTPModule.encryptAttachment(this.inboxId, file)
}
/**
@@ -451,7 +451,7 @@ export class Client<
if (!encryptedFile.encryptedLocalFileUri?.startsWith('file://')) {
throw new Error('the attachment must be a local file:// uri')
}
- return await XMTPModule.decryptAttachment(encryptedFile)
+ return await XMTPModule.decryptAttachment(this.inboxId, encryptedFile)
}
}
export type XMTPEnvironment = 'local' | 'dev' | 'production'
diff --git a/src/lib/Conversations.ts b/src/lib/Conversations.ts
index e9f134fa2..bd0873056 100644
--- a/src/lib/Conversations.ts
+++ b/src/lib/Conversations.ts
@@ -77,13 +77,13 @@ export default class Conversations<
}
/**
- * This method returns a list of all groups that the client is a member of.
- * To get the latest list of groups from the network, call syncGroups() first.
- * @param {ConversationOptions} opts - The options to specify what fields you want returned for the groups in the list.
- * @param {ConversationOrder} order - The order to specify if you want groups listed by last message or by created at.
- * @param {number} limit - Limit the number of groups returned in the list.
+ * This method returns a list of all dms that the client is a member of.
+ * To get the latest list of dms from the network, call sync() first.
+ * @param {ConversationOptions} opts - The options to specify what fields you want returned for the dms in the list.
+ * @param {ConversationOrder} order - The order to specify if you want dms listed by last message or by created at.
+ * @param {number} limit - Limit the number of dms returned in the list.
*
- * @returns {Promise} A Promise that resolves to an array of Group objects.
+ * @returns {Promise} A Promise that resolves to an array of Dms objects.
*/
async listDms(
opts?: ConversationOptions | undefined,
@@ -95,7 +95,7 @@ export default class Conversations<
/**
* This method returns a group by the group id if that group exists in the local database.
- * To get the latest list of groups from the network, call syncGroups() first.
+ * To get the latest list of groups from the network, call sync() first.
*
* @returns {Promise} A Promise that resolves to a Group or undefined if not found.
*/
@@ -107,9 +107,9 @@ export default class Conversations<
/**
* This method returns a Dm by the address if that dm exists in the local database.
- * To get the latest list of groups from the network, call syncConversations() first.
+ * To get the latest list of dms from the network, call sync() first.
*
- * @returns {Promise} A Promise that resolves to a Group or undefined if not found.
+ * @returns {Promise} A Promise that resolves to a Dm or undefined if not found.
*/
async findDm(address: Address): Promise | undefined> {
return await XMTPModule.findDm(this.client, address)
@@ -117,9 +117,9 @@ export default class Conversations<
/**
* This method returns a conversation by the topic if that conversation exists in the local database.
- * To get the latest list of groups from the network, call syncConversations() first.
+ * To get the latest list of conversations from the network, call sync() first.
*
- * @returns {Promise} A Promise that resolves to a Group or undefined if not found.
+ * @returns {Promise} A Promise that resolves to a Conversation or undefined if not found.
*/
async findConversationByTopic(
topic: ConversationTopic
@@ -129,9 +129,9 @@ export default class Conversations<
/**
* This method returns a conversation by the conversation id if that conversation exists in the local database.
- * To get the latest list of groups from the network, call syncConversations() first.
+ * To get the latest list of conversations from the network, call sync() first.
*
- * @returns {Promise} A Promise that resolves to a Group or undefined if not found.
+ * @returns {Promise} A Promise that resolves to a Conversation or undefined if not found.
*/
async findConversation(
conversationId: ConversationId
@@ -141,7 +141,7 @@ export default class Conversations<
/**
* This method returns a message by the message id if that message exists in the local database.
- * To get the latest list of messages from the network, call syncGroups() first.
+ * To get the latest list of messages from the network, call sync() first.
*
* @returns {Promise} A Promise that resolves to a DecodedMessage or undefined if not found.
*/
@@ -153,7 +153,7 @@ export default class Conversations<
/**
* This method returns a list of all V3 conversations that the client is a member of.
- * To include the latest groups from the network in the returned list, call syncGroups() first.
+ * To include the latest conversations from the network in the returned list, call sync() first.
*
* @returns {Promise} A Promise that resolves to an array of Conversation objects.
*/
diff --git a/src/lib/Dm.ts b/src/lib/Dm.ts
index 601258496..9a54216d7 100644
--- a/src/lib/Dm.ts
+++ b/src/lib/Dm.ts
@@ -43,8 +43,7 @@ export class Dm
}
/**
- * This method returns an array of inbox ids associated with the group.
- * To get the latest member inbox ids from the network, call sync() first.
+ * This method return the peer inbox id associated with the dm.
* @returns {Promise} A Promise that resolves to a InboxId.
*/
async peerInboxId(): Promise {
@@ -52,7 +51,7 @@ export class Dm
}
/**
- * Sends a message to the current group.
+ * Sends a message to the current dm.
*
* @param {string | MessageContent} content - The content of the message. It can be either a string or a structured MessageContent object.
* @returns {Promise} A Promise that resolves to a string identifier for the sent message.
@@ -79,7 +78,7 @@ export class Dm
}
/**
- * Prepare a group message to be sent.
+ * Prepare a dm message to be sent.
*
* @param {string | MessageContent} content - The content of the message. It can be either a string or a structured MessageContent object.
* @returns {Promise} A Promise that resolves to a string identifier for the prepared message to be sent.
@@ -120,7 +119,7 @@ export class Dm
}
/**
- * This method returns an array of messages associated with the group.
+ * This method returns an array of messages associated with the dm.
* To get the latest messages from the network, call sync() first.
*
* @param {number | undefined} limit - Optional maximum number of messages to return.
@@ -144,14 +143,14 @@ export class Dm
/**
* Executes a network request to fetch the latest messages and membership changes
- * associated with the group and saves them to the local state.
+ * associated with the dm and saves them to the local state.
*/
async sync() {
await XMTP.syncConversation(this.client.inboxId, this.id)
}
/**
- * Sets up a real-time message stream for the current group.
+ * Sets up a real-time message stream for the current dm.
*
* This method subscribes to incoming messages in real-time and listens for new message events.
* When a new message is detected, the provided callback function is invoked with the details of the message.
diff --git a/src/lib/Group.ts b/src/lib/Group.ts
index 0c785b7b3..38861c938 100644
--- a/src/lib/Group.ts
+++ b/src/lib/Group.ts
@@ -200,17 +200,16 @@ export class Group<
async ({
inboxId,
message,
- groupId,
+ conversationId,
}: {
inboxId: string
message: DecodedMessage
- groupId: string
+ conversationId: string
}) => {
- // Long term these checks should be able to be done on the native layer as well, but additional checks in JS for safety
if (inboxId !== this.client.inboxId) {
return
}
- if (groupId !== this.id) {
+ if (conversationId !== this.id) {
return
}
@@ -223,7 +222,6 @@ export class Group<
await XMTP.unsubscribeFromMessages(this.client.inboxId, this.id)
}
}
-
/**
*
* @param addresses addresses to add to the group
diff --git a/src/lib/PrivatePreferences.ts b/src/lib/PrivatePreferences.ts
index 7bd355c1e..04c8e7825 100644
--- a/src/lib/PrivatePreferences.ts
+++ b/src/lib/PrivatePreferences.ts
@@ -11,7 +11,7 @@ export default class PrivatePreferences {
this.client = client
}
- async conversationIdConsentState(
+ async conversationConsentState(
conversationId: ConversationId
): Promise {
return await XMTPModule.consentConversationIdState(
diff --git a/src/lib/Signer.ts b/src/lib/Signer.ts
index efc994d27..97946beba 100644
--- a/src/lib/Signer.ts
+++ b/src/lib/Signer.ts
@@ -14,6 +14,8 @@ export function getSigner(wallet: Signer | WalletClient | null): Signer | null {
if (!wallet) {
return null
}
+ console.log("Lopi444")
+
if (isWalletClient(wallet)) {
return convertWalletClientToSigner(wallet)
}