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

feat: Groups Consent #311

Merged
merged 1 commit into from
Mar 13, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import java.util.UUID
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import com.facebook.common.util.Hex

class ReactNativeSigner(var module: XMTPModule, override var address: String) : SigningKey {
private val continuations: MutableMap<String, Continuation<Signature>> = mutableMapOf()
Expand Down Expand Up @@ -913,6 +914,32 @@ class XMTPModule : Module() {
logV("preEnableIdentityCallbackCompleted")
preEnableIdentityCallbackDeferred?.complete(Unit)
}

AsyncFunction("allowGroups") { clientAddress: String, groupIds: List<String> ->
logV("allowGroups")
val client = clients[clientAddress] ?: throw XMTPException("No client")
val groupDataIds = groupIds.mapNotNull { Hex.hexStringToByteArray(it) }
client.contacts.allowGroup(groupDataIds)
}

AsyncFunction("denyGroups") { clientAddress: String, groupIds: List<String> ->
logV("denyGroups")
val client = clients[clientAddress] ?: throw XMTPException("No client")
val groupDataIds = groupIds.mapNotNull { Hex.hexStringToByteArray(it) }
client.contacts.denyGroup(groupDataIds)
}

AsyncFunction("isGroupAllowed") { clientAddress: String, groupId: String ->
logV("isGroupAllowed")
val client = clients[clientAddress] ?: throw XMTPException("No client")
client.contacts.isGroupAllowed(Hex.hexStringToByteArray(groupId))
}

AsyncFunction("isGroupDenied") { clientAddress: String, groupId: String ->
logV("isGroupDenied")
val client = clients[clientAddress] ?: throw XMTPException("No client")
client.contacts.isGroupDenied(Hex.hexStringToByteArray(groupId))
}
}

//
Expand Down
81 changes: 81 additions & 0 deletions example/src/tests/groupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,87 @@ test('can stream all group Messages from multiple clients - swapped', async () =
return true
})

test('creating a group should allow group', async () => {
const [alix, bo] = await createClients(2)

const group = await alix.conversations.newGroup([bo.address])
const consent = await alix.contacts.isGroupAllowed(group.id)

if (!consent) {
throw Error('Group should be allowed')
}

return true
})

test('can allow a group', async () => {
const [alix, bo] = await createClients(2)
const alixGroup = await alix.conversations.newGroup([bo.address])
const startConsent = await bo.contacts.isGroupAllowed(alixGroup.id)
if (startConsent) {
throw Error('Group should not be allowed')
}
await bo.contacts.allowGroups([alixGroup.id])
const isAllowed = await bo.contacts.isGroupAllowed(alixGroup.id)
if (!isAllowed) {
throw Error('Group should be allowed')
}

return true
})

test('can deny a group', async () => {
const [alix, bo] = await createClients(2)
const alixGroup = await alix.conversations.newGroup([bo.address])
const startConsent = await bo.contacts.isGroupDenied(alixGroup.id)
if (startConsent) {
throw Error('Group should be unknown')
}
await bo.contacts.denyGroups([alixGroup.id])
const isDenied = await bo.contacts.isGroupDenied(alixGroup.id)
if (!isDenied) {
throw Error('Group should be denied')
}
await bo.contacts.allowGroups([alixGroup.id])
const isAllowed = await bo.contacts.isGroupAllowed(alixGroup.id)
if (!isAllowed) {
throw Error('Group should be allowed')
}

return true
})

test('can check if group is allowed', async () => {
const [alix, bo] = await createClients(2)
const alixGroup = await alix.conversations.newGroup([bo.address])
const startConsent = await bo.contacts.isGroupAllowed(alixGroup.id)
if (startConsent) {
throw Error('Group should not be allowed by default')
}
await bo.contacts.allowGroups([alixGroup.id])
const consent = await bo.contacts.isGroupAllowed(alixGroup.id)
if (!consent) {
throw Error('Group should be allowed')
}

return true
})

test('can check if group is denied', async () => {
const [alix, bo] = await createClients(2)
const alixGroup = await alix.conversations.newGroup([bo.address])
const startConsent = await bo.contacts.isGroupDenied(alixGroup.id)
if (startConsent) {
throw Error('Group should not be denied by default')
}
await bo.contacts.denyGroups([alixGroup.id])
const consent = await bo.contacts.isGroupDenied(alixGroup.id)
if (!consent) {
throw Error('Group should be denied')
}
return true
})

