Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove client from serializable objects & Revoke installations #586

Merged
merged 8 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/neat-insects-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@xmtp/react-native-sdk": patch
---

- Remove client from serializable objects
- Ability to revoke installations
- Static inboxStates for inboxIds
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ repositories {
dependencies {
implementation project(':expo-modules-core')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
implementation "org.xmtp:android:3.0.20"
implementation "org.xmtp:android:3.0.21"
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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,26 @@ class XMTPModule : Module() {
}
}

AsyncFunction("revokeInstallations") Coroutine { installationId: String, walletParams: String, installationIds: List<String> ->
withContext(Dispatchers.IO) {
logV("revokeInstallations")
val client = clients[installationId] ?: throw XMTPException("No client")
val walletOptions = WalletParamsWrapper.walletParamsFromJson(walletParams)
val reactSigner =
ReactNativeSigner(
module = this@XMTPModule,
address = client.address,
type = walletOptions.walletType,
chainId = walletOptions.chainId,
blockNumber = walletOptions.blockNumber
)
signer = reactSigner

client.revokeInstallations(reactSigner, installationIds)
signer = null
}
}

AsyncFunction("revokeAllOtherInstallations") Coroutine { installationId: String, walletParams: String ->
withContext(Dispatchers.IO) {
logV("revokeAllOtherInstallations")
Expand Down Expand Up @@ -450,12 +470,22 @@ class XMTPModule : Module() {
logV("staticCanMessage")
Client.canMessage(
peerAddresses,
context,
apiEnvironments(environment, null),
)
}
}

AsyncFunction("staticInboxStatesForInboxIds") Coroutine { environment: String, inboxIds: List<String> ->
withContext(Dispatchers.IO) {
logV("staticInboxStatesForInboxIds")
val inboxStates = Client.inboxStatesForInboxIds(
inboxIds,
apiEnvironments(environment, null),
)
inboxStates.map { InboxStateWrapper.encode(it) }
}
}

AsyncFunction("getOrCreateInboxId") Coroutine { address: String, environment: String ->
withContext(Dispatchers.IO) {
try {
Expand Down
10 changes: 5 additions & 5 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -448,18 +448,18 @@ PODS:
- SQLCipher/standard (4.5.7):
- SQLCipher/common
- SwiftProtobuf (1.28.2)
- XMTP (3.0.22):
- XMTP (3.0.23):
- Connect-Swift (= 1.0.0)
- CryptoSwift (= 1.8.3)
- CSecp256k1 (~> 0.2)
- LibXMTP (= 3.0.18)
- SQLCipher (= 4.5.7)
- XMTPReactNative (3.1.5):
- XMTPReactNative (3.1.6):
- CSecp256k1 (~> 0.2)
- ExpoModulesCore
- MessagePacker
- SQLCipher (= 4.5.7)
- XMTP (= 3.0.22)
- XMTP (= 3.0.23)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -762,8 +762,8 @@ SPEC CHECKSUMS:
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396
SQLCipher: 5e6bfb47323635c8b657b1b27d25c5f1baf63bf5
SwiftProtobuf: 4dbaffec76a39a8dc5da23b40af1a5dc01a4c02d
XMTP: c2049f379dd2a799921466cb0720690b786a2958
XMTPReactNative: f8a2a80d316d2b381604131448462eb56baffbfb
XMTP: 645cb707d5d779b896d00b5f547227325a7379a7
XMTPReactNative: 9b25afc1fb008aa92d83d957263e8a14507efefc
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9

PODFILE CHECKSUM: 0e6fe50018f34e575d38dc6a1fdf1f99c9596cdd
Expand Down
3 changes: 2 additions & 1 deletion example/src/ConversationScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
StaticAttachmentContent,
ReplyContent,
useClient,
Client,
} from 'xmtp-react-native-sdk'
import { ConversationSendPayload } from 'xmtp-react-native-sdk/lib/types'

Expand Down Expand Up @@ -1077,7 +1078,7 @@
contentTypeId: string
content: any
}) {
const { client } = useClient<SupportedContentTypes>()

Check warning on line 1081 in example/src/ConversationScreen.tsx

View workflow job for this annotation

GitHub Actions / lint

'client' is assigned a value but never used

if (contentTypeId === 'xmtp.org/text:1.0') {
const text: string = content
Expand Down Expand Up @@ -1111,7 +1112,7 @@
if (contentTypeId === 'xmtp.org/reply:1.0') {
const replyContent: ReplyContent = content
const replyContentType = replyContent.contentType
const codec = client?.codecRegistry[replyContentType]
const codec = Client.codecRegistry[replyContentType]
const actualReplyContent = codec?.decode(replyContent.content)

return (
Expand Down
4 changes: 2 additions & 2 deletions example/src/GroupScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
ReplyContent,
useClient,
GroupUpdatedContent,
Client,
} from 'xmtp-react-native-sdk'
import { ConversationSendPayload } from 'xmtp-react-native-sdk/lib/types'

Expand Down Expand Up @@ -1122,7 +1123,7 @@
contentTypeId: string
content: any
}) {
const { client } = useClient<SupportedContentTypes>()

Check warning on line 1126 in example/src/GroupScreen.tsx

View workflow job for this annotation

GitHub Actions / lint

'client' is assigned a value but never used

if (contentTypeId === 'xmtp.org/text:1.0') {
const text: string = content
Expand Down Expand Up @@ -1155,8 +1156,7 @@

if (contentTypeId === 'xmtp.org/reply:1.0') {
const replyContent: ReplyContent = content
const replyContentType = replyContent.contentType
const codec = client?.codecRegistry[replyContentType]
const codec = Client.codecRegistry[contentTypeId]
const actualReplyContent = codec?.decode(replyContent.content)

return (
Expand Down
112 changes: 101 additions & 11 deletions example/src/tests/clientTests.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Wallet } from 'ethers'
import RNFS from 'react-native-fs'

import { Test, assert, createClients } from './test-utils'
import {
Test,
assert,
createClients,
adaptEthersWalletToSigner,
} from './test-utils'
import { Client } from '../../../src/index'

export const clientTests: Test[] = []
Expand Down Expand Up @@ -62,37 +67,119 @@ test('static can message', async () => {
return true
})

test('can revoke all other installations', async () => {
test('static inboxStates for inboxIds', async () => {
const [alix, bo] = await createClients(2)

const inboxStates = await Client.inboxStatesForInboxIds('local', [
alix.inboxId,
bo.inboxId,
])

assert(
inboxStates[0].recoveryAddress.toLowerCase === alix.address.toLowerCase,
`inbox state should be ${alix.address.toLowerCase} but was ${inboxStates[0].recoveryAddress.toLowerCase}`
)

assert(
inboxStates[1].recoveryAddress.toLowerCase === bo.address.toLowerCase,
`inbox state should be ${bo.address.toLowerCase} but was ${inboxStates[1].recoveryAddress.toLowerCase}`
)

return true
})

test('can revoke 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,
])
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const dbDirPath = `${RNFS.DocumentDirectoryPath}/xmtp_db`
const dbDirPath2 = `${RNFS.DocumentDirectoryPath}/xmtp_db2`
const dbDirPath3 = `${RNFS.DocumentDirectoryPath}/xmtp_db3`
const directoryExists = await RNFS.exists(dbDirPath)
if (!directoryExists) {
await RNFS.mkdir(dbDirPath)
}
const directoryExists2 = await RNFS.exists(dbDirPath2)
if (!directoryExists2) {
await RNFS.mkdir(dbDirPath2)
}
const directoryExists3 = await RNFS.exists(dbDirPath3)
if (!directoryExists3) {
await RNFS.mkdir(dbDirPath3)
}
const alixWallet = Wallet.createRandom()

// create a v3 client
const alix = await Client.create(alixWallet, {
const alix = await Client.create(adaptEthersWalletToSigner(alixWallet), {
env: 'local',
appVersion: 'Testing/0.0.0',
dbEncryptionKey: keyBytes,
dbDirectory: dbDirPath,
})

const alix2 = await Client.create(adaptEthersWalletToSigner(alixWallet), {
env: 'local',
appVersion: 'Testing/0.0.0',
dbEncryptionKey: keyBytes,
dbDirectory: dbDirPath2,
})

const alix3 = await Client.create(adaptEthersWalletToSigner(alixWallet), {
env: 'local',
appVersion: 'Testing/0.0.0',
dbEncryptionKey: keyBytes,
dbDirectory: dbDirPath3,
})

const inboxState2 = await alix3.inboxState(true)
assert(
inboxState2.installations.length === 3,
`installations length should be 3 but was ${inboxState2.installations.length}`
)

await alix3.revokeInstallations(adaptEthersWalletToSigner(alixWallet), [
alix2.installationId,
])

const inboxState3 = await alix3.inboxState(true)
assert(
inboxState3.installations.length === 2,
`installations length should be 2 but was ${inboxState3.installations.length}`
)
await alix.deleteLocalDatabase()
await alix2.deleteLocalDatabase()
await alix3.deleteLocalDatabase()

const alix2 = await Client.create(alixWallet, {
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(adaptEthersWalletToSigner(alixWallet), {
env: 'local',
appVersion: 'Testing/0.0.0',
dbEncryptionKey: keyBytes,
})

await Client.build(alix2.address, {
await alix.deleteLocalDatabase()

const alix2 = await Client.create(adaptEthersWalletToSigner(alixWallet), {
env: 'local',
appVersion: 'Testing/0.0.0',
dbEncryptionKey: keyBytes,
})

await alix2.deleteLocalDatabase()

const alix3 = await Client.create(alixWallet, {
const alix3 = await Client.create(adaptEthersWalletToSigner(alixWallet), {
env: 'local',
appVersion: 'Testing/0.0.0',
dbEncryptionKey: keyBytes,
Expand All @@ -104,7 +191,7 @@ test('can revoke all other installations', async () => {
`installations length should be 3 but was ${inboxState2.installations.length}`
)

await alix3.revokeAllOtherInstallations(alixWallet)
await alix3.revokeAllOtherInstallations(adaptEthersWalletToSigner(alixWallet))

const inboxState3 = await alix3.inboxState(true)
assert(
Expand Down Expand Up @@ -358,14 +445,14 @@ test('can add and remove accounts', async () => {
const alixWallet2 = Wallet.createRandom()
const alixWallet3 = Wallet.createRandom()

const alix = await Client.create(alixWallet, {
const alix = await Client.create(adaptEthersWalletToSigner(alixWallet), {
env: 'local',
appVersion: 'Testing/0.0.0',
dbEncryptionKey: keyBytes,
})

await alix.addAccount(alixWallet2)
await alix.addAccount(alixWallet3)
await alix.addAccount(adaptEthersWalletToSigner(alixWallet2))
await alix.addAccount(adaptEthersWalletToSigner(alixWallet3))

const inboxState = await alix.inboxState(true)
assert(
Expand All @@ -381,7 +468,10 @@ test('can add and remove accounts', async () => {
`recovery address should be ${alix.address} but was ${inboxState.recoveryAddress}`
)

await alix.removeAccount(alixWallet, await alixWallet3.getAddress())
await alix.removeAccount(
adaptEthersWalletToSigner(alixWallet),
await alixWallet3.getAddress()
)
const inboxState2 = await alix.inboxState(true)
assert(
inboxState2.addresses.length === 2,
Expand Down
Loading
Loading