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

xmtp.chat updates #871

Merged
merged 9 commits into from
Mar 13, 2025
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
2 changes: 1 addition & 1 deletion apps/xmtp.chat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@mantine/modals": "^7.16.0",
"@mantine/notifications": "^7.16.0",
"@tanstack/react-query": "^5.61.5",
"@xmtp/browser-sdk": "0.0.23",
"@xmtp/browser-sdk": "1.0.0-rc1",
"@xmtp/content-type-group-updated": "^2.0.0",
"@xmtp/content-type-primitives": "^2.0.0",
"@xmtp/content-type-reaction": "^2.0.0",
Expand Down
46 changes: 36 additions & 10 deletions apps/xmtp.chat/src/components/App/AppHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Burger, Button, Flex } from "@mantine/core";
import { Burger, Button, Flex, Skeleton } from "@mantine/core";
import { useEffect, useState } from "react";
import { useNavigate } from "react-router";
import { shortAddress } from "@/helpers/address";
import { useClient } from "@/hooks/useClient";
Expand All @@ -19,6 +20,28 @@ export const AppHeader: React.FC<AppHeaderProps> = ({
}) => {
const { client } = useClient();
const navigate = useNavigate();
const [accountIdentifier, setAccountIdentifier] = useState<string | null>(
null,
);
const [isLoadingIdentifier, setIsLoadingIdentifier] = useState(false);

useEffect(() => {
const fetchAccountIdentifier = async () => {
if (!client) return;

setIsLoadingIdentifier(true);
try {
const identifier = await client.accountIdentifier();
setAccountIdentifier(identifier.identifier.toLowerCase());
} catch (error) {
console.error("Failed to fetch account identifier:", error);
} finally {
setIsLoadingIdentifier(false);
}
};

void fetchAccountIdentifier();
}, [client]);

const handleClick = () => {
void navigate("/identity");
Expand All @@ -31,15 +54,18 @@ export const AppHeader: React.FC<AppHeaderProps> = ({
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
)}
<Flex align="center" flex={1}>
{client && (
<Button
variant="default"
aria-label={client.accountAddress}
className={classes.button}
onClick={handleClick}>
{shortAddress(client.accountAddress)}
</Button>
)}
{client &&
(isLoadingIdentifier ? (
<Skeleton height={36} width={120} radius="sm" />
) : (
<Button
variant="default"
aria-label={accountIdentifier || ""}
className={classes.button}
onClick={handleClick}>
{accountIdentifier ? shortAddress(accountIdentifier) : "..."}
</Button>
))}
</Flex>
</Flex>
<Flex align="center" justify="space-between" gap="xs" p="md" flex={1}>
Expand Down
10 changes: 8 additions & 2 deletions apps/xmtp.chat/src/components/Conversation/LoadDM.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ export const LoadDM: React.FC = () => {

try {
setMessage("Verifying address...");
const inboxId = await client.findInboxIdByAddress(address);
const inboxId = await client.findInboxIdByIdentifier({
identifier: address.toLowerCase(),
identifierKind: "Ethereum",
});
// no inbox ID, redirect to root

if (!inboxId) {
Expand All @@ -65,7 +68,10 @@ export const LoadDM: React.FC = () => {
if (dmId === undefined) {
// no DM group, create it
setMessage("Creating new DM...");
const dmGroup = await client.conversations.newDm(address);
const dmGroup = await client.conversations.newDmWithIdentifier({
identifier: address.toLowerCase(),
identifierKind: "Ethereum",
});
dmId = dmGroup.id;
// go to new DM group
}
Expand Down
24 changes: 19 additions & 5 deletions apps/xmtp.chat/src/components/Conversation/ManageGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,14 @@ export const ManageGroup: React.FC<ManageGroupProps> = ({ group }) => {
}, [permissionsPolicy]);

useEffect(() => {
if (members.some((member) => member.accountAddresses.includes(address))) {
if (
members.some((member) =>
member.accountIdentifiers.some(
(identifier) =>
identifier.identifier.toLowerCase() === address.toLowerCase(),
),
)
) {
setAddressError("Duplicate address");
} else if (address && !isValidLongWalletAddress(address)) {
setAddressError("Invalid address");
Expand All @@ -133,10 +140,15 @@ export const ManageGroup: React.FC<ManageGroupProps> = ({ group }) => {
await group.updateImageUrl(imageUrl);
}
if (addedMembers.length > 0) {
await group.addMembers(addedMembers);
await group.addMembersByIdentifiers(
addedMembers.map((member) => ({
identifier: member.toLowerCase(),
identifierKind: "Ethereum",
})),
);
}
if (removedMembers.length > 0) {
await group.removeMembersByInboxId(
await group.removeMembers(
removedMembers.map((member) => member.inboxId),
);
}
Expand Down Expand Up @@ -264,7 +276,7 @@ export const ManageGroup: React.FC<ManageGroupProps> = ({ group }) => {
};

const handleAddMember = () => {
setAddedMembers([...addedMembers, address]);
setAddedMembers([...addedMembers, address.toLowerCase()]);
setAddress("");
setAddressError(null);
};
Expand Down Expand Up @@ -684,7 +696,9 @@ export const ManageGroup: React.FC<ManageGroupProps> = ({ group }) => {
justify="space-between"
align="center">
<Text truncate="end" flex={1}>
{member.accountAddresses.join(", ")}
{member.accountIdentifiers
.map((identifier) => identifier.identifier)
.join(", ")}
</Text>
<Button
onClick={() => {
Expand Down
43 changes: 37 additions & 6 deletions apps/xmtp.chat/src/components/Identity/Identity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ import { InstallationTable } from "./InstallationTable";
export const Identity: React.FC = () => {
useBodyClass("main-flex-layout");
const { setNavbar } = useAppState();

useEffect(() => {
setNavbar(true);
}, []);

const { client } = useClient();
const {
installations,
Expand All @@ -34,10 +29,40 @@ export const Identity: React.FC = () => {
sync,
syncing,
} = useIdentity(true);

const [revokeInstallationError, setRevokeInstallationError] = useState<
string | null
>(null);

// Add state for account identifier
const [accountIdentifier, setAccountIdentifier] = useState<string | null>(
null,
);
const [isLoadingIdentifier, setIsLoadingIdentifier] = useState(false);

useEffect(() => {
setNavbar(true);
}, []);

// Fetch account identifier when client is available
useEffect(() => {
const fetchAccountIdentifier = async () => {
if (!client) return;

setIsLoadingIdentifier(true);
try {
const identifier = await client.accountIdentifier();
setAccountIdentifier(identifier.identifier.toLowerCase());
} catch (error) {
console.error("Failed to fetch account identifier:", error);
} finally {
setIsLoadingIdentifier(false);
}
};

void fetchAccountIdentifier();
}, [client]);

const handleRevokeAllOtherInstallations = async () => {
try {
await revokeAllOtherInstallations();
Expand Down Expand Up @@ -92,7 +117,13 @@ export const Identity: React.FC = () => {
<Text flex="0 0 25%" style={{ whiteSpace: "nowrap" }}>
Address
</Text>
<BadgeWithCopy value={client.accountAddress} />
{isLoadingIdentifier ? (
<Text size="sm" c="dimmed">
Loading...
</Text>
) : (
<BadgeWithCopy value={accountIdentifier || ""} />
)}
</Group>
<Group gap="md" wrap="nowrap">
<Text flex="0 0 25%" style={{ whiteSpace: "nowrap" }}>
Expand Down
12 changes: 10 additions & 2 deletions apps/xmtp.chat/src/helpers/createSigner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { privateKeyToAccount } from "viem/accounts";
export const createEphemeralSigner = (privateKey: Hex): Signer => {
const account = privateKeyToAccount(privateKey);
return {
getAddress: () => account.address,
type: "EOA",
getIdentifier: () => ({
identifier: account.address.toLowerCase(),
identifierKind: "Ethereum",
}),
signMessage: async (message: string) => {
const signature = await account.signMessage({
message,
Expand All @@ -20,7 +24,11 @@ export const createSigner = (
walletClient: WalletClient,
): Signer => {
return {
getAddress: () => address,
type: "EOA",
getIdentifier: () => ({
identifier: address.toLowerCase(),
identifierKind: "Ethereum",
}),
signMessage: async (message: string) => {
const signature = await walletClient.signMessage({
account: address,
Expand Down
5 changes: 4 additions & 1 deletion apps/xmtp.chat/src/hooks/useClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ export const useClient = (onError?: (error: Error) => void) => {
);

const disconnect = useCallback(() => {
setClient(undefined);
if (client) {
client.close();
setClient(undefined);
}
}, [client, setClient]);

return {
Expand Down
12 changes: 9 additions & 3 deletions apps/xmtp.chat/src/hooks/useConversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@ export const useConversations = () => {
setLoading(true);

try {
const conversation = await client.conversations.newGroup(
members,
const conversation = await client.conversations.newGroupWithIdentifiers(
members.map((member) => ({
identifier: member.toLowerCase(),
identifierKind: "Ethereum",
})),
options,
);
return conversation;
Expand All @@ -106,7 +109,10 @@ export const useConversations = () => {
setLoading(true);

try {
const conversation = await client.conversations.newDm(member);
const conversation = await client.conversations.newDmWithIdentifier({
identifier: member.toLowerCase(),
identifierKind: "Ethereum",
});
return conversation;
} finally {
setLoading(false);
Expand Down
17 changes: 10 additions & 7 deletions apps/xmtp.chat/src/hooks/useIdentity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { SafeInstallation } from "@xmtp/browser-sdk";
import type { Identifier, SafeInstallation } from "@xmtp/browser-sdk";
import { useEffect, useState } from "react";
import { useClient } from "./useClient";

Expand All @@ -7,8 +7,11 @@ export const useIdentity = (syncOnMount: boolean = false) => {
const [syncing, setSyncing] = useState(false);
const [revoking, setRevoking] = useState(false);
const [inboxId, setInboxId] = useState<string | null>(null);
const [recoveryAddress, setRecoveryAddress] = useState<string | null>(null);
const [accountAddresses, setAccountAddresses] = useState<string[]>([]);
const [recoveryIdentifier, setRecoveryIdentifier] =
useState<Identifier | null>(null);
const [accountIdentifiers, setAccountIdentifiers] = useState<Identifier[]>(
[],
);
const [installations, setInstallations] = useState<SafeInstallation[]>([]);

useEffect(() => {
Expand All @@ -27,8 +30,8 @@ export const useIdentity = (syncOnMount: boolean = false) => {
try {
const inboxState = await client.inboxState(true);
setInboxId(inboxState.inboxId);
setAccountAddresses(inboxState.accountAddresses);
setRecoveryAddress(inboxState.recoveryAddress);
setAccountIdentifiers(inboxState.accountIdentifiers);
setRecoveryIdentifier(inboxState.recoveryIdentifier);
const installations = inboxState.installations.filter(
(installation) => installation.id !== client.installationId,
);
Expand Down Expand Up @@ -67,10 +70,10 @@ export const useIdentity = (syncOnMount: boolean = false) => {
};

return {
accountAddresses,
accountIdentifiers,
inboxId,
installations,
recoveryAddress,
recoveryIdentifier,
revokeAllOtherInstallations,
revokeInstallation,
revoking,
Expand Down
25 changes: 2 additions & 23 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4848,21 +4848,7 @@ __metadata:
languageName: node
linkType: hard

"@xmtp/browser-sdk@npm:0.0.23":
version: 0.0.23
resolution: "@xmtp/browser-sdk@npm:0.0.23"
dependencies:
"@xmtp/content-type-group-updated": "npm:^2.0.0"
"@xmtp/content-type-primitives": "npm:^2.0.0"
"@xmtp/content-type-text": "npm:^2.0.0"
"@xmtp/proto": "npm:^3.72.3"
"@xmtp/wasm-bindings": "npm:^0.0.21"
uuid: "npm:^11.0.3"
checksum: 10/e703462bd32ad6090169b4d1cd47c75cdd1fac0a850659b07110f6d7667244e699234e17e7f9fd01b7240ba35683be6bd443ce5960646482abe0725ec302232e
languageName: node
linkType: hard

"@xmtp/browser-sdk@workspace:sdks/browser-sdk":
"@xmtp/browser-sdk@npm:1.0.0-rc1, @xmtp/browser-sdk@workspace:sdks/browser-sdk":
version: 0.0.0-use.local
resolution: "@xmtp/browser-sdk@workspace:sdks/browser-sdk"
dependencies:
Expand Down Expand Up @@ -5302,13 +5288,6 @@ __metadata:
languageName: node
linkType: hard

"@xmtp/wasm-bindings@npm:^0.0.21":
version: 0.0.21
resolution: "@xmtp/wasm-bindings@npm:0.0.21"
checksum: 10/fa51d41c7381cd19774f72fd501cf9c2dcee72e3a750d4411edc55f27dd7059c4bd1ca9727a4dbbf8be61bea61e285c8801e769a88647d770494d1d75b0d6930
languageName: node
linkType: hard

"@xmtp/wasm-bindings@npm:^1.0.0-rc4":
version: 1.0.0-rc4
resolution: "@xmtp/wasm-bindings@npm:1.0.0-rc4"
Expand Down Expand Up @@ -5404,7 +5383,7 @@ __metadata:
"@types/react": "npm:^18.3.18"
"@types/react-dom": "npm:^18.3.5"
"@vitejs/plugin-react": "npm:^4.3.4"
"@xmtp/browser-sdk": "npm:0.0.23"
"@xmtp/browser-sdk": "npm:1.0.0-rc1"
"@xmtp/content-type-group-updated": "npm:^2.0.0"
"@xmtp/content-type-primitives": "npm:^2.0.0"
"@xmtp/content-type-reaction": "npm:^2.0.0"
Expand Down
Loading