// Commenting this out so it doesn't block people, but nice to have?
// test('can stream messages for a long time', async () => {
// const bo = await Client.createRandom({ env: 'local', enableAlphaMls: true })
Expand Down
38 changes: 37 additions & 1 deletion ios/XMTPModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class XMTPModule: Module {
}

enum Error: Swift.Error {
case noClient, conversationNotFound(String), noMessage, invalidKeyBundle, invalidDigest, badPreparation(String), mlsNotEnabled(String)
case noClient, conversationNotFound(String), noMessage, invalidKeyBundle, invalidDigest, badPreparation(String), mlsNotEnabled(String), invalidString
}

public func definition() -> ModuleDefinition {
Expand Down Expand Up @@ -867,6 +867,42 @@ public class XMTPModule: Module {
self.preCreateIdentityCallbackDeferred = nil
}
}

AsyncFunction("allowGroups") { (clientAddress: String, groupIds: [String]) in
guard let client = await clientsManager.getClient(key: clientAddress) else {
throw Error.noClient
}
let groupDataIds = groupIds.compactMap { Data(hex: $0) }
try await client.contacts.allowGroup(groupIds: groupDataIds)
}

AsyncFunction("denyGroups") { (clientAddress: String, groupIds: [String]) in
guard let client = await clientsManager.getClient(key: clientAddress) else {
throw Error.noClient
}
let groupDataIds = groupIds.compactMap { Data(hex: $0) }
try await client.contacts.denyGroup(groupIds: groupDataIds)
}

AsyncFunction("isGroupAllowed") { (clientAddress: String, groupId: String) -> Bool in
guard let client = await clientsManager.getClient(key: clientAddress) else {
throw Error.noClient
}
guard let groupDataId = Data(hex: groupId) else {
throw Error.invalidString
}
return try await client.contacts.isGroupAllowed(groupId: groupDataId)
}

AsyncFunction("isGroupDenied") { (clientAddress: String, groupId: String) -> Bool in
guard let client = await clientsManager.getClient(key: clientAddress) else {
throw Error.invalidString
}
guard let groupDataId = Data(hex: groupId) else {
throw Error.invalidString
}
return try await client.contacts.isGroupDenied(groupId: groupDataId)
}
}

//
Expand Down
28 changes: 28 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,34 @@ export async function isGroupAdmin(
return XMTPModule.isGroupAdmin(clientAddress, id)
}

export async function allowGroups(
clientAddress: string,
groupIds: string[]
): Promise<void> {
return XMTPModule.allowGroups(clientAddress, groupIds)
}

export async function denyGroups(
clientAddress: string,
groupIds: string[]
): Promise<void> {
return XMTPModule.denyGroups(clientAddress, groupIds)
}

export async function isGroupAllowed(
clientAddress: string,
groupId: string
): Promise<boolean> {
return XMTPModule.isGroupAllowed(clientAddress, groupId)
}

export async function isGroupDenied(
clientAddress: string,
groupId: string
): Promise<boolean> {
return XMTPModule.isGroupDenied(clientAddress, groupId)
}

export const emitter = new EventEmitter(XMTPModule ?? NativeModulesProxy.XMTP)

export * from './XMTP.types'
Expand Down
18 changes: 17 additions & 1 deletion src/lib/Contacts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Client } from './Client'
import { ConsentListEntry } from './ConsentListEntry'
import { ConsentListEntry, ConsentState } from './ConsentListEntry'

Check warning on line 2 in src/lib/Contacts.ts

View workflow job for this annotation

GitHub Actions / lint

'ConsentState' is defined but never used
import * as XMTPModule from '../index'
import { getAddress } from '../utils/address'

Expand Down Expand Up @@ -41,4 +41,20 @@
async consentList(): Promise<ConsentListEntry[]> {
return await XMTPModule.consentList(this.client.address)
}

async allowGroups(groupIds: string[]): Promise<void> {
return await XMTPModule.allowGroups(this.client.address, groupIds)
}

async denyGroups(groupIds: string[]): Promise<void> {
return await XMTPModule.denyGroups(this.client.address, groupIds)
}

async isGroupAllowed(groupId: string): Promise<boolean> {
return await XMTPModule.isGroupAllowed(this.client.address, groupId)
}

async isGroupDenied(groupId: string): Promise<boolean> {
return await XMTPModule.isGroupDenied(this.client.address, groupId)
}
}
Loading