Skip to content

Commit

Permalink
feat: add support for a v3 only client
Browse files Browse the repository at this point in the history
  • Loading branch information
nplasterer committed Sep 23, 2024
1 parent 5084d87 commit 23f496e
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 6 deletions.
26 changes: 26 additions & 0 deletions example/src/tests/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,29 @@ export async function createClients(numClients: number): Promise<Client[]> {
}
return clients
}

export async function createV3TestingClients(): Promise<Client[]> {
const clients = []
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 alix = await Client.createRandom({
env: 'local',
})
const bo = await Client.createRandomV3({
env: 'local',
enableV3: true,
dbEncryptionKey: keyBytes,
})
const caro = await Client.createRandom({
env: 'local',
enableV3: true,
dbEncryptionKey: keyBytes,
})
bo.register(new GroupUpdatedCodec())
caro.register(new GroupUpdatedCodec())

clients.push(alix, bo, caro)
return clients
}
81 changes: 75 additions & 6 deletions example/src/tests/v3OnlyTests.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/* eslint-disable @typescript-eslint/no-extra-non-null-assertion */
import { Client, Group } from 'xmtp-react-native-sdk'
import { Client } from 'xmtp-react-native-sdk'

import { Test, assert, createClients } from './test-utils'
import {
Test,
assert,
createV3TestingClients,
delayToPropogate,
} from './test-utils'

export const v3OnlyTests: Test[] = []
let counter = 1
Expand Down Expand Up @@ -33,34 +38,98 @@ test('can make a V3 only client', async () => {
client.inboxId === inboxId,
`inboxIds should match but were ${client.inboxId} and ${inboxId}`
)
const canMessageV2 = await client.canMessage(client.address)
assert(canMessageV2 === false, `canMessageV2 should be false`)
const canMessageV3 = await client.canGroupMessage([client.address])

assert(
canMessageV3[client.address.toLowerCase()] === true,
`canMessageV3 should be true`
)
try {
await client.canMessage(client.address)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
return true
}

return true
throw new Error('should throw error when hitting V2 api')
})

test('can create group', async () => {
const [alixV2, boV3, caroV2V3] = await createV3TestingClients()
const group = await boV3.conversations.newGroup([caroV2V3.address])
assert(group?.members?.length === 2, `group should have 2 members`)

return true
try {
await boV3.conversations.newGroup([alixV2.address])
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) {
return true
}
throw new Error(
'should throw error when trying to add a V2 only client to a group'
)
})

test('can send message', async () => {
const [alixV2, boV3, caroV2V3] = await createV3TestingClients()

Check warning on line 74 in example/src/tests/v3OnlyTests.ts

View workflow job for this annotation

GitHub Actions / lint

'alixV2' is assigned a value but never used
const group = await boV3.conversations.newGroup([caroV2V3.address])
await group.send('gm')
await group.sync()
const groupMessages = await group.messages()
assert(
groupMessages[0].content() === 'gm',
`first should be gm but was ${groupMessages[0].content()}`
)

await caroV2V3.conversations.syncGroups()
const sameGroups = await caroV2V3.conversations.listGroups()
await sameGroups[0].sync()

const sameGroupMessages = await sameGroups[0].messages()
assert(
sameGroupMessages[0].content() === 'gm',
`second should be gm but was ${sameGroupMessages[0].content()}`
)
return true
})

test('can stream all messages', async () => {
const [alixV2, boV3, caroV2V3] = await createV3TestingClients()
const conversation = await alixV2.conversations.newConversation(
caroV2V3.address
)
const group = await boV3.conversations.newGroup([caroV2V3.address])
await caroV2V3.conversations.syncGroups()

const allMessages: any[] = []

await caroV2V3.conversations.streamAllMessages(async (conversation) => {
allMessages.push(conversation)
}, true)

await conversation.send('hi')
await group.send('hi')

assert(allMessages.length === 2, '2 messages should have been streamed')

return true
})

test('can stream groups and conversations', async () => {
const [alixV2, boV3, caroV2V3] = await createV3TestingClients()

const allConvos: any[] = []

await caroV2V3.conversations.streamAll(async (conversation) => {
allConvos.push(conversation)
})

await alixV2.conversations.newConversation(caroV2V3.address)
await boV3.conversations.newGroup([caroV2V3.address])

await delayToPropogate()

assert(allConvos.length === 2, '2 convos should have been streamed')

return true
})
71 changes: 71 additions & 0 deletions ios/XMTPModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public class XMTPModule: Module {
// Auth
"sign",
"authed",
"authedV3",
"preCreateIdentityCallback",
"preEnableIdentityCallback",
"preAuthenticateToInboxCallback",
Expand Down Expand Up @@ -280,6 +281,76 @@ public class XMTPModule: Module {
throw error
}
}

