Skip to content

Commit

Permalink
Add support for HMAC keys (browser) (#778)
Browse files Browse the repository at this point in the history
* Add support for HMAC keys (browser)

* Create nine-toes-wink.md

* Fix changesets config

* Refactor dev tool

* Add more tests

* Auto-sync groups created without members
  • Loading branch information
rygine authored Jan 13, 2025
1 parent a538d11 commit c91612d
Show file tree
Hide file tree
Showing 14 changed files with 210 additions and 85 deletions.
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@xmtp/react-vite-browser-sdk-example"]
"ignore": ["@xmtp/xmtp.chat"]
}
5 changes: 5 additions & 0 deletions .changeset/nine-toes-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@xmtp/browser-sdk": patch
---

Add support for HMAC keys (browser)
125 changes: 52 additions & 73 deletions apps/xmtp.chat/src/components/ManageConversation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,30 @@ type ClassProperties<C> = {
};
type PolicySet = ClassProperties<PermissionPolicySet>;

const defaultPolicySet: PolicySet = {
addAdminPolicy: PermissionPolicy.SuperAdmin,
addMemberPolicy: PermissionPolicy.Allow,
removeAdminPolicy: PermissionPolicy.SuperAdmin,
removeMemberPolicy: PermissionPolicy.Admin,
updateGroupNamePolicy: PermissionPolicy.Allow,
updateGroupDescriptionPolicy: PermissionPolicy.Allow,
updateGroupImageUrlSquarePolicy: PermissionPolicy.Allow,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Allow,
updateMessageExpirationPolicy: PermissionPolicy.Admin,
};

const adminPolicySet: PolicySet = {
addAdminPolicy: PermissionPolicy.SuperAdmin,
addMemberPolicy: PermissionPolicy.Admin,
removeAdminPolicy: PermissionPolicy.SuperAdmin,
removeMemberPolicy: PermissionPolicy.Admin,
updateGroupNamePolicy: PermissionPolicy.Admin,
updateGroupDescriptionPolicy: PermissionPolicy.Admin,
updateGroupImageUrlSquarePolicy: PermissionPolicy.Admin,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Admin,
updateMessageExpirationPolicy: PermissionPolicy.Admin,
};

