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

Browser SDK updates #760

Merged
merged 4 commits into from
Dec 19, 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
5 changes: 5 additions & 0 deletions .changeset/beige-bananas-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@xmtp/browser-sdk": patch
---

Browser SDK updates
32 changes: 24 additions & 8 deletions sdks/browser-sdk/src/Conversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export class Conversation {

#createdAtNs?: SafeConversation["createdAtNs"];

#admins: SafeConversation["admins"] = [];

#superAdmins: SafeConversation["superAdmins"] = [];

constructor(client: Client, id: string, data?: SafeConversation) {
this.#client = client;
this.#id = id;
Expand All @@ -48,6 +52,8 @@ export class Conversation {
this.#metadata = data?.metadata ?? undefined;
this.#permissions = data?.permissions ?? undefined;
this.#createdAtNs = data?.createdAtNs ?? undefined;
this.#admins = data?.admins ?? [];
this.#superAdmins = data?.superAdmins ?? [];
}

get id() {
Expand Down Expand Up @@ -128,30 +134,40 @@ export class Conversation {
});
}

async admins() {
return this.#client.sendMessage("getGroupAdmins", {
get admins() {
return this.#admins;
}

get superAdmins() {
return this.#superAdmins;
}

async syncAdmins() {
const admins = await this.#client.sendMessage("getGroupAdmins", {
id: this.#id,
});
this.#admins = admins;
}

async superAdmins() {
return this.#client.sendMessage("getGroupSuperAdmins", {
async syncSuperAdmins() {
const superAdmins = await this.#client.sendMessage("getGroupSuperAdmins", {
id: this.#id,
});
this.#superAdmins = superAdmins;
}

get permissions() {
return this.#permissions;
}

async isAdmin(inboxId: string) {
const admins = await this.admins();
return admins.includes(inboxId);
await this.syncAdmins();
return this.#admins.includes(inboxId);
}

async isSuperAdmin(inboxId: string) {
const superAdmins = await this.superAdmins();
return superAdmins.includes(inboxId);
await this.syncSuperAdmins();
return this.#superAdmins.includes(inboxId);
}

async sync() {
Expand Down
24 changes: 19 additions & 5 deletions sdks/browser-sdk/src/Conversations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Client } from "@/Client";
import { Conversation } from "@/Conversation";
import { DecodedMessage } from "@/DecodedMessage";
import type {
SafeCreateGroupOptions,
SafeListConversationsOptions,
Expand All @@ -21,21 +22,24 @@ export class Conversations {
}

async getConversationById(id: string) {
return this.#client.sendMessage("getConversationById", {
const data = await this.#client.sendMessage("getConversationById", {
id,
});
return data ? new Conversation(this.#client, id, data) : undefined;
}

async getMessageById(id: string) {
return this.#client.sendMessage("getMessageById", {
const data = await this.#client.sendMessage("getMessageById", {
id,
});
return data ? new DecodedMessage(this.#client, data) : undefined;
}

async getDmByInboxId(inboxId: string) {
return this.#client.sendMessage("getDmByInboxId", {
const data = await this.#client.sendMessage("getDmByInboxId", {
inboxId,
});
return data ? new Conversation(this.#client, data.id, data) : undefined;
}

async list(options?: SafeListConversationsOptions) {
Expand All @@ -52,17 +56,27 @@ export class Conversations {
async listGroups(
options?: Omit<SafeListConversationsOptions, "conversation_type">,
) {
return this.#client.sendMessage("getGroups", {
const conversations = await this.#client.sendMessage("getGroups", {
options,
});

return conversations.map(
(conversation) =>
new Conversation(this.#client, conversation.id, conversation),
);
}

async listDms(
options?: Omit<SafeListConversationsOptions, "conversation_type">,
) {
return this.#client.sendMessage("getDms", {
const conversations = await this.#client.sendMessage("getDms", {
options,
});

return conversations.map(
(conversation) =>
new Conversation(this.#client, conversation.id, conversation),
);
}

