diff --git a/android/build.gradle b/android/build.gradle index 5a9df29a..e39d03f4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,6 +5,11 @@ apply plugin: 'maven-publish' group = 'expo.modules.xmtpreactnativesdk' version = '0.1.0' +def isNewArchEnabled = + ((findProperty('newArchEnabled') ?: rootProject.findProperty('newArchEnabled') ?: 'false') + .toString() + .toBoolean()) + buildscript { def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle") if (expoModulesCorePlugin.exists()) { @@ -82,6 +87,7 @@ android { targetSdkVersion safeExtGet("targetSdkVersion", 31) versionCode 1 versionName "0.1.0" + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchEnabled.toString() } lintOptions { abortOnError false diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index 53a93534..736571f8 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -177,6 +177,18 @@ class XMTPModule : Module() { return reactContext } + private fun readHostBuildConfigBoolean(fieldName: String): Boolean? { + val packageName = appContext.reactContext?.applicationContext?.packageName ?: return null + + return try { + val buildConfigClass = Class.forName("$packageName.BuildConfig") + buildConfigClass.getField(fieldName).getBoolean(null) + } catch (e: Throwable) { + logV("Unable to read $fieldName from $packageName.BuildConfig: ${e.message}") + null + } + } + private fun apiEnvironments(env: String, customLocalUrl: String? = null, appVersion: String? = null, gatewayHost: String? = null): ClientOptions.Api { return when (env) { "local" -> { @@ -283,6 +295,22 @@ class XMTPModule : Module() { "messageDeletionClosed" ) + AsyncFunction("getArchitectureDiagnostics") { + val hostPackageName = appContext.reactContext?.applicationContext?.packageName.orEmpty() + val hostNewArchEnabled = readHostBuildConfigBoolean("IS_NEW_ARCHITECTURE_ENABLED") + + mapOf( + "platform" to "android", + "moduleName" to "XMTP", + "moduleType" to "expo-module", + "moduleClassName" to this@XMTPModule::class.java.name, + "hostAppId" to hostPackageName, + "isNewArchitectureEnabled" to (hostNewArchEnabled ?: BuildConfig.IS_NEW_ARCHITECTURE_ENABLED), + "newArchitectureFlagSource" to "BuildConfig.IS_NEW_ARCHITECTURE_ENABLED", + "newArchitectureFlagProvider" to if (hostNewArchEnabled != null) "host-app" else "xmtp-module", + ) + } + Function("inboxId") { installationId: String -> logV("inboxId") val client = clients[installationId] diff --git a/example/App.tsx b/example/App.tsx index 55a907ab..fb18cb01 100644 --- a/example/App.tsx +++ b/example/App.tsx @@ -1,5 +1,6 @@ import { NavigationContainer } from '@react-navigation/native' // import { Ethereum } from '@thirdweb-dev/chains' +import React, { useEffect, useState } from 'react' import 'react-native-get-random-values' import '@ethersproject/shims' import { Buffer as BufferPolyfill } from 'buffer' @@ -21,12 +22,9 @@ import { StyleSheet, FlatList, } from 'react-native' -import Config from 'react-native-config' // Used to polyfill webCrypto in react-native import { QueryClient, QueryClientProvider } from 'react-query' import { XmtpProvider, Client } from 'xmtp-react-native-sdk' -import { useState, useEffect } from 'react' -import React from 'react' import ConversationCreateScreen from './src/ConversationCreateScreen' import ConversationScreen from './src/ConversationScreen' @@ -135,7 +133,9 @@ const LogFilesModal: React.FC = ({ visible, onClose }) => { } } - fetchLogFiles() + fetchLogFiles().catch((error) => { + console.error('Unexpected error fetching log files:', error) + }) } }, [visible]) @@ -347,8 +347,53 @@ const AndroidDropdown: React.FC = ({ } export default function App() { - // Uncomment below to ensure correct id loaded from .env - // console.log("Thirdweb client id: " + Config.THIRD_WEB_CLIENT_ID) + // New Architecture verification logging + useEffect(() => { + const checkNewArchitecture = async () => { + const diagnostics = await Client.getArchitectureDiagnostics() + + console.log('=== XMTP ARCHITECTURE DIAGNOSTICS ===') + console.log( + ` Native new architecture flag (${diagnostics.newArchitectureFlagSource} via ${diagnostics.newArchitectureFlagProvider}): ${ + diagnostics.isNewArchitectureEnabled ? 'ENABLED' : 'DISABLED' + }` + ) + console.log( + ` XMTP module access path: ${diagnostics.moduleAccess.toUpperCase()}` + ) + console.log( + ` Supports synchronous native methods: ${ + diagnostics.supportsSynchronousFunctions ? 'YES' : 'NO' + }` + ) + console.log(` Native module type: ${diagnostics.moduleType}`) + console.log(` Native module class: ${diagnostics.moduleClassName}`) + console.log(` Host app id: ${diagnostics.hostAppId}`) + console.log(` Platform: ${diagnostics.platform}`) + console.log('=====================================') + + if ( + diagnostics.isNewArchitectureEnabled && + diagnostics.moduleAccess === 'jsi' + ) { + console.log( + 'XMTP is running in a new-architecture app and is being resolved through the JSI module registry.' + ) + } else if (diagnostics.isNewArchitectureEnabled) { + console.log( + 'The app is built with the new architecture, but XMTP is currently being accessed through the bridge proxy.' + ) + } else { + console.log( + 'The app is not built with the new architecture, so XMTP cannot use the new-architecture runtime path.' + ) + } + } + + checkNewArchitecture().catch((error) => { + console.error('Failed to read XMTP architecture diagnostics:', error) + }) + }, []) const [showAndroidDropdown, setShowAndroidDropdown] = useState(false) const [showLogFilesModal, setShowLogFilesModal] = useState(false) @@ -362,7 +407,7 @@ export default function App() { return } - let successCount = await Client.clearXMTPLogs() + const successCount = await Client.clearXMTPLogs() if (successCount === files.length) { alert('All log files cleared successfully') @@ -463,7 +508,12 @@ export default function App() { }, (buttonIndex) => { if (buttonIndex === 0) { - clearLogFiles() + clearLogFiles().catch((error) => { + console.error( + 'Unexpected error clearing log files:', + error + ) + }) } } ) diff --git a/example/app.json b/example/app.json index a2880d74..7a3fe123 100644 --- a/example/app.json +++ b/example/app.json @@ -1,5 +1,6 @@ { "expo": { + "newArchEnabled": true, "name": "xmtp-react-native-sdk-example", "slug": "xmtp-react-native-sdk-example", "version": "1.0.0", diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 390a2799..3c602116 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1348,7 +1348,26 @@ PODS: - ReactCommon/turbomodule/core - Yoga - react-native-blob-util (0.19.11): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - react-native-config (1.5.5): - react-native-config/App (= 1.5.5) - react-native-config/App (1.5.5): @@ -1426,7 +1445,71 @@ PODS: - react-native-randombytes (3.6.1): - React-Core - react-native-safe-area-context (4.12.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - react-native-safe-area-context/common (= 4.12.0) + - react-native-safe-area-context/fabric (= 4.12.0) + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - react-native-safe-area-context/common (4.12.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - react-native-safe-area-context/fabric (4.12.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - react-native-safe-area-context/common + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - react-native-sqlite-storage (6.0.1): - React-Core - react-native-webview (13.12.5): @@ -1723,10 +1806,52 @@ PODS: - React-perflogger - React-utils (= 0.76.9) - RNCAsyncStorage (1.23.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - RNFS (2.20.0): - React-Core - RNScreens (4.4.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-RCTImage + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNScreens/common (= 4.4.0) + - Yoga + - RNScreens/common (4.4.0): - DoubleConversion - glog - hermes-engine @@ -1749,7 +1874,48 @@ PODS: - ReactCommon/turbomodule/core - Yoga - RNSVG (15.8.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - RNSVG/common (= 15.8.0) + - Yoga + - RNSVG/common (15.8.0): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - SocketRocket (0.7.1) - SQLCipher (4.5.7): - SQLCipher/standard (= 4.5.7) @@ -1761,7 +1927,7 @@ PODS: - Connect-Swift (~> 1.2.0) - CryptoSwift (= 1.8.3) - SQLCipher (= 4.5.7) - - XMTPReactNative (5.6.0): + - XMTPReactNative (5.7.0): - CSecp256k1 (~> 0.2) - ExpoModulesCore - MessagePacker @@ -2092,9 +2258,9 @@ SPEC CHECKSUMS: ExpoFont: 773955186469acc5108ff569712a2d243857475f ExpoImagePicker: 482b2a6198b365dd18b5a0cb6d4caeec880cb8e1 ExpoKeepAwake: 2a5f15dd4964cba8002c9a36676319a3394c85c7 - ExpoModulesCore: c2eeb11b2fc321dfc21b892be14c124dcac0a1e8 + ExpoModulesCore: dd965804a882f1dbb6036fceea4d912461aeaa0d ExpoSecureStore: d006eea5e316283099d46f80a6b10055b89a6008 - ExpoSplashScreen: 1832984021b0795fda9302cf84ac62f0490eeadd + ExpoSplashScreen: 0e176e85073dfb9686c100c7f9637975d00608dd ExpoSystemUI: fb8213e39d19e0861320fa69eb60cad7a839c080 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 @@ -2115,16 +2281,16 @@ SPEC CHECKSUMS: React-CoreModules: 907334e94314189c2e5eed4877f3efe7b26d85b0 React-cxxreact: 3a1d5e8f4faa5e09be26614e9c8bbcae8d11b73d React-debug: 817160c07dc8d24d020fbd1eac7b3558ffc08964 - React-defaultsnativemodule: a965cb39fb0a79276ab611793d39f52e59a9a851 - React-domnativemodule: d647f94e503c62c44f54291334b1aa22a30fa08b + React-defaultsnativemodule: 814830ccbc3fb08d67d0190e63b179ee4098c67b + React-domnativemodule: 270acf94bd0960b026bc3bfb327e703665d27fb4 React-Fabric: 64586dc191fc1c170372a638b8e722e4f1d0a09b React-FabricComponents: b0ebd032387468ea700574c581b139f57a7497fb React-FabricImage: 81f0e0794caf25ad1224fa406d288fbc1986607f React-featureflags: f2792b067a351d86fdc7bec23db3b9a2f2c8d26c - React-featureflagsnativemodule: 95a02d895475de8ace78fedd76143866838bb720 + React-featureflagsnativemodule: 0d7091ae344d6160c0557048e127897654a5c00f React-graphics: cbebe910e4a15b65b0bff94a4d3ed278894d6386 React-hermes: ec18c10f5a69d49fb9b5e17ae95494e9ea13d4d3 - React-idlecallbacksnativemodule: 0c1ae840cc5587197cd926a3cb76828ad059d116 + React-idlecallbacksnativemodule: 6b84add48971da9c40403bd1860d4896462590f2 React-ImageManager: f2a4c01c2ccb2193e60a20c135da74c7ca4d36f2 React-jserrorhandler: 61d205b5a7cbc57fed3371dd7eed48c97f49fc64 React-jsi: 95f7676103137861b79b0f319467627bcfa629ee @@ -2133,27 +2299,27 @@ SPEC CHECKSUMS: React-jsitracing: 3758cdb155ea7711f0e77952572ea62d90c69f0b React-logger: dbca7bdfd4aa5ef69431362bde6b36d49403cb20 React-Mapbuffer: 6efad4a606c1fae7e4a93385ee096681ef0300dc - React-microtasksnativemodule: 8732b71aa66045da4bb341ddee1bb539f71e5f38 - react-native-blob-util: 39a20f2ef11556d958dc4beb0aa07d1ef2690745 + React-microtasksnativemodule: a645237a841d733861c70b69908ab4a1707b52ad + react-native-blob-util: ab2378ecfc93511a7947bd10fdf95da8cfd2e2ff react-native-config: 3367df9c1f25bb96197007ec531c7087ed4554c3 react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06 - react-native-mmkv: e842cad766fc2ad46e70e161f4bbaf0b7e90d41d + react-native-mmkv: 5f158bf698010348037b0309c872ba17fe518b29 react-native-netinfo: f0a9899081c185db1de5bb2fdc1c88c202a059ac - react-native-quick-base64: 764b8014da7dc834e5b8ad756c980addf919c177 - react-native-quick-crypto: 4a5011fb16940bf07059cb6d3f3388da95d77813 + react-native-quick-base64: bc78760fe74909778662f104c101eb950fe5509d + react-native-quick-crypto: e4460bc2d6533324358db47068741166b06230e9 react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846 - react-native-safe-area-context: 142fade490cbebbe428640b8cbdb09daf17e8191 + react-native-safe-area-context: 0dfbd139206a79364916959bc8b1a6bea3caba97 react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261 - react-native-webview: 0a19ebf8e1b6f1a5689bdd771ca158dcc88c5ef4 + react-native-webview: b8f9b1f9bdb081c239e46d9bf030881d2678f25d React-nativeconfig: 8efdb1ef1e9158c77098a93085438f7e7b463678 React-NativeModulesApple: 958d4f6c5c2ace4c0f427cf7ef82e28ae6538a22 React-perflogger: 9b4f13c0afe56bc7b4a0e93ec74b1150421ee22d React-performancetimeline: 359db1cb889aa0282fafc5838331b0987c4915a9 React-RCTActionSheet: aacf2375084dea6e7c221f4a727e579f732ff342 React-RCTAnimation: d8c82deebebe3aaf7a843affac1b57cb2dc073d4 - React-RCTAppDelegate: 6c0377d9c4058773ea7073bb34bb9ebd6ddf5a84 + React-RCTAppDelegate: 1774aa421a29a41a704ecaf789811ef73c4634b6 React-RCTBlob: 70a58c11a6a3500d1a12f2e51ca4f6c99babcff8 - React-RCTFabric: 7eb6dd2c8fda98cb860a572e3f4e4eb60d62c89e + React-RCTFabric: 731cda82aed592aacce2d32ead69d78cde5d9274 React-RCTImage: 5e9d655ba6a790c31e3176016f9b47fd0978fbf0 React-RCTLinking: 2a48338252805091f7521eaf92687206401bdf2a React-RCTNetwork: 0c1282b377257f6b1c81934f72d8a1d0c010e4c3 @@ -2172,15 +2338,15 @@ SPEC CHECKSUMS: React-utils: 54df9ada708578c8ad40d92895d6fed03e0e8a9e ReactCodegen: 21a52ccddc6479448fc91903a437dd23ddc7366c ReactCommon: bfd3600989d79bc3acbe7704161b171a1480b9fd - RNCAsyncStorage: 826b603ae9c0f88b5ac4e956801f755109fa4d5c + RNCAsyncStorage: ac02697867ba18d2cfd761ebb144417fe65b210e RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 - RNScreens: 02c4adf5b4820807807b1d7d4f8bc27eeaed8e11 - RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d + RNScreens: 2856a2abe759a5a56946874f76372c9f033af19a + RNSVG: 81d52481cde97ce0dcc81a55b0310723817088d0 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 SQLCipher: 5e6bfb47323635c8b657b1b27d25c5f1baf63bf5 SwiftProtobuf: 3697407f0d5b23bedeba9c2eaaf3ec6fdff69349 XMTP: 40a323abd37322a4d0c323a1fd97e9a67ce9c16f - XMTPReactNative: f62ff8302fb02c611cae0be0981021d6f3426036 + XMTPReactNative: d675648285bc8064dbc96cd22e183fc6304511dc Yoga: 40f19fff64dce86773bf8b602c7070796c007970 PODFILE CHECKSUM: c76510e65e7d9673f44024ae2d0a10eec063a555 diff --git a/ios/XMTPModule.swift b/ios/XMTPModule.swift index bd7013bd..12621f5a 100644 --- a/ios/XMTPModule.swift +++ b/ios/XMTPModule.swift @@ -2,6 +2,14 @@ import ExpoModulesCore import OSLog import XMTP +private let xmtpIsNewArchitectureEnabled: Bool = { + #if RCT_NEW_ARCH_ENABLED + return true + #else + return false + #endif +}() + extension Conversation { static func cacheKeyForTopic(installationId: String, topic: String) -> String @@ -152,6 +160,19 @@ public class XMTPModule: Module { "messageDeletionClosed" ) + AsyncFunction("getArchitectureDiagnostics") { () -> [String: Any] in + return [ + "platform": "ios", + "moduleName": "XMTP", + "moduleType": "expo-module", + "moduleClassName": String(describing: type(of: self)), + "hostAppId": Bundle.main.bundleIdentifier ?? "", + "isNewArchitectureEnabled": xmtpIsNewArchitectureEnabled, + "newArchitectureFlagSource": "RCT_NEW_ARCH_ENABLED", + "newArchitectureFlagProvider": "xmtp-module", + ] + } + AsyncFunction("inboxId") { (installationId: String) -> String in if let client = await clientsManager.getClient(key: installationId) { return client.inboxID diff --git a/ios/XMTPReactNative.podspec b/ios/XMTPReactNative.podspec index 4d7d6af5..60e9bfbf 100644 --- a/ios/XMTPReactNative.podspec +++ b/ios/XMTPReactNative.podspec @@ -1,6 +1,8 @@ require 'json' package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json'))) +swift_flags = ['$(inherited)'] +swift_flags << '-D RCT_NEW_ARCH_ENABLED' if ENV['RCT_NEW_ARCH_ENABLED'] == '1' Pod::Spec.new do |s| s.name = 'XMTPReactNative' @@ -20,7 +22,8 @@ Pod::Spec.new do |s| # Swift/Objective-C compatibility s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', - 'SWIFT_COMPILATION_MODE' => 'wholemodule' + 'SWIFT_COMPILATION_MODE' => 'wholemodule', + 'OTHER_SWIFT_FLAGS' => swift_flags.join(' ') } s.source_files = "**/*.{h,m,swift}" diff --git a/src/XMTPModule.ts b/src/XMTPModule.ts index 963582c1..6c921b21 100644 --- a/src/XMTPModule.ts +++ b/src/XMTPModule.ts @@ -1,5 +1,28 @@ -import { requireNativeModule } from 'expo-modules-core' +import { NativeModulesProxy, requireNativeModule } from 'expo-modules-core' + +import type { + NativeXMTPArchitectureDiagnostics, + XMTPModuleAccess, +} from './lib/XMTPArchitectureDiagnostics' // It loads the native module object from the JSI or falls back to // the bridge module (from NativeModulesProxy) if the remote debugger is on. -export default requireNativeModule('XMTP') +const XMTPModule = requireNativeModule('XMTP') + +export function getXMTPModuleAccess(): XMTPModuleAccess { + if ((globalThis as any).expo?.modules?.XMTP) { + return 'jsi' + } + + if (NativeModulesProxy.XMTP) { + return 'bridge' + } + + return 'bridge' +} + +export function getNativeArchitectureDiagnostics(): Promise { + return XMTPModule.getArchitectureDiagnostics() +} + +export default XMTPModule diff --git a/src/index.ts b/src/index.ts index dec732a0..90ef31ab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,10 @@ import { content, keystore } from '@xmtp/proto' import { EventEmitter, NativeModulesProxy } from 'expo-modules-core' -import XMTPModule from './XMTPModule' +import XMTPModule, { + getNativeArchitectureDiagnostics, + getXMTPModuleAccess, +} from './XMTPModule' import { ArchiveMetadata, AvailableArchive } from './lib/ArchiveOptions' import { Address, @@ -29,6 +32,10 @@ import { InboxState } from './lib/InboxState' import { Member, MembershipResult } from './lib/Member' import { PublicIdentity } from './lib/PublicIdentity' import { SignerType } from './lib/Signer' +import { + type XMTPArchitectureDiagnostics, + toArchitectureDiagnostics, +} from './lib/XMTPArchitectureDiagnostics' import { KeyPackageStatuses, NetworkDebugInfo, @@ -56,6 +63,7 @@ import { PermissionPolicySet } from './lib/types/PermissionPolicySet' export * from './context' export * from './hooks' +export * from './lib/XMTPArchitectureDiagnostics' export { GroupUpdatedCodec } from './lib/NativeCodecs/GroupUpdatedCodec' export { ReactionCodec } from './lib/NativeCodecs/ReactionCodec' export { ReactionV2Codec } from './lib/NativeCodecs/ReactionV2Codec' @@ -1957,6 +1965,11 @@ export async function getNetworkDebugInformation( return NetworkDebugInfo.from(info) } +export async function getArchitectureDiagnostics(): Promise { + const info = await getNativeArchitectureDiagnostics() + return toArchitectureDiagnostics(info, getXMTPModuleAccess()) +} + export async function clearAllNetworkStatistics( installationId: InstallationId ): Promise { diff --git a/src/lib/Client.ts b/src/lib/Client.ts index e2bd794b..265d0344 100644 --- a/src/lib/Client.ts +++ b/src/lib/Client.ts @@ -9,6 +9,7 @@ import type { } from './ContentCodec' import Conversations from './Conversations' import { InboxState } from './InboxState' +import type { XMTPArchitectureDiagnostics } from './XMTPArchitectureDiagnostics' import { TextCodec } from './NativeCodecs/TextCodec' import PrivatePreferences from './PrivatePreferences' import { PublicIdentity } from './PublicIdentity' @@ -50,6 +51,10 @@ export class Client< return XMTPModule.exportNativeLogs() } + static async getArchitectureDiagnostics(): Promise { + return XMTPModule.getArchitectureDiagnostics() + } + private static removeAllSubscriptions( authInboxSubscription?: Subscription ): void { diff --git a/src/lib/XMTPArchitectureDiagnostics.ts b/src/lib/XMTPArchitectureDiagnostics.ts new file mode 100644 index 00000000..f62cffd2 --- /dev/null +++ b/src/lib/XMTPArchitectureDiagnostics.ts @@ -0,0 +1,35 @@ +export type XMTPModuleAccess = 'jsi' | 'bridge' + +export type NewArchitectureFlagSource = + | 'BuildConfig.IS_NEW_ARCHITECTURE_ENABLED' + | 'RCT_NEW_ARCH_ENABLED' + +export type NewArchitectureFlagProvider = 'host-app' | 'xmtp-module' + +export interface NativeXMTPArchitectureDiagnostics { + platform: 'android' | 'ios' + moduleName: string + moduleType: 'expo-module' + moduleClassName: string + hostAppId: string + isNewArchitectureEnabled: boolean + newArchitectureFlagSource: NewArchitectureFlagSource + newArchitectureFlagProvider: NewArchitectureFlagProvider +} + +export interface XMTPArchitectureDiagnostics + extends NativeXMTPArchitectureDiagnostics { + moduleAccess: XMTPModuleAccess + supportsSynchronousFunctions: boolean +} + +export function toArchitectureDiagnostics( + diagnostics: NativeXMTPArchitectureDiagnostics, + moduleAccess: XMTPModuleAccess +): XMTPArchitectureDiagnostics { + return { + ...diagnostics, + moduleAccess, + supportsSynchronousFunctions: moduleAccess === 'jsi', + } +}