export const ManageConversation: React.FC = () => {
useBodyClass("main-flex-layout");
const { conversationId } = useParams();
Expand All @@ -53,17 +77,8 @@ export const ManageConversation: React.FC = () => {
const [addedMembers, setAddedMembers] = useState<string[]>([]);
const [removedMembers, setRemovedMembers] = useState<SafeGroupMember[]>([]);
const [permissionsPolicy, setPermissionsPolicy] =
useState<GroupPermissionsOptions>(GroupPermissionsOptions.AllMembers);
const [policySet, setPolicySet] = useState<PolicySet>({
addAdminPolicy: PermissionPolicy.Admin,
addMemberPolicy: PermissionPolicy.Admin,
removeAdminPolicy: PermissionPolicy.Admin,
removeMemberPolicy: PermissionPolicy.Admin,
updateGroupDescriptionPolicy: PermissionPolicy.Allow,
updateGroupImageUrlSquarePolicy: PermissionPolicy.Allow,
updateGroupNamePolicy: PermissionPolicy.Allow,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Allow,
});
useState<GroupPermissionsOptions>(GroupPermissionsOptions.Default);
const [policySet, setPolicySet] = useState<PolicySet>(defaultPolicySet);
const [updateConversationError, setUpdateConversationError] = useState<
string | null
>(null);
Expand All @@ -76,7 +91,7 @@ export const ManageConversation: React.FC = () => {
const [pinnedFrameUrl, setPinnedFrameUrl] = useState("");

const policyTooltip = useMemo(() => {
if (permissionsPolicy === GroupPermissionsOptions.AllMembers) {
if (permissionsPolicy === GroupPermissionsOptions.Default) {
return "All members of the group can perform group actions";
} else if (permissionsPolicy === GroupPermissionsOptions.AdminOnly) {
return "Only admins can perform group actions";
Expand All @@ -86,30 +101,12 @@ export const ManageConversation: React.FC = () => {

useEffect(() => {
if (
permissionsPolicy === GroupPermissionsOptions.AllMembers ||
permissionsPolicy === GroupPermissionsOptions.Default ||
permissionsPolicy === GroupPermissionsOptions.CustomPolicy
) {
setPolicySet({
addAdminPolicy: PermissionPolicy.Admin,
addMemberPolicy: PermissionPolicy.Admin,
removeAdminPolicy: PermissionPolicy.Admin,
removeMemberPolicy: PermissionPolicy.Admin,
updateGroupDescriptionPolicy: PermissionPolicy.Allow,
updateGroupImageUrlSquarePolicy: PermissionPolicy.Allow,
updateGroupNamePolicy: PermissionPolicy.Allow,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Allow,
});
setPolicySet(defaultPolicySet);
} else {
setPolicySet({
addAdminPolicy: PermissionPolicy.Admin,
addMemberPolicy: PermissionPolicy.Admin,
removeAdminPolicy: PermissionPolicy.Admin,
removeMemberPolicy: PermissionPolicy.Admin,
updateGroupDescriptionPolicy: PermissionPolicy.Admin,
updateGroupImageUrlSquarePolicy: PermissionPolicy.Admin,
updateGroupNamePolicy: PermissionPolicy.Admin,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Admin,
});
setPolicySet(adminPolicySet);
}
}, [permissionsPolicy]);

Expand Down Expand Up @@ -161,80 +158,80 @@ export const ManageConversation: React.FC = () => {
permissionsPolicy !== GroupPermissionsOptions.CustomPolicy
) {
switch (permissionsPolicy) {
case GroupPermissionsOptions.AllMembers: {
case GroupPermissionsOptions.Default: {
await conversation?.updatePermission(
PermissionUpdateType.AddMember,
PermissionPolicy.Deny,
defaultPolicySet.addMemberPolicy,
);
await conversation?.updatePermission(
PermissionUpdateType.RemoveMember,
PermissionPolicy.Admin,
defaultPolicySet.removeMemberPolicy,
);
await conversation?.updatePermission(
PermissionUpdateType.AddAdmin,
PermissionPolicy.SuperAdmin,
defaultPolicySet.addAdminPolicy,
);
await conversation?.updatePermission(
PermissionUpdateType.RemoveAdmin,
PermissionPolicy.SuperAdmin,
defaultPolicySet.removeAdminPolicy,
);
await conversation?.updatePermission(
PermissionUpdateType.UpdateMetadata,
PermissionPolicy.Allow,
defaultPolicySet.updateGroupNamePolicy,
MetadataField.GroupName,
);
await conversation?.updatePermission(
PermissionUpdateType.UpdateMetadata,
PermissionPolicy.Allow,
defaultPolicySet.updateGroupDescriptionPolicy,
MetadataField.Description,
);
await conversation?.updatePermission(
PermissionUpdateType.UpdateMetadata,
PermissionPolicy.Allow,
defaultPolicySet.updateGroupImageUrlSquarePolicy,
MetadataField.ImageUrlSquare,
);
await conversation?.updatePermission(
PermissionUpdateType.UpdateMetadata,
PermissionPolicy.Allow,
defaultPolicySet.updateGroupPinnedFrameUrlPolicy,
MetadataField.PinnedFrameUrl,
);
break;
}
case GroupPermissionsOptions.AdminOnly: {
await conversation?.updatePermission(
PermissionUpdateType.AddMember,
PermissionPolicy.Admin,
adminPolicySet.addMemberPolicy,
);
await conversation?.updatePermission(
PermissionUpdateType.RemoveMember,
PermissionPolicy.Admin,
adminPolicySet.removeMemberPolicy,
);
await conversation?.updatePermission(
PermissionUpdateType.AddAdmin,
PermissionPolicy.SuperAdmin,
adminPolicySet.addAdminPolicy,
);
await conversation?.updatePermission(
PermissionUpdateType.RemoveAdmin,
PermissionPolicy.SuperAdmin,
adminPolicySet.removeAdminPolicy,
);
await conversation?.updatePermission(
PermissionUpdateType.UpdateMetadata,
PermissionPolicy.Admin,
adminPolicySet.updateGroupNamePolicy,
MetadataField.GroupName,
);
await conversation?.updatePermission(
PermissionUpdateType.UpdateMetadata,
PermissionPolicy.Admin,
adminPolicySet.updateGroupDescriptionPolicy,
MetadataField.Description,
);
await conversation?.updatePermission(
PermissionUpdateType.UpdateMetadata,
PermissionPolicy.Admin,
adminPolicySet.updateGroupImageUrlSquarePolicy,
MetadataField.ImageUrlSquare,
);
await conversation?.updatePermission(
PermissionUpdateType.UpdateMetadata,
PermissionPolicy.Admin,
adminPolicySet.updateGroupPinnedFrameUrlPolicy,
MetadataField.PinnedFrameUrl,
);
}
Expand Down Expand Up @@ -312,31 +309,13 @@ export const ManageConversation: React.FC = () => {
const permissions = await conversation.permissions();
const policyType = permissions.policyType;
switch (policyType) {
case GroupPermissionsOptions.AllMembers:
setPermissionsPolicy(GroupPermissionsOptions.AllMembers);
setPolicySet({
addAdminPolicy: PermissionPolicy.Admin,
addMemberPolicy: PermissionPolicy.Admin,
removeAdminPolicy: PermissionPolicy.Admin,
removeMemberPolicy: PermissionPolicy.Admin,
updateGroupDescriptionPolicy: PermissionPolicy.Allow,
updateGroupImageUrlSquarePolicy: PermissionPolicy.Allow,
updateGroupNamePolicy: PermissionPolicy.Allow,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Allow,
});
case GroupPermissionsOptions.Default:
setPermissionsPolicy(GroupPermissionsOptions.Default);
setPolicySet(defaultPolicySet);
break;
case GroupPermissionsOptions.AdminOnly:
setPermissionsPolicy(GroupPermissionsOptions.AdminOnly);
setPolicySet({
addAdminPolicy: PermissionPolicy.Admin,
addMemberPolicy: PermissionPolicy.Admin,
removeAdminPolicy: PermissionPolicy.Admin,
removeMemberPolicy: PermissionPolicy.Admin,
updateGroupDescriptionPolicy: PermissionPolicy.Admin,
updateGroupImageUrlSquarePolicy: PermissionPolicy.Admin,
updateGroupNamePolicy: PermissionPolicy.Admin,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Admin,
});
setPolicySet(adminPolicySet);
break;
case GroupPermissionsOptions.CustomPolicy:
setPermissionsPolicy(GroupPermissionsOptions.CustomPolicy);
Expand Down Expand Up @@ -486,7 +465,7 @@ export const ManageConversation: React.FC = () => {
);
}}
data={[
{ value: "0", label: "All members" },
{ value: "0", label: "Default" },
{ value: "1", label: "Admins only" },
{ value: "2", label: "Custom policy" },
]}
Expand Down
15 changes: 11 additions & 4 deletions apps/xmtp.chat/src/components/NewConversation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const NewConversation: React.FC = () => {
const [members, setMembers] = useState<string[]>([]);
const [isDmGroup, setIsDmGroup] = useState(false);
const [permissionsPolicy, setPermissionsPolicy] =
useState<GroupPermissionsOptions>(GroupPermissionsOptions.AllMembers);
useState<GroupPermissionsOptions>(GroupPermissionsOptions.Default);
const [policySet, setPolicySet] = useState<PolicySet>({
addAdminPolicy: PermissionPolicy.Admin,
addMemberPolicy: PermissionPolicy.Admin,
Expand All @@ -53,6 +53,7 @@ export const NewConversation: React.FC = () => {
updateGroupImageUrlSquarePolicy: PermissionPolicy.Allow,
updateGroupNamePolicy: PermissionPolicy.Allow,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Allow,
updateMessageExpirationPolicy: PermissionPolicy.Allow,
});
const [createConversationError, setCreateConversationError] = useState<
string | null
Expand All @@ -64,7 +65,7 @@ export const NewConversation: React.FC = () => {
const [pinnedFrameUrl, setPinnedFrameUrl] = useState("");

const policyTooltip = useMemo(() => {
if (permissionsPolicy === GroupPermissionsOptions.AllMembers) {
if (permissionsPolicy === GroupPermissionsOptions.Default) {
return "All members of the group can perform group actions";
} else if (permissionsPolicy === GroupPermissionsOptions.AdminOnly) {
return "Only admins can perform group actions";
Expand All @@ -74,7 +75,7 @@ export const NewConversation: React.FC = () => {

useEffect(() => {
if (
permissionsPolicy === GroupPermissionsOptions.AllMembers ||
permissionsPolicy === GroupPermissionsOptions.Default ||
permissionsPolicy === GroupPermissionsOptions.CustomPolicy
) {
setPolicySet({
Expand All @@ -86,6 +87,7 @@ export const NewConversation: React.FC = () => {
updateGroupImageUrlSquarePolicy: PermissionPolicy.Allow,
updateGroupNamePolicy: PermissionPolicy.Allow,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Allow,
updateMessageExpirationPolicy: PermissionPolicy.Admin,
});
} else {
setPolicySet({
Expand All @@ -97,6 +99,7 @@ export const NewConversation: React.FC = () => {
updateGroupImageUrlSquarePolicy: PermissionPolicy.Admin,
updateGroupNamePolicy: PermissionPolicy.Admin,
updateGroupPinnedFrameUrlPolicy: PermissionPolicy.Admin,
updateMessageExpirationPolicy: PermissionPolicy.Admin,
});
}
}, [permissionsPolicy]);
Expand Down Expand Up @@ -131,6 +134,10 @@ export const NewConversation: React.FC = () => {
? policySet
: undefined,
});
// automatically sync when creating a group with no members
if (!isDmGroup && members.length === 0) {
await conversation.sync();
}
void navigate(`/conversations/${conversation.id}`);
};

Expand Down Expand Up @@ -258,7 +265,7 @@ export const NewConversation: React.FC = () => {
);
}}
data={[
{ value: "0", label: "All members" },
{ value: "0", label: "Default" },
{ value: "1", label: "Admins only" },
{ value: "2", label: "Custom policy" },
]}
Expand Down
2 changes: 1 addition & 1 deletion sdks/browser-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@xmtp/content-type-primitives": "^2.0.0",
"@xmtp/content-type-text": "^2.0.0",
"@xmtp/proto": "^3.72.3",
"@xmtp/wasm-bindings": "^0.0.11",
"@xmtp/wasm-bindings": "^0.0.12",
"uuid": "^11.0.3"
},
"devDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions sdks/browser-sdk/src/Conversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,8 @@ export class Conversations {

return new Conversation(this.#client, conversation.id, conversation);
}

async getHmacKeys() {
return this.#client.sendMessage("getHmacKeys", undefined);
}
}
5 changes: 5 additions & 0 deletions sdks/browser-sdk/src/WorkerConversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Conversation, Conversations } from "@xmtp/wasm-bindings";
import {
fromSafeCreateGroupOptions,
fromSafeListConversationsOptions,
type HmacKeys,
type SafeCreateGroupOptions,
type SafeListConversationsOptions,
} from "@/utils/conversions";
Expand Down Expand Up @@ -89,4 +90,8 @@ export class WorkerConversations {
const group = await this.#conversations.createDm(accountAddress);
return new WorkerConversation(this.#client, group);
}

getHmacKeys() {
return this.#conversations.getHmacKeys() as HmacKeys;
}
}
1 change: 1 addition & 0 deletions sdks/browser-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export {
SortDirection,
Consent,
ContentTypeId,
HmacKey,
} from "@xmtp/wasm-bindings";
export {
isSmartContractSigner,
Expand Down
7 changes: 7 additions & 0 deletions sdks/browser-sdk/src/types/clientEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type {
SafeCreateGroupOptions,
SafeEncodedContent,
SafeGroupMember,
SafeHmacKeys,
SafeInboxState,
SafeListConversationsOptions,
SafeListMessagesOptions,
Expand Down Expand Up @@ -276,6 +277,12 @@ export type ClientEvents =
result: undefined;
data: undefined;
}
| {
action: "getHmacKeys";
id: string;
result: SafeHmacKeys;
data: undefined;
}
/**
* Group actions
*/
Expand Down
Loading

0 comments on commit c91612d

Please sign in to comment.