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

Add ability to stream consent #546

Merged
merged 14 commits into from
Nov 26, 2024
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.8"
implementation "org.xmtp:android:3.0.10"
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
460 changes: 250 additions & 210 deletions android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package expo.modules.xmtpreactnativesdk.wrappers

import com.google.gson.GsonBuilder
import org.xmtp.android.library.ConsentRecord
import org.xmtp.android.library.ConsentState

class ConsentWrapper {

companion object {
fun encode(model: ConsentRecord): String {
val gson = GsonBuilder().create()
val message = encodeMap(model)
return gson.toJson(message)
}

fun encodeMap(model: ConsentRecord): Map<String, Any> = mapOf(
"type" to model.entryType.name.lowercase(),
"value" to model.value.lowercase(),
"state" to consentStateToString(model.consentType),
)

fun consentStateToString(state: ConsentState): String {
return when (state) {
ConsentState.ALLOWED -> "allowed"
ConsentState.DENIED -> "denied"
ConsentState.UNKNOWN -> "unknown"
}
}
}
}
8 changes: 4 additions & 4 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ PODS:
- RNSVG (13.14.0):
- React-Core
- SwiftProtobuf (1.28.2)
- XMTP (3.0.8):
- XMTP (3.0.10):
- Connect-Swift (= 1.0.0)
- CryptoSwift (= 1.8.3)
- CSecp256k1 (~> 0.2)
Expand All @@ -450,7 +450,7 @@ PODS:
- CSecp256k1 (~> 0.2)
- ExpoModulesCore
- MessagePacker
- XMTP (= 3.0.8)
- XMTP (= 3.0.10)
- Yoga (1.14.0)

DEPENDENCIES:
Expand Down Expand Up @@ -747,8 +747,8 @@ SPEC CHECKSUMS:
RNScreens: 218801c16a2782546d30bd2026bb625c0302d70f
RNSVG: d00c8f91c3cbf6d476451313a18f04d220d4f396
SwiftProtobuf: 4dbaffec76a39a8dc5da23b40af1a5dc01a4c02d
XMTP: f8ee66c4aacc5750ab0f67f5bb86c05cd2513185
XMTPReactNative: 8cda2ca09be81cee8c26d986db65fc6faef57bea
XMTP: 22bd60ccd58801953c1bbc7619b0046b2eef5b97
XMTPReactNative: 48e8bbbb9c1800cd507121bd9918aa4a571e8237
Yoga: e71803b4c1fff832ccf9b92541e00f9b873119b9