async newGroup(accountAddresses: string[], options?: SafeCreateGroupOptions) {
Expand Down
3 changes: 3 additions & 0 deletions sdks/browser-sdk/src/DecodedMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export class DecodedMessage {

parameters: Map<string, string>;

encodedContent: SafeMessage["content"];

senderInboxId: string;

sentAtNs: bigint;
Expand All @@ -37,6 +39,7 @@ export class DecodedMessage {
this.sentAtNs = message.sentAtNs;
this.conversationId = message.convoId;
this.senderInboxId = message.senderInboxId;
this.encodedContent = message.content;

switch (message.kind) {
case GroupMessageKind.Application:
Expand Down
4 changes: 1 addition & 3 deletions sdks/browser-sdk/src/WorkerConversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Conversation, Conversations } from "@xmtp/wasm-bindings";
import {
fromSafeCreateGroupOptions,
fromSafeListConversationsOptions,
toSafeMessage,
type SafeCreateGroupOptions,
type SafeListConversationsOptions,
} from "@/utils/conversions";
Expand Down Expand Up @@ -40,8 +39,7 @@ export class WorkerConversations {
getMessageById(id: string) {
try {
// findMessageById will throw if message is not found
const message = this.#conversations.findMessageById(id);
return toSafeMessage(message);
return this.#conversations.findMessageById(id);
} catch {
return undefined;
}
Expand Down
2 changes: 1 addition & 1 deletion sdks/browser-sdk/src/workers/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ self.onmessage = async (event: MessageEvent<ClientEventsClientMessageData>) => {
postMessage({
id,
action,
result: message,
result: message ? toSafeMessage(message) : undefined,
});
break;
}
Expand Down
7 changes: 2 additions & 5 deletions sdks/browser-sdk/test/Client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { ConsentEntityType, ConsentState } from "@xmtp/wasm-bindings";
import { v4 } from "uuid";
import { describe, expect, it } from "vitest";
import { Client } from "@/Client";
import { Conversation } from "@/Conversation";
import {
createClient,
createRegisteredClient,
Expand Down Expand Up @@ -163,11 +162,9 @@ describe.concurrent("Client", () => {
await client2.getConsentState(ConsentEntityType.GroupId, group2!.id),
).toBe(ConsentState.Allowed);

const convo = new Conversation(client2, group2!.id, group2);
expect(await group2!.consentState()).toBe(ConsentState.Allowed);

expect(await convo.consentState()).toBe(ConsentState.Allowed);

await convo.updateConsentState(ConsentState.Denied);
await group2!.updateConsentState(ConsentState.Denied);

expect(
await client2.getConsentState(ConsentEntityType.GroupId, group2!.id),
Expand Down
57 changes: 26 additions & 31 deletions sdks/browser-sdk/test/Conversation.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ConsentState } from "@xmtp/wasm-bindings";
import { describe, expect, it } from "vitest";
import { Conversation } from "@/Conversation";
import {
ContentTypeTest,
createRegisteredClient,
Expand Down Expand Up @@ -323,24 +322,24 @@ describe.concurrent("Conversation", () => {
]);

expect(await conversation.isSuperAdmin(client1.inboxId!)).toBe(true);
const superAdmins = await conversation.superAdmins();
expect(superAdmins.length).toBe(1);
expect(superAdmins).toContain(client1.inboxId);
await conversation.syncSuperAdmins();
expect(conversation.superAdmins.length).toBe(1);
expect(conversation.superAdmins).toContain(client1.inboxId);
expect(await conversation.isAdmin(client1.inboxId!)).toBe(false);
expect(await conversation.isAdmin(client2.inboxId!)).toBe(false);
const admins = await conversation.admins();
expect(admins.length).toBe(0);
await conversation.syncAdmins();
expect(conversation.admins.length).toBe(0);

await conversation.addAdmin(client2.inboxId!);
expect(await conversation.isAdmin(client2.inboxId!)).toBe(true);
const admins2 = await conversation.admins();
expect(admins2.length).toBe(1);
expect(admins2).toContain(client2.inboxId);
await conversation.syncAdmins();
expect(conversation.admins.length).toBe(1);
expect(conversation.admins).toContain(client2.inboxId);

await conversation.removeAdmin(client2.inboxId!);
expect(await conversation.isAdmin(client2.inboxId!)).toBe(false);
const admins3 = await conversation.admins();
expect(admins3.length).toBe(0);
await conversation.syncAdmins();
expect(conversation.admins.length).toBe(0);
});

it("should add and remove super admins", async () => {
Expand All @@ -355,24 +354,24 @@ describe.concurrent("Conversation", () => {
expect(await conversation.isSuperAdmin(client1.inboxId!)).toBe(true);
expect(await conversation.isSuperAdmin(client2.inboxId!)).toBe(false);

const superAdmins = await conversation.superAdmins();
expect(superAdmins.length).toBe(1);
expect(superAdmins).toContain(client1.inboxId);
await conversation.syncSuperAdmins();
expect(conversation.superAdmins.length).toBe(1);
expect(conversation.superAdmins).toContain(client1.inboxId);

await conversation.addSuperAdmin(client2.inboxId!);
expect(await conversation.isSuperAdmin(client2.inboxId!)).toBe(true);

const superAdmins2 = await conversation.superAdmins();
expect(superAdmins2.length).toBe(2);
expect(superAdmins2).toContain(client1.inboxId);
expect(superAdmins2).toContain(client2.inboxId);
await conversation.syncSuperAdmins();
expect(conversation.superAdmins.length).toBe(2);
expect(conversation.superAdmins).toContain(client1.inboxId);
expect(conversation.superAdmins).toContain(client2.inboxId);

await conversation.removeSuperAdmin(client2.inboxId!);
expect(await conversation.isSuperAdmin(client2.inboxId!)).toBe(false);

const superAdmins3 = await conversation.superAdmins();
expect(superAdmins3.length).toBe(1);
expect(superAdmins3).toContain(client1.inboxId);
await conversation.syncSuperAdmins();
expect(conversation.superAdmins.length).toBe(1);
expect(conversation.superAdmins).toContain(client1.inboxId);
});

it("should manage group consent state", async () => {
Expand All @@ -391,22 +390,18 @@ describe.concurrent("Conversation", () => {
const group2 = await client2.conversations.getConversationById(group.id);
expect(group2).toBeDefined();

const groupConvo = new Conversation(client2, group2!.id, group2);

expect(await groupConvo.consentState()).toBe(ConsentState.Unknown);
await groupConvo.send("gm!");
expect(await groupConvo.consentState()).toBe(ConsentState.Allowed);
expect(await group2!.consentState()).toBe(ConsentState.Unknown);
await group2!.send("gm!");
expect(await group2!.consentState()).toBe(ConsentState.Allowed);

await client3.conversations.sync();
const dmGroup2 = await client3.conversations.getConversationById(
dmGroup.id,
);
expect(dmGroup2).toBeDefined();

const dmConvo = new Conversation(client3, dmGroup2!.id, dmGroup2);

expect(await dmConvo.consentState()).toBe(ConsentState.Unknown);
await dmConvo.send("gm!");
expect(await dmConvo.consentState()).toBe(ConsentState.Allowed);
expect(await dmGroup2!.consentState()).toBe(ConsentState.Unknown);
await dmGroup2!.send("gm!");
expect(await dmGroup2!.consentState()).toBe(ConsentState.Allowed);
});
});
Loading