AsyncFunction("createRandomV3") { (hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, hasAuthenticateToInboxCallback: Bool?, dbEncryptionKey: [UInt8]?, authParams: String) -> [String: String] in

let privateKey = try PrivateKey.generate()
if(hasCreateIdentityCallback ?? false) {
preCreateIdentityCallbackDeferred = DispatchSemaphore(value: 0)
}
if(hasEnableIdentityCallback ?? false) {
preEnableIdentityCallbackDeferred = DispatchSemaphore(value: 0)
}
if(hasAuthenticateToInboxCallback ?? false) {
preAuthenticateToInboxCallbackDeferred = DispatchSemaphore(value: 0)
}
let preCreateIdentityCallback: PreEventCallback? = hasCreateIdentityCallback ?? false ? self.preCreateIdentityCallback : nil
let preEnableIdentityCallback: PreEventCallback? = hasEnableIdentityCallback ?? false ? self.preEnableIdentityCallback : nil
let preAuthenticateToInboxCallback: PreEventCallback? = hasAuthenticateToInboxCallback ?? false ? self.preAuthenticateToInboxCallback : nil
let encryptionKeyData = dbEncryptionKey == nil ? nil : Data(dbEncryptionKey!)
let authOptions = AuthParamsWrapper.authParamsFromJson(authParams)

let options = createClientConfig(
env: authOptions.environment,
appVersion: authOptions.appVersion,
preEnableIdentityCallback: preEnableIdentityCallback,
preCreateIdentityCallback: preCreateIdentityCallback,
preAuthenticateToInboxCallback: preAuthenticateToInboxCallback,
enableV3: authOptions.enableV3,
dbEncryptionKey: encryptionKeyData,
dbDirectory: authOptions.dbDirectory,
historySyncUrl: authOptions.historySyncUrl
)
let client = try await Client.createOrBuild(account: privateKey, options: options)

await clientsManager.updateClient(key: client.inboxID, client: client)
return try ClientWrapper.encodeToObj(client)
}

AsyncFunction("createOrBuild") { (address: String, hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, hasAuthenticateToInboxCallback: Bool?, dbEncryptionKey: [UInt8]?, authParams: String) in
let signer = ReactNativeSigner(module: self, address: address)
self.signer = signer
if(hasCreateIdentityCallback ?? false) {
self.preCreateIdentityCallbackDeferred = DispatchSemaphore(value: 0)
}
if(hasEnableIdentityCallback ?? false) {
self.preEnableIdentityCallbackDeferred = DispatchSemaphore(value: 0)
}
if(hasAuthenticateToInboxCallback ?? false) {
self.preAuthenticateToInboxCallbackDeferred = DispatchSemaphore(value: 0)
}
let preCreateIdentityCallback: PreEventCallback? = hasCreateIdentityCallback ?? false ? self.preCreateIdentityCallback : nil
let preEnableIdentityCallback: PreEventCallback? = hasEnableIdentityCallback ?? false ? self.preEnableIdentityCallback : nil
let preAuthenticateToInboxCallback: PreEventCallback? = hasAuthenticateToInboxCallback ?? false ? self.preAuthenticateToInboxCallback : nil
let encryptionKeyData = dbEncryptionKey == nil ? nil : Data(dbEncryptionKey!)
let authOptions = AuthParamsWrapper.authParamsFromJson(authParams)

let options = self.createClientConfig(
env: authOptions.environment,
appVersion: authOptions.appVersion,
preEnableIdentityCallback: preEnableIdentityCallback,
preCreateIdentityCallback: preCreateIdentityCallback,
preAuthenticateToInboxCallback: preAuthenticateToInboxCallback,
enableV3: authOptions.enableV3,
dbEncryptionKey: encryptionKeyData,
dbDirectory: authOptions.dbDirectory,
historySyncUrl: authOptions.historySyncUrl
)
let client = try await XMTP.Client.createOrBuild(account: signer, options: options)
await self.clientsManager.updateClient(key: client.inboxID, client: client)
self.signer = nil
self.sendEvent("authedV3", try ClientWrapper.encodeToObj(client))
}

// Remove a client from memory for a given inboxId
AsyncFunction("dropClient") { (inboxId: String) in
Expand Down
1 change: 1 addition & 0 deletions src/lib/types/EventTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum EventTypes {
// Auth
Sign = 'sign',
Authed = 'authed',
AuthedV3 = 'authedV3',
PreCreateIdentityCallback = 'preCreateIdentityCallback',
PreEnableIdentityCallback = 'preEnableIdentityCallback',
// Conversations Events
Expand Down

0 comments on commit 23f496e

Please sign in to comment.