diff --git a/android/build.gradle b/android/build.gradle index f8c506495..fe06092dc 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -98,12 +98,12 @@ repositories { dependencies { implementation project(':expo-modules-core') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" - implementation "org.xmtp:android:0.14.14" + implementation "org.xmtp:android:0.14.15" implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.facebook.react:react-native:0.71.3' implementation "com.daveanthonythomas.moshipack:moshipack:1.0.1" // xmtp-android local testing setup below (comment org.xmtp:android above) - // implementation files('/xmtp-android/library/build/outputs/aar/library-debug.aar') + // implementation files('/xmtp-android/library/build/outputs/aar/library-debug.aar') // implementation 'com.google.crypto.tink:tink-android:1.8.0' // implementation 'io.grpc:grpc-kotlin-stub:1.4.1' // implementation 'io.grpc:grpc-okhttp:1.62.2' diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index 496277323..03d58ff34 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -155,6 +155,7 @@ class XMTPModule : Module() { private val subscriptions: MutableMap = mutableMapOf() private var preEnableIdentityCallbackDeferred: CompletableDeferred? = null private var preCreateIdentityCallbackDeferred: CompletableDeferred? = null + private var preAuthenticateToInboxCallbackDeferred: CompletableDeferred? = null override fun definition() = ModuleDefinition { @@ -165,6 +166,7 @@ class XMTPModule : Module() { "authed", "preCreateIdentityCallback", "preEnableIdentityCallback", + "preAuthenticateToInboxCallback", // Conversations "conversation", "group", @@ -227,7 +229,7 @@ class XMTPModule : Module() { // // Auth functions // - AsyncFunction("auth") Coroutine { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, dbEncryptionKey: List?, authParams: String -> + AsyncFunction("auth") Coroutine { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasAuthInboxCallback: Boolean?, dbEncryptionKey: List?, authParams: String -> withContext(Dispatchers.IO) { logV("auth") @@ -239,10 +241,14 @@ class XMTPModule : Module() { preCreateIdentityCallbackDeferred = CompletableDeferred() if (hasEnableIdentityCallback == true) preEnableIdentityCallbackDeferred = CompletableDeferred() + if (hasAuthInboxCallback == true) + preAuthenticateToInboxCallbackDeferred = CompletableDeferred() val preCreateIdentityCallback: PreEventCallback? = preCreateIdentityCallback.takeIf { hasCreateIdentityCallback == true } val preEnableIdentityCallback: PreEventCallback? = preEnableIdentityCallback.takeIf { hasEnableIdentityCallback == true } + val preAuthenticateToInboxCallback: PreEventCallback? = + preAuthenticateToInboxCallback.takeIf { hasAuthInboxCallback == true } val context = if (authOptions.enableV3) context else null val encryptionKeyBytes = dbEncryptionKey?.foldIndexed(ByteArray(dbEncryptionKey.size)) { i, a, v -> @@ -253,6 +259,7 @@ class XMTPModule : Module() { api = apiEnvironments(authOptions.environment, authOptions.appVersion), preCreateIdentityCallback = preCreateIdentityCallback, preEnableIdentityCallback = preEnableIdentityCallback, + preAuthenticateToInboxCallback = preAuthenticateToInboxCallback, enableV3 = authOptions.enableV3, appContext = context, dbEncryptionKey = encryptionKeyBytes, @@ -273,7 +280,7 @@ class XMTPModule : Module() { } // Generate a random wallet and set the client to that - AsyncFunction("createRandom") Coroutine { hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, dbEncryptionKey: List?, authParams: String -> + AsyncFunction("createRandom") Coroutine { hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, hasPreAuthenticateToInboxCallback: Boolean?, dbEncryptionKey: List?, authParams: String -> withContext(Dispatchers.IO) { logV("createRandom") val privateKey = PrivateKeyBuilder() @@ -282,10 +289,14 @@ class XMTPModule : Module() { preCreateIdentityCallbackDeferred = CompletableDeferred() if (hasEnableIdentityCallback == true) preEnableIdentityCallbackDeferred = CompletableDeferred() + if (hasPreAuthenticateToInboxCallback == true) + preAuthenticateToInboxCallbackDeferred = CompletableDeferred() val preCreateIdentityCallback: PreEventCallback? = preCreateIdentityCallback.takeIf { hasCreateIdentityCallback == true } val preEnableIdentityCallback: PreEventCallback? = preEnableIdentityCallback.takeIf { hasEnableIdentityCallback == true } + val preAuthenticateToInboxCallback: PreEventCallback? = + preAuthenticateToInboxCallback.takeIf { hasPreAuthenticateToInboxCallback == true } val authOptions = AuthParamsWrapper.authParamsFromJson(authParams) val context = if (authOptions.enableV3) context else null @@ -298,6 +309,7 @@ class XMTPModule : Module() { api = apiEnvironments(authOptions.environment, authOptions.appVersion), preCreateIdentityCallback = preCreateIdentityCallback, preEnableIdentityCallback = preEnableIdentityCallback, + preAuthenticateToInboxCallback = preAuthenticateToInboxCallback, enableV3 = authOptions.enableV3, appContext = context, dbEncryptionKey = encryptionKeyBytes, @@ -1536,6 +1548,11 @@ class XMTPModule : Module() { preEnableIdentityCallbackDeferred?.complete(Unit) } + Function("preAuthenticateToInboxCallbackCompleted") { + logV("preAuthenticateToInboxCallbackCompleted") + preAuthenticateToInboxCallbackDeferred?.complete(Unit) + } + AsyncFunction("allowGroups") Coroutine { inboxId: String, groupIds: List -> withContext(Dispatchers.IO) { logV("allowGroups") @@ -1856,6 +1873,12 @@ class XMTPModule : Module() { preCreateIdentityCallbackDeferred?.await() preCreateIdentityCallbackDeferred = null } + + private val preAuthenticateToInboxCallback: suspend () -> Unit = { + sendEvent("preAuthenticateToInboxCallback") + preAuthenticateToInboxCallbackDeferred?.await() + preAuthenticateToInboxCallbackDeferred = null + } } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 3ea1add7d..803f028f1 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -449,7 +449,7 @@ PODS: - GenericJSON (~> 2.0) - Logging (~> 1.0.0) - secp256k1.swift (~> 0.1) - - XMTP (0.13.14): + - XMTP (0.13.15): - Connect-Swift (= 0.12.0) - GzipSwift - LibXMTP (= 0.5.6-beta4) @@ -458,7 +458,7 @@ PODS: - ExpoModulesCore - MessagePacker - secp256k1.swift - - XMTP (= 0.13.14) + - XMTP (= 0.13.15) - Yoga (1.14.0) DEPENDENCIES: @@ -763,8 +763,8 @@ SPEC CHECKSUMS: secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634 SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1 web3.swift: 2263d1e12e121b2c42ffb63a5a7beb1acaf33959 - XMTP: f590939a897f00a0f957223eba31d5d6bee10a48 - XMTPReactNative: ca2cdef4fcadb3e072fec62de63f379c84464250 + XMTP: d364bb93ef30523ba8a66a3e57c3a39f32e06f5e + XMTPReactNative: 9ce794973cfe73271557ae504c99be9d81d80cd2 Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9 PODFILE CHECKSUM: 95d6ace79946933ecf80684613842ee553dd76a2 diff --git a/example/src/LaunchScreen.tsx b/example/src/LaunchScreen.tsx index 27a387bc3..48d8f7ab3 100644 --- a/example/src/LaunchScreen.tsx +++ b/example/src/LaunchScreen.tsx @@ -99,6 +99,10 @@ export default function LaunchScreen( console.log('Pre Enable Identity Callback') } + const preAuthenticateToInboxCallback = async () => { + console.log('Pre Authenticate To Inbox Callback') + } + const networkOptions = [ { key: 0, label: 'dev' }, { key: 1, label: 'local' }, @@ -222,8 +226,10 @@ export default function LaunchScreen( XMTP.Client.create(signer, { env: selectedNetwork, appVersion, + codecs: supportedCodecs, preCreateIdentityCallback, preEnableIdentityCallback, + preAuthenticateToInboxCallback, enableV3: enableGroups === 'true', dbEncryptionKey, }) @@ -258,6 +264,7 @@ export default function LaunchScreen( codecs: supportedCodecs, preCreateIdentityCallback, preEnableIdentityCallback, + preAuthenticateToInboxCallback, enableV3: enableGroups === 'true', dbEncryptionKey, }) diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index 60d3e55fb..68d6cd818 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -49,6 +49,45 @@ test('can make a MLS V3 client', async () => { 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) diff --git a/ios/XMTPModule.swift b/ios/XMTPModule.swift index 93523c7c9..b47bde3f3 100644 --- a/ios/XMTPModule.swift +++ b/ios/XMTPModule.swift @@ -42,6 +42,7 @@ public class XMTPModule: Module { let subscriptionsManager = IsolatedManager>() private var preEnableIdentityCallbackDeferred: DispatchSemaphore? private var preCreateIdentityCallbackDeferred: DispatchSemaphore? + private var preAuthenticateToInboxCallbackDeferred: DispatchSemaphore? actor ClientsManager { private var clients: [String: XMTP.Client] = [:] @@ -71,6 +72,7 @@ public class XMTPModule: Module { "authed", "preCreateIdentityCallback", "preEnableIdentityCallback", + "preAuthenticateToInboxCallback", // Conversations "conversation", "group", @@ -137,7 +139,7 @@ public class XMTPModule: Module { // // Auth functions // - AsyncFunction("auth") { (address: String, hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, dbEncryptionKey: [UInt8]?, authParams: String) in + AsyncFunction("auth") { (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) { @@ -146,12 +148,26 @@ public class XMTPModule: Module { 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, enableV3: authOptions.enableV3, dbEncryptionKey: encryptionKeyData, dbDirectory: authOptions.dbDirectory, historySyncUrl: authOptions.historySyncUrl) + 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.create(account: signer, options: options) await self.clientsManager.updateClient(key: client.inboxID, client: client) self.signer = nil @@ -163,7 +179,7 @@ public class XMTPModule: Module { } // Generate a random wallet and set the client to that - AsyncFunction("createRandom") { (hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, dbEncryptionKey: [UInt8]?, authParams: String) -> [String: String] in + AsyncFunction("createRandom") { (hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, hasAuthenticateToInboxCallback: Bool?, dbEncryptionKey: [UInt8]?, authParams: String) -> [String: String] in let privateKey = try PrivateKey.generate() if(hasCreateIdentityCallback ?? false) { @@ -172,12 +188,26 @@ public class XMTPModule: Module { 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, enableV3: authOptions.enableV3, dbEncryptionKey: encryptionKeyData, dbDirectory: authOptions.dbDirectory, historySyncUrl: authOptions.historySyncUrl) + 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.create(account: privateKey, options: options) await clientsManager.updateClient(key: client.inboxID, client: client) @@ -1414,6 +1444,13 @@ public class XMTPModule: Module { self.preCreateIdentityCallbackDeferred = nil } } + + Function("preAuthenticateToInboxCallbackCompleted") { + DispatchQueue.global().async { + self.preAuthenticateToInboxCallbackDeferred?.signal() + self.preAuthenticateToInboxCallbackDeferred = nil + } + } AsyncFunction("allowGroups") { (inboxId: String, groupIds: [String]) in guard let client = await clientsManager.getClient(key: inboxId) else { @@ -1463,7 +1500,7 @@ public class XMTPModule: Module { } } - func createClientConfig(env: String, appVersion: String?, preEnableIdentityCallback: PreEventCallback? = nil, preCreateIdentityCallback: PreEventCallback? = nil, enableV3: Bool = false, dbEncryptionKey: Data? = nil, dbDirectory: String? = nil, historySyncUrl: String? = nil) -> XMTP.ClientOptions { + func createClientConfig(env: String, appVersion: String?, preEnableIdentityCallback: PreEventCallback? = nil, preCreateIdentityCallback: PreEventCallback? = nil, preAuthenticateToInboxCallback: PreEventCallback? = nil, enableV3: Bool = false, dbEncryptionKey: Data? = nil, dbDirectory: String? = nil, historySyncUrl: String? = nil) -> XMTP.ClientOptions { // Ensure that all codecs have been registered. switch env { case "local": @@ -1471,19 +1508,19 @@ public class XMTPModule: Module { env: XMTP.XMTPEnvironment.local, isSecure: false, appVersion: appVersion - ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, enableV3: enableV3, encryptionKey: dbEncryptionKey, dbDirectory: dbDirectory, historySyncUrl: historySyncUrl) + ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, preAuthenticateToInboxCallback: preAuthenticateToInboxCallback, enableV3: enableV3, encryptionKey: dbEncryptionKey, dbDirectory: dbDirectory, historySyncUrl: historySyncUrl) case "production": return XMTP.ClientOptions(api: XMTP.ClientOptions.Api( env: XMTP.XMTPEnvironment.production, isSecure: true, appVersion: appVersion - ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, enableV3: enableV3, encryptionKey: dbEncryptionKey, dbDirectory: dbDirectory, historySyncUrl: historySyncUrl) + ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, preAuthenticateToInboxCallback: preAuthenticateToInboxCallback, enableV3: enableV3, encryptionKey: dbEncryptionKey, dbDirectory: dbDirectory, historySyncUrl: historySyncUrl) default: return XMTP.ClientOptions(api: XMTP.ClientOptions.Api( env: XMTP.XMTPEnvironment.dev, isSecure: true, appVersion: appVersion - ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, enableV3: enableV3, encryptionKey: dbEncryptionKey, dbDirectory: dbDirectory, historySyncUrl: historySyncUrl) + ), preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, preAuthenticateToInboxCallback: preAuthenticateToInboxCallback, enableV3: enableV3, encryptionKey: dbEncryptionKey, dbDirectory: dbDirectory, historySyncUrl: historySyncUrl) } } @@ -1734,4 +1771,9 @@ public class XMTPModule: Module { sendEvent("preCreateIdentityCallback") self.preCreateIdentityCallbackDeferred?.wait() } + + func preAuthenticateToInboxCallback() { + sendEvent("preAuthenticateToInboxCallback") + self.preAuthenticateToInboxCallbackDeferred?.wait() + } } diff --git a/ios/XMTPReactNative.podspec b/ios/XMTPReactNative.podspec index bb034d47d..add0e4079 100644 --- a/ios/XMTPReactNative.podspec +++ b/ios/XMTPReactNative.podspec @@ -26,5 +26,5 @@ Pod::Spec.new do |s| s.source_files = "**/*.{h,m,swift}" s.dependency 'secp256k1.swift' s.dependency "MessagePacker" - s.dependency "XMTP", "= 0.13.14" + s.dependency "XMTP", "= 0.13.15" end diff --git a/src/index.ts b/src/index.ts index 3d129ff3a..35fa9e7be 100644 --- a/src/index.ts +++ b/src/index.ts @@ -76,6 +76,7 @@ export async function auth( appVersion?: string | undefined, hasCreateIdentityCallback?: boolean | undefined, hasEnableIdentityCallback?: boolean | undefined, + hasPreAuthenticateToInboxCallback?: boolean | undefined, enableV3?: boolean | undefined, dbEncryptionKey?: Uint8Array | undefined, dbDirectory?: string | undefined, @@ -96,6 +97,7 @@ export async function auth( address, hasCreateIdentityCallback, hasEnableIdentityCallback, + hasPreAuthenticateToInboxCallback, encryptionKey, JSON.stringify(authParams) ) @@ -110,6 +112,7 @@ export async function createRandom( appVersion?: string | undefined, hasCreateIdentityCallback?: boolean | undefined, hasEnableIdentityCallback?: boolean | undefined, + hasPreAuthenticateToInboxCallback?: boolean | undefined, enableV3?: boolean | undefined, dbEncryptionKey?: Uint8Array | undefined, dbDirectory?: string | undefined, @@ -129,6 +132,7 @@ export async function createRandom( return await XMTPModule.createRandom( hasCreateIdentityCallback, hasEnableIdentityCallback, + hasPreAuthenticateToInboxCallback, encryptionKey, JSON.stringify(authParams) ) @@ -870,6 +874,10 @@ export function preCreateIdentityCallbackCompleted() { XMTPModule.preCreateIdentityCallbackCompleted() } +export function preAuthenticateToInboxCallbackCompleted() { + XMTPModule.preAuthenticateToInboxCallbackCompleted() +} + export async function isGroupActive( inboxId: string, id: string diff --git a/src/lib/Client.ts b/src/lib/Client.ts index aac08d70a..5f784ce16 100644 --- a/src/lib/Client.ts +++ b/src/lib/Client.ts @@ -62,7 +62,7 @@ export class Client< ) { throw new Error('Must pass an encryption key that is exactly 32 bytes.') } - const { enableSubscription, createSubscription } = + const { enableSubscription, createSubscription, authInboxSubscription } = this.setupSubscriptions(options) const signer = getSigner(wallet) if (!signer) { @@ -90,10 +90,11 @@ export class Client< } catch (e) { const errorMessage = 'ERROR in create. User rejected signature' console.info(errorMessage, e) - this.removeSubscription(enableSubscription) - this.removeSubscription(createSubscription) - this.removeSignSubscription() - this.removeAuthSubscription() + this.removeAllSubscriptions( + createSubscription, + enableSubscription, + authInboxSubscription + ) reject(errorMessage) } } @@ -107,10 +108,11 @@ export class Client< installationId: string dbPath: string }) => { - this.removeSubscription(enableSubscription) - this.removeSubscription(createSubscription) - this.removeSignSubscription() - this.removeAuthSubscription() + this.removeAllSubscriptions( + createSubscription, + enableSubscription, + authInboxSubscription + ) resolve( new Client( message.address, @@ -128,29 +130,38 @@ export class Client< options.appVersion, Boolean(createSubscription), Boolean(enableSubscription), + Boolean(authInboxSubscription), Boolean(options.enableV3), options.dbEncryptionKey, options.dbDirectory, options.historySyncUrl ) })().catch((error) => { + this.removeAllSubscriptions( + createSubscription, + enableSubscription, + authInboxSubscription + ) console.error('ERROR in create: ', error) }) }) } - private static removeSignSubscription(): void { - if (this.signSubscription) { - this.signSubscription.remove() - this.signSubscription = null - } - } - - private static removeAuthSubscription(): void { - if (this.authSubscription) { - this.authSubscription.remove() - this.authSubscription = null - } + private static removeAllSubscriptions( + createSubscription?: Subscription, + enableSubscription?: Subscription, + authInboxSubscription?: Subscription + ): void { + ;[ + createSubscription, + enableSubscription, + authInboxSubscription, + this.signSubscription, + this.authSubscription, + ].forEach((subscription) => subscription?.remove()) + + this.signSubscription = null + this.authSubscription = null } /** @@ -169,20 +180,22 @@ export class Client< ) { throw new Error('Must pass an encryption key that is exactly 32 bytes.') } - const { enableSubscription, createSubscription } = + const { createSubscription, enableSubscription, authInboxSubscription } = this.setupSubscriptions(options) const client = await XMTPModule.createRandom( options.env, options.appVersion, Boolean(createSubscription), Boolean(enableSubscription), + Boolean(authInboxSubscription), Boolean(options.enableV3), options.dbEncryptionKey, options.dbDirectory, options.historySyncUrl ) - this.removeSubscription(enableSubscription) this.removeSubscription(createSubscription) + this.removeSubscription(enableSubscription) + this.removeSubscription(authInboxSubscription) return new Client( client['address'], @@ -284,8 +297,9 @@ export class Client< } private static setupSubscriptions(opts: ClientOptions): { - enableSubscription?: Subscription createSubscription?: Subscription + enableSubscription?: Subscription + authInboxSubscription?: Subscription } { const enableSubscription = this.addSubscription( 'preEnableIdentityCallback', @@ -305,15 +319,24 @@ export class Client< } ) - return { enableSubscription, createSubscription } + const authInboxSubscription = this.addSubscription( + 'preAuthenticateToInboxCallback', + opts, + async () => { + await this.executeCallback(opts?.preAuthenticateToInboxCallback) + XMTPModule.preAuthenticateToInboxCallbackCompleted() + } + ) + + return { createSubscription, enableSubscription, authInboxSubscription } } /** * Static method to determine the inboxId for the address. - * + * * @param {string} peerAddress - The address of the peer to check for messaging eligibility. * @param {Partial} opts - Optional configuration options for the Client. - * @returns {Promise} + * @returns {Promise} */ static async getOrCreateInboxId( address: string, @@ -541,6 +564,7 @@ export type ClientOptions = { */ preCreateIdentityCallback?: () => Promise | void preEnableIdentityCallback?: () => Promise | void + preAuthenticateToInboxCallback?: () => Promise | void /** * Specify whether to enable V3 version of MLS (Group Chat) */