PODFILE CHECKSUM: 0e6fe50018f34e575d38dc6a1fdf1f99c9596cdd
Expand Down
2 changes: 1 addition & 1 deletion example/src/tests/clientTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ test('can drop client from memory', async () => {
await anotherClient.dropLocalDatabaseConnection()

await client.reconnectLocalDatabase()
await Client.dropClient(anotherClient.inboxId)
await Client.dropClient(anotherClient.installationId)
try {
await anotherClient.reconnectLocalDatabase()
return false
Expand Down
157 changes: 157 additions & 0 deletions example/src/tests/conversationTests.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import RNFS from 'react-native-fs'

Check warning on line 1 in example/src/tests/conversationTests.ts

View workflow job for this annotation

GitHub Actions / lint

There should be at least one empty line between import groups
import { Test, assert, createClients, delayToPropogate } from './test-utils'
import {

Check warning on line 3 in example/src/tests/conversationTests.ts

View workflow job for this annotation

GitHub Actions / lint

There should be at least one empty line between import groups
Client,
ConsentRecord,
Conversation,
ConversationId,
ConversationVersion,
} from '../../../src/index'
import { Wallet } from 'ethers'

Check warning on line 10 in example/src/tests/conversationTests.ts

View workflow job for this annotation

GitHub Actions / lint

`ethers` import should occur before import of `react-native-fs`

export const conversationTests: Test[] = []
let counter = 1
Expand Down Expand Up @@ -547,3 +551,156 @@

return true
})

test('can sync consent', async () => {
const [bo] = await createClients(1)
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 directoryExists = await RNFS.exists(dbDirPath)
if (!directoryExists) {
await RNFS.mkdir(dbDirPath)
}
const directoryExists2 = await RNFS.exists(dbDirPath2)
if (!directoryExists2) {
await RNFS.mkdir(dbDirPath2)
}
const alixWallet = Wallet.createRandom()

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

// Create DM conversation
const dm = await alix.conversations.findOrCreateDm(bo.address)
await dm.updateConsent('denied')
const consentState = await dm.consentState()
assert(consentState === 'denied', `Expected 'denied', got ${consentState}`)

await bo.conversations.sync()
const boDm = await bo.conversations.findConversation(dm.id)

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

const state = await alix2.inboxState(true)
assert(
state.installations.length === 2,
`Expected 2 installations, got ${state.installations.length}`
)

// Sync conversations
await bo.conversations.sync()
if (boDm) await boDm.sync()
await alix.conversations.sync()
await alix2.conversations.sync()
await alix2.preferences.syncConsent()
await alix.conversations.syncAllConversations()
await delayToPropogate(2000)
await alix2.conversations.syncAllConversations()
await delayToPropogate(2000)

const dm2 = await alix2.conversations.findConversation(dm.id)
const consentState2 = await dm2?.consentState()
assert(consentState2 === 'denied', `Expected 'denied', got ${consentState2}`)

await alix2.preferences.setConsentState(
new ConsentRecord(dm2!.id, 'conversation_id', 'allowed')
)

const convoState = await alix2.preferences.conversationConsentState(dm2!.id)
assert(convoState === 'allowed', `Expected 'allowed', got ${convoState}`)

const updatedConsentState = await dm2?.consentState()
assert(
updatedConsentState === 'allowed',
`Expected 'allowed', got ${updatedConsentState}`
)

return true
})

test('can stream consent', async () => {
const [bo] = await createClients(1)
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 dbDirPath = `${RNFS.DocumentDirectoryPath}/xmtp_db`
const dbDirPath2 = `${RNFS.DocumentDirectoryPath}/xmtp_db2`

// Ensure the directories exist
if (!(await RNFS.exists(dbDirPath))) {
await RNFS.mkdir(dbDirPath)
}
if (!(await RNFS.exists(dbDirPath2))) {
await RNFS.mkdir(dbDirPath2)
}

const alixWallet = Wallet.createRandom()

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

const alixGroup = await alix.conversations.newGroup([bo.address])

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

await alixGroup.send('Hello')
await alix.conversations.sync()
await alix2.conversations.sync()
await alix.conversations.syncAllConversations()
await alix2.conversations.syncAllConversations()

const alix2Group = await alix2.conversations.findConversation(alixGroup.id)
await delayToPropogate()

const consent = []
await alix.preferences.streamConsent(async (entry: ConsentRecord) => {
consent.push(entry)
})

await delayToPropogate()

await alix2Group!.updateConsent('denied')
const dm = await alix2.conversations.newConversation(bo.address)
await dm!.updateConsent('denied')

await delayToPropogate(3000)
await alix.conversations.syncAllConversations()
await alix2.conversations.syncAllConversations()

assert(
consent.length === 4,
`Expected 4 consent records, got ${consent.length}`
)
const updatedConsentState = await alixGroup.consentState()
assert(
updatedConsentState === 'denied',
`Expected 'denied', got ${updatedConsentState}`
)

alix.preferences.cancelStreamConsent()

return true
})
10 changes: 5 additions & 5 deletions example/src/tests/groupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {
Group,
GroupUpdatedContent,
GroupUpdatedCodec,
ConsentListEntry,
DecodedMessage,
ConsentRecord,
} from '../../../src/index'

export const groupTests: Test[] = []
Expand Down Expand Up @@ -1185,7 +1185,7 @@ test('can group consent', async () => {
)

await bo.preferences.setConsentState(
new ConsentListEntry(group.id, 'conversation_id', 'denied')
new ConsentRecord(group.id, 'conversation_id', 'denied')
)
const isDenied = await bo.preferences.conversationConsentState(group.id)
assert(isDenied === 'denied', `bo group should be denied but was ${isDenied}`)
Expand Down Expand Up @@ -1219,7 +1219,7 @@ test('can allow and deny a inbox id', async () => {
)

await bo.preferences.setConsentState(
new ConsentListEntry(alix.inboxId, 'inbox_id', 'allowed')
new ConsentRecord(alix.inboxId, 'inbox_id', 'allowed')
)

let alixMember = (await boGroup.members()).find(
Expand All @@ -1243,7 +1243,7 @@ test('can allow and deny a inbox id', async () => {
)

await bo.preferences.setConsentState(
new ConsentListEntry(alix.inboxId, 'inbox_id', 'denied')
new ConsentRecord(alix.inboxId, 'inbox_id', 'denied')
)

alixMember = (await boGroup.members()).find(
Expand All @@ -1261,7 +1261,7 @@ test('can allow and deny a inbox id', async () => {
)

await bo.preferences.setConsentState(
new ConsentListEntry(alix.address, 'address', 'allowed')
new ConsentRecord(alix.address, 'address', 'allowed')
)

isAddressAllowed = await bo.preferences.addressConsentState(alix.address)
Expand Down
4 changes: 2 additions & 2 deletions ios/Wrappers/ConsentWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import Foundation
import XMTP

struct ConsentWrapper {
static func encodeToObj(_ entry: XMTP.ConsentListEntry) throws -> [String: Any] {
static func encodeToObj(_ entry: XMTP.ConsentRecord) throws -> [String: Any] {
return [
"type": entry.entryType.rawValue,
"value": entry.value,
"state": consentStateToString(state: entry.consentType),
]
}

static func encode(_ entry: XMTP.ConsentListEntry) throws -> String {
static func encode(_ entry: XMTP.ConsentRecord) throws -> String {
let obj = try encodeToObj(entry)
let data = try JSONSerialization.data(withJSONObject: obj)
guard let result = String(data: data, encoding: .utf8) else {
Expand Down
Loading
Loading