diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index b79f4ad52..97f982a50 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -67,6 +67,7 @@ import kotlin.coroutines.Continuation import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import com.facebook.common.util.Hex +import expo.modules.xmtpreactnativesdk.wrappers.ClientWrapper import expo.modules.xmtpreactnativesdk.wrappers.MemberWrapper import org.xmtp.android.library.messages.MessageDeliveryStatus import org.xmtp.android.library.messages.Topic @@ -242,7 +243,7 @@ class XMTPModule : Module() { clients[address] = client ContentJson.Companion signer = null - sendEvent("authed", mapOf("inboxId" to client.inboxId)) + sendEvent("authed", ClientWrapper.encodeToObj(client)) } Function("receiveSignature") { requestID: String, signature: String -> @@ -282,10 +283,7 @@ class XMTPModule : Module() { val randomClient = Client().create(account = privateKey, options = options) ContentJson.Companion clients[randomClient.address] = randomClient - mapOf ( - "address" to randomClient.address, - "inboxId" to randomClient.inboxId - ) + ClientWrapper.encodeToObj(randomClient) } AsyncFunction("createFromKeyBundle") { keyBundle: String, environment: String, appVersion: String?, enableAlphaMls: Boolean?, dbEncryptionKey: List?, dbDirectory: String? -> @@ -315,10 +313,7 @@ class XMTPModule : Module() { val client = Client().buildFromBundle(bundle = bundle, options = options) ContentJson.Companion clients[client.address] = client - mapOf ( - "address" to client.address, - "inboxId" to client.inboxId - ) + ClientWrapper.encodeToObj(client) } catch (e: Exception) { throw XMTPException("Failed to create client: $e") } diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ClientWrapper.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ClientWrapper.kt new file mode 100644 index 000000000..cb9f2e958 --- /dev/null +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/ClientWrapper.kt @@ -0,0 +1,22 @@ +package expo.modules.xmtpreactnativesdk.wrappers + +import com.google.gson.GsonBuilder +import org.xmtp.android.library.Client + +class ClientWrapper { + companion object { + fun encodeToObj(client: Client): Map { + return mapOf( + "inboxId" to client.inboxId, + "address" to client.address, + "installationId" to client.installationId + ) + } + + fun encode(client: Client): String { + val gson = GsonBuilder().create() + val obj = encodeToObj(client) + return gson.toJson(obj) + } + } +} \ No newline at end of file diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index e9e4e9a45..2e0c22e1e 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -191,6 +191,16 @@ test('can make a MLS V3 client from bundle', async () => { `clients dont match ${client2.address} and ${client.address}` ) + assert( + client.inboxId === client2.inboxId, + `clients dont match ${client2.inboxId} and ${client.inboxId}` + ) + + assert( + client.installationId === client2.installationId, + `clients dont match ${client2.installationId} and ${client.installationId}` + ) + const randomClient = await Client.createRandom({ env: 'local', appVersion: 'Testing/0.0.0', diff --git a/ios/Wrappers/ClientWrapper.swift b/ios/Wrappers/ClientWrapper.swift new file mode 100644 index 000000000..13a262ad5 --- /dev/null +++ b/ios/Wrappers/ClientWrapper.swift @@ -0,0 +1,29 @@ +// +// ClientWrapper.swift +// XMTPReactNative +// +// Created by Naomi Plasterer on 6/10/24. +// + +import Foundation +import XMTP + +// Wrapper around XMTP.Client to allow passing these objects back into react native. +struct ClientWrapper { + static func encodeToObj(_ client: XMTP.Client) throws -> [String: String] { + return [ + "inboxId": client.inboxID, + "address": client.address, + "installationId": client.installationID, + ] + } + + static func encode(_ client: XMTP.Client) throws -> String { + let obj = try encodeToObj(client) + let data = try JSONSerialization.data(withJSONObject: obj) + guard let result = String(data: data, encoding: .utf8) else { + throw WrapperError.encodeError("could not encode client") + } + return result + } +} diff --git a/ios/Wrappers/MemberWrapper.swift b/ios/Wrappers/MemberWrapper.swift index 142dc55bd..5bd59d184 100644 --- a/ios/Wrappers/MemberWrapper.swift +++ b/ios/Wrappers/MemberWrapper.swift @@ -8,7 +8,7 @@ import Foundation import XMTP -// Wrapper around XMTP.Group to allow passing these objects back into react native. +// Wrapper around XMTP.Member to allow passing these objects back into react native. struct MemberWrapper { static func encodeToObj(_ member: XMTP.Member) throws -> [String: Any] { let permissionString = switch member.permissionLevel { diff --git a/ios/XMTPModule.swift b/ios/XMTPModule.swift index 8b96a825b..3dd958a4e 100644 --- a/ios/XMTPModule.swift +++ b/ios/XMTPModule.swift @@ -142,7 +142,7 @@ public class XMTPModule: Module { let client = try await XMTP.Client.create(account: signer, options: options) await clientsManager.updateClient(key: address, client: client) self.signer = nil - sendEvent("authed", ["inboxId": client.inboxID]) + sendEvent("authed", try ClientWrapper.encodeToObj(client)) } Function("receiveSignature") { (requestID: String, signature: String) in @@ -168,10 +168,7 @@ public class XMTPModule: Module { let client = try await Client.create(account: privateKey, options: options) await clientsManager.updateClient(key: client.address, client: client) - return [ - "address": client.address, - "inboxId": client.inboxID - ] + return try ClientWrapper.encodeToObj(client) } // Create a client using its serialized key bundle. @@ -188,10 +185,7 @@ public class XMTPModule: Module { let options = createClientConfig(env: environment, appVersion: appVersion, mlsAlpha: enableAlphaMls == true, encryptionKey: encryptionKeyData, dbDirectory: dbDirectory) let client = try await Client.from(bundle: bundle, options: options) await clientsManager.updateClient(key: client.address, client: client) - return [ - "address": client.address, - "inboxId": client.inboxID - ] + return try ClientWrapper.encodeToObj(client) } catch { print("ERRO! Failed to create client: \(error)") throw error diff --git a/src/lib/Client.ts b/src/lib/Client.ts index 7993781a0..51321e448 100644 --- a/src/lib/Client.ts +++ b/src/lib/Client.ts @@ -30,6 +30,7 @@ export class Client< > { address: string inboxId: string + installationId: string conversations: Conversations contacts: Contacts codecRegistry: { [key: string]: XMTPModule.ContentCodec } @@ -92,13 +93,23 @@ export class Client< this.authSubscription = XMTPModule.emitter.addListener( 'authed', - async (message: { inboxId: string }) => { + async (message: { + inboxId: string + address: string + installationId: string + }) => { this.removeSubscription(enableSubscription) this.removeSubscription(createSubscription) this.removeSignSubscription() this.removeAuthSubscription() - const address = await signer.getAddress() - resolve(new Client(address, message.inboxId, opts?.codecs || [])) + resolve( + new Client( + message.address, + message.inboxId, + message.installationId, + opts?.codecs || [] + ) + ) } ) await XMTPModule.auth( @@ -143,7 +154,7 @@ export class Client< const options = defaultOptions(opts) const { enableSubscription, createSubscription } = this.setupSubscriptions(options) - const addressInboxId = await XMTPModule.createRandom( + const client = await XMTPModule.createRandom( options.env, options.appVersion, Boolean(createSubscription), @@ -156,8 +167,9 @@ export class Client< this.removeSubscription(createSubscription) return new Client( - addressInboxId['address'], - addressInboxId['inboxId'], + client['address'], + client['inboxId'], + client['installationId'], opts?.codecs || [] ) } @@ -179,7 +191,7 @@ export class Client< opts?: Partial & { codecs?: ContentCodecs } ): Promise> { const options = defaultOptions(opts) - const addressInboxId = await XMTPModule.createFromKeyBundle( + const client = await XMTPModule.createFromKeyBundle( keyBundle, options.env, options.appVersion, @@ -189,8 +201,9 @@ export class Client< ) return new Client( - addressInboxId['address'], - addressInboxId['inboxId'], + client['address'], + client['inboxId'], + client['installationId'], opts?.codecs || [] ) } @@ -271,10 +284,12 @@ export class Client< constructor( address: string, inboxId: string, + installationId: string, codecs: XMTPModule.ContentCodec[] = [] ) { this.address = address this.inboxId = inboxId + this.installationId = installationId this.conversations = new Conversations(this) this.contacts = new Contacts(this) this.codecRegistry = {}