diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index bffa998a1..750b6c52c 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -18,11 +18,13 @@ import expo.modules.xmtpreactnativesdk.wrappers.ConsentWrapper import expo.modules.xmtpreactnativesdk.wrappers.ConsentWrapper.Companion.consentStateToString import expo.modules.xmtpreactnativesdk.wrappers.ContentJson import expo.modules.xmtpreactnativesdk.wrappers.ConversationContainerWrapper +import expo.modules.xmtpreactnativesdk.wrappers.ConversationOrder import expo.modules.xmtpreactnativesdk.wrappers.ConversationWrapper import expo.modules.xmtpreactnativesdk.wrappers.CreateGroupParamsWrapper import expo.modules.xmtpreactnativesdk.wrappers.DecodedMessageWrapper import expo.modules.xmtpreactnativesdk.wrappers.DecryptedLocalAttachment import expo.modules.xmtpreactnativesdk.wrappers.EncryptedLocalAttachment +import expo.modules.xmtpreactnativesdk.wrappers.GroupParamsWrapper import expo.modules.xmtpreactnativesdk.wrappers.GroupWrapper import expo.modules.xmtpreactnativesdk.wrappers.InboxStateWrapper import expo.modules.xmtpreactnativesdk.wrappers.MemberWrapper @@ -624,14 +626,26 @@ class XMTPModule : Module() { } } - AsyncFunction("listGroups") Coroutine { inboxId: String -> + AsyncFunction("listGroups") Coroutine { inboxId: String, groupParams: String?, sortOrder: String?, limit: Int? -> withContext(Dispatchers.IO) { logV("listGroups") val client = clients[inboxId] ?: throw XMTPException("No client") - val groupList = client.conversations.listGroups() - groupList.map { group -> + val params = GroupParamsWrapper.groupParamsFromJson(groupParams ?: "") + val order = getConversationSortOrder(sortOrder ?: "") + val sortedGroupList = if (order == ConversationOrder.LAST_MESSAGE) { + client.conversations.listGroups() + .sortedByDescending { group -> + group.decryptedMessages(limit = 1).firstOrNull()?.sentAt + } + .let { groups -> + if (limit != null && limit > 0) groups.take(limit) else groups + } + } else { + client.conversations.listGroups(limit = limit) + } + sortedGroupList.map { group -> groups[group.cacheKey(inboxId)] = group - GroupWrapper.encode(client, group) + GroupWrapper.encode(client, group, params) } } } @@ -1718,6 +1732,13 @@ class XMTPModule : Module() { } } + private fun getConversationSortOrder(order: String): ConversationOrder { + return when (order) { + "lastMessage" -> ConversationOrder.LAST_MESSAGE + else -> ConversationOrder.CREATED_AT + } + } + private suspend fun findConversation( inboxId: String, topic: String, diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt index 0336e5f70..b75c58c4f 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/GroupWrapper.kt @@ -1,36 +1,89 @@ package expo.modules.xmtpreactnativesdk.wrappers import com.google.gson.GsonBuilder +import com.google.gson.JsonParser import expo.modules.xmtpreactnativesdk.wrappers.ConsentWrapper.Companion.consentStateToString import org.xmtp.android.library.Client import org.xmtp.android.library.Group +enum class ConversationOrder { + LAST_MESSAGE, CREATED_AT +} + class GroupWrapper { companion object { - suspend fun encodeToObj(client: Client, group: Group): Map { - return mapOf( - "clientAddress" to client.address, - "id" to group.id, - "createdAt" to group.createdAt.time, - "members" to group.members().map { MemberWrapper.encode(it) }, - "version" to "GROUP", - "topic" to group.topic, - "creatorInboxId" to group.creatorInboxId(), - "isActive" to group.isActive(), - "addedByInboxId" to group.addedByInboxId(), - "name" to group.name, - "imageUrlSquare" to group.imageUrlSquare, - "description" to group.description, - "consentState" to consentStateToString(group.consentState()) - // "pinnedFrameUrl" to group.pinnedFrameUrl - ) + suspend fun encodeToObj( + client: Client, + group: Group, + groupParams: GroupParamsWrapper = GroupParamsWrapper(), + ): Map { + return buildMap { + put("clientAddress", client.address) + put("id", group.id) + put("createdAt", group.createdAt.time) + put("version", "GROUP") + put("topic", group.topic) + if (groupParams.members) { + put("members", group.members().map { MemberWrapper.encode(it) }) + } + if (groupParams.creatorInboxId) put("creatorInboxId", group.creatorInboxId()) + if (groupParams.isActive) put("isActive", group.isActive()) + if (groupParams.addedByInboxId) put("addedByInboxId", group.addedByInboxId()) + if (groupParams.name) put("name", group.name) + if (groupParams.imageUrlSquare) put("imageUrlSquare", group.imageUrlSquare) + if (groupParams.description) put("description", group.description) + if (groupParams.consentState) { + put("consentState", consentStateToString(group.consentState())) + } + if (groupParams.lastMessage) { + val lastMessage = group.decryptedMessages(limit = 1).firstOrNull() + if (lastMessage != null) { + put("lastMessage", DecodedMessageWrapper.encode(lastMessage)) + } + } + } } - suspend fun encode(client: Client, group: Group): String { + suspend fun encode( + client: Client, + group: Group, + groupParams: GroupParamsWrapper = GroupParamsWrapper(), + ): String { val gson = GsonBuilder().create() - val obj = encodeToObj(client, group) + val obj = encodeToObj(client, group, groupParams) return gson.toJson(obj) } } } + +class GroupParamsWrapper( + val members: Boolean = true, + val creatorInboxId: Boolean = true, + val isActive: Boolean = true, + val addedByInboxId: Boolean = true, + val name: Boolean = true, + val imageUrlSquare: Boolean = true, + val description: Boolean = true, + val consentState: Boolean = true, + val lastMessage: Boolean = false, +) { + companion object { + fun groupParamsFromJson(groupParams: String): GroupParamsWrapper { + if (groupParams.isEmpty()) return GroupParamsWrapper() + val jsonOptions = JsonParser.parseString(groupParams).asJsonObject + return GroupParamsWrapper( + if (jsonOptions.has("members")) jsonOptions.get("members").asBoolean else true, + if (jsonOptions.has("creatorInboxId")) jsonOptions.get("creatorInboxId").asBoolean else true, + if (jsonOptions.has("isActive")) jsonOptions.get("isActive").asBoolean else true, + if (jsonOptions.has("addedByInboxId")) jsonOptions.get("addedByInboxId").asBoolean else true, + if (jsonOptions.has("name")) jsonOptions.get("name").asBoolean else true, + if (jsonOptions.has("imageUrlSquare")) jsonOptions.get("imageUrlSquare").asBoolean else true, + if (jsonOptions.has("description")) jsonOptions.get("description").asBoolean else true, + if (jsonOptions.has("consentState")) jsonOptions.get("consentState").asBoolean else true, + if (jsonOptions.has("lastMessage")) jsonOptions.get("lastMessage").asBoolean else false, + ) + } + } +} + diff --git a/example/src/tests/groupPerformanceTests.ts b/example/src/tests/groupPerformanceTests.ts index 7ee2ac8d4..144e71038 100644 --- a/example/src/tests/groupPerformanceTests.ts +++ b/example/src/tests/groupPerformanceTests.ts @@ -57,6 +57,83 @@ async function beforeAll( ) } +test('testing large group listings with ordering', async () => { + await beforeAll(1000, 10, 10) + + let start = Date.now() + let groups = await alixClient.conversations.listGroups() + let end = Date.now() + console.log(`Alix loaded ${groups.length} groups in ${end - start}ms`) + + await groups[5].send({ text: `Alix message` }) + await groups[50].send({ text: `Alix message` }) + await groups[150].send({ text: `Alix message` }) + await groups[500].send({ text: `Alix message` }) + await groups[700].send({ text: `Alix message` }) + await groups[900].send({ text: `Alix message` }) + + let start2 = Date.now() + let groups2 = await alixClient.conversations.listGroups( + { + members: false, + consentState: false, + description: false, + creatorInboxId: false, + addedByInboxId: false, + isActive: false, + lastMessage: true, + }, + 'lastMessage' + ) + let end2 = Date.now() + console.log(`Alix loaded ${groups2.length} groups in ${end2 - start2}ms`) + assert( + end2 - start2 < end - start, + 'listing 1000 groups without certain fields should take less time' + ) + + start = Date.now() + await alixClient.conversations.syncGroups() + end = Date.now() + console.log(`Alix synced ${groups.length} groups in ${end - start}ms`) + assert( + end - start < 100, + 'syncing 1000 cached groups should take less than a .1 second' + ) + + start = Date.now() + await boClient.conversations.syncGroups() + end = Date.now() + console.log(`Bo synced ${groups.length} groups in ${end - start}ms`) + + start = Date.now() + groups = await boClient.conversations.listGroups() + end = Date.now() + console.log(`Bo loaded ${groups.length} groups in ${end - start}ms`) + + start2 = Date.now() + groups2 = await boClient.conversations.listGroups( + { + members: false, + consentState: false, + description: false, + creatorInboxId: false, + addedByInboxId: false, + isActive: false, + lastMessage: true, + }, + 'lastMessage' + ) + end2 = Date.now() + console.log(`Bo loaded ${groups2.length} groups in ${end2 - start2}ms`) + assert( + end2 - start2 < end - start, + 'listing 1000 groups without certain fields should take less time' + ) + + return true +}) + test('testing large group listings', async () => { await beforeAll(1000) diff --git a/example/src/tests/groupTests.ts b/example/src/tests/groupTests.ts index be6550d6a..a34186170 100644 --- a/example/src/tests/groupTests.ts +++ b/example/src/tests/groupTests.ts @@ -1062,6 +1062,57 @@ test('can stream groups', async () => { return true }) +test('can list groups with params', async () => { + const [alixClient, boClient] = await createClients(2) + + const boGroup1 = await boClient.conversations.newGroup([alixClient.address]) + const boGroup2 = await boClient.conversations.newGroup([alixClient.address]) + + await boGroup1.send({ text: `first message` }) + await boGroup1.send({ text: `second message` }) + await boGroup1.send({ text: `third message` }) + await boGroup2.send({ text: `first message` }) + + const boGroupsOrderCreated = await boClient.conversations.listGroups() + const boGroupsOrderLastMessage = await boClient.conversations.listGroups( + { lastMessage: true }, + 'lastMessage' + ) + const boGroupsLimit = await boClient.conversations.listGroups( + {}, + undefined, + 1 + ) + + assert( + boGroupsOrderCreated.map((group: any) => group.id).toString() === + [boGroup1.id, boGroup2.id].toString(), + `Group order should be group1 then group2 but was ${boGroupsOrderCreated.map((group: any) => group.id).toString()}` + ) + + assert( + boGroupsOrderLastMessage.map((group: any) => group.id).toString() === + [boGroup2.id, boGroup1.id].toString(), + `Group order should be group2 then group1 but was ${boGroupsOrderLastMessage.map((group: any) => group.id).toString()}` + ) + + const messages = await boGroupsOrderLastMessage[0].messages() + assert( + messages[0].content() === 'first message', + `last message should be first message ${messages[0].content()}` + ) + assert( + boGroupsLimit.length === 1, + `List length should be 1 but was ${boGroupsLimit.length}` + ) + assert( + boGroupsLimit[0].id === boGroup1.id, + `Group should be ${boGroup1.id} but was ${boGroupsLimit[0].id}` + ) + + return true +}) + test('can list groups', async () => { const [alixClient, boClient] = await createClients(2) @@ -1844,34 +1895,25 @@ test('can group consent', async () => { ) isAllowed = await bo.contacts.isGroupAllowed(group.id) + assert(isAllowed === true, `bo group should be allowed but was ${isAllowed}`) assert( - isAllowed === true, - `bo group should be allowed but was ${isAllowed}` - ) - assert( - await group.state === 'allowed', + (await group.state) === 'allowed', `the group should have a consent state of allowed but was ${await group.state}` ) - + await bo.contacts.denyGroups([group.id]) - let isDenied = await bo.contacts.isGroupDenied(group.id) + const isDenied = await bo.contacts.isGroupDenied(group.id) + assert(isDenied === true, `bo group should be denied but was ${isDenied}`) assert( - isDenied === true, - `bo group should be denied but was ${isDenied}` - ) - assert( - await group.consentState() === 'denied', + (await group.consentState()) === 'denied', `the group should have a consent state of denied but was ${await group.consentState()}` ) await group.updateConsent('allowed') isAllowed = await bo.contacts.isGroupAllowed(group.id) + assert(isAllowed === true, `bo group should be allowed2 but was ${isAllowed}`) assert( - isAllowed === true, - `bo group should be allowed2 but was ${isAllowed}` - ) - assert( - await group.consentState() === 'allowed', + (await group.consentState()) === 'allowed', `the group should have a consent state2 of allowed but was ${await group.consentState()}` ) diff --git a/example/src/tests/v3OnlyTests.ts b/example/src/tests/v3OnlyTests.ts index 9108f6ddf..f673dfbea 100644 --- a/example/src/tests/v3OnlyTests.ts +++ b/example/src/tests/v3OnlyTests.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-extra-non-null-assertion */ import { Client } from 'xmtp-react-native-sdk' diff --git a/ios/Wrappers/GroupWrapper.swift b/ios/Wrappers/GroupWrapper.swift index 3d80b8a70..928fdd6d5 100644 --- a/ios/Wrappers/GroupWrapper.swift +++ b/ios/Wrappers/GroupWrapper.swift @@ -8,29 +8,56 @@ import Foundation import XMTP +enum ConversationOrder { + case lastMessage, createdAt +} + // Wrapper around XMTP.Group to allow passing these objects back into react native. struct GroupWrapper { - static func encodeToObj(_ group: XMTP.Group, client: XMTP.Client) async throws -> [String: Any] { - return [ + static func encodeToObj(_ group: XMTP.Group, client: XMTP.Client, groupParams: GroupParamsWrapper = GroupParamsWrapper()) async throws -> [String: Any] { + var result: [String: Any] = [ "clientAddress": client.address, "id": group.id, "createdAt": UInt64(group.createdAt.timeIntervalSince1970 * 1000), - "members": try await group.members.compactMap { member in return try MemberWrapper.encode(member) }, "version": "GROUP", - "topic": group.topic, - "creatorInboxId": try group.creatorInboxId(), - "isActive": try group.isActive(), - "addedByInboxId": try group.addedByInboxId(), - "name": try group.groupName(), - "imageUrlSquare": try group.groupImageUrlSquare(), - "description": try group.groupDescription(), - "consentState": ConsentWrapper.consentStateToString(state: try group.consentState()) - // "pinnedFrameUrl": try group.groupPinnedFrameUrl() + "topic": group.topic ] + + if groupParams.members { + result["members"] = try await group.members.compactMap { member in return try MemberWrapper.encode(member) } + } + if groupParams.creatorInboxId { + result["creatorInboxId"] = try group.creatorInboxId() + } + if groupParams.isActive { + result["isActive"] = try group.isActive() + } + if groupParams.addedByInboxId { + result["addedByInboxId"] = try group.addedByInboxId() + } + if groupParams.name { + result["name"] = try group.groupName() + } + if groupParams.imageUrlSquare { + result["imageUrlSquare"] = try group.groupImageUrlSquare() + } + if groupParams.description { + result["description"] = try group.groupDescription() + } + if groupParams.consentState { + result["consentState"] = ConsentWrapper.consentStateToString(state: try group.consentState()) + } + if groupParams.lastMessage { + if let lastMessage = try await group.decryptedMessages(limit: 1).first { + result["lastMessage"] = try DecodedMessageWrapper.encode(lastMessage, client: client) + } + } + + return result } - static func encode(_ group: XMTP.Group, client: XMTP.Client) async throws -> String { - let obj = try await encodeToObj(group, client: client) + static func encode(_ group: XMTP.Group, client: XMTP.Client, groupParams: GroupParamsWrapper = GroupParamsWrapper()) async throws -> String { + let obj = try await encodeToObj(group, client: client, groupParams: groupParams) let data = try JSONSerialization.data(withJSONObject: obj) guard let result = String(data: data, encoding: .utf8) else { throw WrapperError.encodeError("could not encode group") @@ -38,3 +65,58 @@ struct GroupWrapper { return result } } + +struct GroupParamsWrapper { + let members: Bool + let creatorInboxId: Bool + let isActive: Bool + let addedByInboxId: Bool + let name: Bool + let imageUrlSquare: Bool + let description: Bool + let consentState: Bool + let lastMessage: Bool + + init( + members: Bool = true, + creatorInboxId: Bool = true, + isActive: Bool = true, + addedByInboxId: Bool = true, + name: Bool = true, + imageUrlSquare: Bool = true, + description: Bool = true, + consentState: Bool = true, + lastMessage: Bool = false + ) { + self.members = members + self.creatorInboxId = creatorInboxId + self.isActive = isActive + self.addedByInboxId = addedByInboxId + self.name = name + self.imageUrlSquare = imageUrlSquare + self.description = description + self.consentState = consentState + self.lastMessage = lastMessage + } + + static func groupParamsFromJson(_ groupParams: String) -> GroupParamsWrapper { + guard let jsonData = groupParams.data(using: .utf8), + let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: []), + let jsonDict = jsonObject as? [String: Any] else { + return GroupParamsWrapper() + } + + return GroupParamsWrapper( + members: jsonDict["members"] as? Bool ?? true, + creatorInboxId: jsonDict["creatorInboxId"] as? Bool ?? true, + isActive: jsonDict["isActive"] as? Bool ?? true, + addedByInboxId: jsonDict["addedByInboxId"] as? Bool ?? true, + name: jsonDict["name"] as? Bool ?? true, + imageUrlSquare: jsonDict["imageUrlSquare"] as? Bool ?? true, + description: jsonDict["description"] as? Bool ?? true, + consentState: jsonDict["consentState"] as? Bool ?? true, + lastMessage: jsonDict["lastMessage"] as? Bool ?? false + ) + } +} + diff --git a/ios/XMTPModule.swift b/ios/XMTPModule.swift index 08edd2df5..ecaeb5d8d 100644 --- a/ios/XMTPModule.swift +++ b/ios/XMTPModule.swift @@ -532,19 +532,45 @@ public class XMTPModule: Module { return results } - AsyncFunction("listGroups") { (inboxId: String) -> [String] in + AsyncFunction("listGroups") { (inboxId: String, groupParams: String?, sortOrder: String?, limit: Int?) -> [String] in guard let client = await clientsManager.getClient(key: inboxId) else { throw Error.noClient } - let groupList = try await client.conversations.groups() - + + let params = GroupParamsWrapper.groupParamsFromJson(groupParams ?? "") + let order = getConversationSortOrder(order: sortOrder ?? "") + + var groupList: [Group] = [] + + if order == .lastMessage { + let groups = try await client.conversations.groups() + var groupsWithMessages: [(Group, Date)] = [] + for group in groups { + do { + let firstMessage = try await group.decryptedMessages(limit: 1).first + let sentAt = firstMessage?.sentAt ?? Date.distantPast + groupsWithMessages.append((group, sentAt)) + } catch { + print("Failed to fetch messages for group: \(group.id)") + } + } + let sortedGroups = groupsWithMessages.sorted { $0.1 > $1.1 }.map { $0.0 } + + if let limit = limit, limit > 0 { + groupList = Array(sortedGroups.prefix(limit)) + } else { + groupList = sortedGroups + } + } else { + groupList = try await client.conversations.groups(limit: limit) + } + var results: [String] = [] for group in groupList { await self.groupsManager.set(group.cacheKey(inboxId), group) - let encodedGroup = try await GroupWrapper.encode(group, client: client) + let encodedGroup = try await GroupWrapper.encode(group, client: client, groupParams: params) results.append(encodedGroup) } - return results } @@ -1663,6 +1689,15 @@ public class XMTPModule: Module { return .unknown } } + + private func getConversationSortOrder(order: String) -> ConversationOrder { + switch order { + case "lastMessage": + return .lastMessage + default: + return .createdAt + } + } func createClientConfig(env: String, appVersion: String?, preEnableIdentityCallback: PreEventCallback? = nil, preCreateIdentityCallback: PreEventCallback? = nil, preAuthenticateToInboxCallback: PreEventCallback? = nil, enableV3: Bool = false, dbEncryptionKey: Data? = nil, dbDirectory: String? = nil, historySyncUrl: String? = nil) -> XMTP.ClientOptions { // Ensure that all codecs have been registered. diff --git a/src/index.ts b/src/index.ts index b1cbfc675..4e1652525 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,7 @@ import { Member } from './lib/Member' import type { Query } from './lib/Query' import { ConversationSendPayload } from './lib/types' import { DefaultContentTypes } from './lib/types/DefaultContentType' +import { ConversationOrder, GroupOptions } from './lib/types/GroupOptions' import { PermissionPolicySet } from './lib/types/PermissionPolicySet' import { getAddress } from './utils/address' @@ -272,7 +273,7 @@ export async function createGroup< ) ) - const members = group['members'].map((mem: string) => { + const members = group['members']?.map((mem: string) => { return Member.from(mem) }) return new Group(client, group, members) @@ -303,7 +304,7 @@ export async function createGroupCustomPermissions< JSON.stringify(options) ) ) - const members = group['members'].map((mem: string) => { + const members = group['members']?.map((mem: string) => { return Member.from(mem) }) return new Group(client, group, members) @@ -311,10 +312,22 @@ export async function createGroupCustomPermissions< export async function listGroups< ContentTypes extends DefaultContentTypes = DefaultContentTypes, ->(client: Client): Promise[]> { - return (await XMTPModule.listGroups(client.inboxId)).map((json: string) => { +>( + client: Client, + opts?: GroupOptions | undefined, + order?: ConversationOrder | undefined, + limit?: number | undefined +): Promise[]> { + return ( + await XMTPModule.listGroups( + client.inboxId, + JSON.stringify(opts), + order, + limit + ) + ).map((json: string) => { const group = JSON.parse(json) - const members = group['members'].map((mem: string) => { + const members = group['members']?.map((mem: string) => { return Member.from(mem) }) return new Group(client, group, members) @@ -399,7 +412,7 @@ export async function findGroup< ): Promise | undefined> { const json = await XMTPModule.findGroup(client.inboxId, groupId) const group = JSON.parse(json) - const members = group['members'].map((mem: string) => { + const members = group['members']?.map((mem: string) => { return Member.from(mem) }) return new Group(client, group, members) @@ -1254,7 +1267,7 @@ export async function processWelcomeMessage< encryptedMessage ) const group = JSON.parse(json) - const members = group['members'].map((mem: string) => { + const members = group['members']?.map((mem: string) => { return Member.from(mem) }) return new Group(client, group, members) @@ -1295,3 +1308,4 @@ export { ConsentListEntry, DecodedMessage, MessageDeliveryStatus } export { Group } from './lib/Group' export { Member } from './lib/Member' export { InboxId } from './lib/Client' +export { GroupOptions, ConversationOrder } from './lib/types/GroupOptions' diff --git a/src/lib/Conversations.ts b/src/lib/Conversations.ts index fc8b8a656..5facaa2d3 100644 --- a/src/lib/Conversations.ts +++ b/src/lib/Conversations.ts @@ -11,6 +11,7 @@ import { Group, GroupParams } from './Group' import { Member } from './Member' import { CreateGroupOptions } from './types/CreateGroupOptions' import { EventTypes } from './types/EventTypes' +import { ConversationOrder, GroupOptions } from './types/GroupOptions' import { PermissionPolicySet } from './types/PermissionPolicySet' import { ConversationContext } from '../XMTP.types' import * as XMTPModule from '../index' @@ -83,11 +84,18 @@ export default class Conversations< /** * This method returns a list of all groups that the client is a member of. * To get the latest list of groups from the network, call syncGroups() first. + * @param {GroupOptions} opts - The options to specify what fields you want returned for the groups in the list. + * @param {ConversationOrder} order - The order to specify if you want groups listed by last message or by created at. + * @param {number} limit - Limit the number of groups returned in the list. * * @returns {Promise} A Promise that resolves to an array of Group objects. */ - async listGroups(): Promise[]> { - const result = await XMTPModule.listGroups(this.client) + async listGroups( + opts?: GroupOptions | undefined, + order?: ConversationOrder | undefined, + limit?: number | undefined + ): Promise[]> { + const result = await XMTPModule.listGroups(this.client, opts, order, limit) for (const group of result) { this.known[group.id] = true diff --git a/src/lib/types/GroupOptions.ts b/src/lib/types/GroupOptions.ts new file mode 100644 index 000000000..9dad2ec2d --- /dev/null +++ b/src/lib/types/GroupOptions.ts @@ -0,0 +1,15 @@ +export type GroupOptions = { + members?: boolean + creatorInboxId?: boolean + isActive?: boolean + addedByInboxId?: boolean + name?: boolean + imageUrlSquare?: boolean + description?: boolean + consentState?: boolean + lastMessage?: boolean +} + +export type ConversationOrder = + | 'lastMessage' // Ordered by the last message that was sent + | 'createdAt' // DEFAULT: Ordered by the date the conversation was created