Skip to content

Commit

Permalink
feat: WalletConnect integration, part 2, pairing list
Browse files Browse the repository at this point in the history
  • Loading branch information
dianasavvatina committed Oct 21, 2024
1 parent b15dbc5 commit a18d63b
Show file tree
Hide file tree
Showing 22 changed files with 352 additions and 53 deletions.
13 changes: 10 additions & 3 deletions apps/desktop/src/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/* istanbul ignore file */
import { DynamicModalContext, useDynamicModal } from "@umami/components";
import { useDataPolling } from "@umami/data-polling";
import { WalletClient, useImplicitAccounts, useResetConnections } from "@umami/state";
import {
WalletClient,
useImplicitAccounts,
useResetBeaconConnections,
useResetWcConnections,
} from "@umami/state";
import { noop } from "lodash";
import { useEffect } from "react";
import { HashRouter, Navigate, Route, Routes } from "react-router-dom";
Expand Down Expand Up @@ -59,11 +64,13 @@ const LoggedInRouterWithPolling = () => {
};

const LoggedOutRouter = () => {
const resetBeaconConnections = useResetConnections();
const resetBeaconConnections = useResetBeaconConnections();
const resetWcConnections = useResetWcConnections();

useEffect(() => {
WalletClient.destroy().then(resetBeaconConnections).catch(noop);
}, [resetBeaconConnections]);
resetWcConnections();
}, [resetBeaconConnections, resetWcConnections]);

return (
<HashRouter>
Expand Down
12 changes: 6 additions & 6 deletions apps/desktop/src/utils/beacon/BeaconPeers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Image,
Text,
} from "@chakra-ui/react";
import { useGetConnectionInfo, usePeers, useRemovePeer } from "@umami/state";
import { useBeaconPeers, useGetBeaconConnectionInfo, useRemoveBeaconPeer } from "@umami/state";
import { parsePkh } from "@umami/tezos";
import capitalize from "lodash/capitalize";
import { Fragment } from "react";
Expand All @@ -22,10 +22,10 @@ import colors from "../../style/colors";
/**
* Component displaying a list of connected dApps.
*
* Loads dApps data from {@link usePeers} hook & zips it with generated dAppIds.
* Loads dApps data from {@link useBeaconPeers} hook & zips it with generated dAppIds.
*/
export const BeaconPeers = () => {
const { peers } = usePeers();
const { peers } = useBeaconPeers();

if (peers.length === 0) {
return (
Expand Down Expand Up @@ -57,7 +57,7 @@ export const BeaconPeers = () => {
* @param onRemove - action for deleting dApp connection.
*/
const PeerRow = ({ peerInfo }: { peerInfo: ExtendedPeerInfo }) => {
const removePeer = useRemovePeer();
const removeBeaconPeer = useRemoveBeaconPeer();

return (
<Flex justifyContent="space-between" height="106px" data-testid="peer-row" paddingY="30px">
Expand All @@ -76,7 +76,7 @@ const PeerRow = ({ peerInfo }: { peerInfo: ExtendedPeerInfo }) => {
<IconButton
aria-label="Remove Peer"
icon={<TrashIcon />}
onClick={() => removePeer(peerInfo)}
onClick={() => removeBeaconPeer(peerInfo)}
size="xs"
variant="circle"
/>
Expand All @@ -94,7 +94,7 @@ const PeerRow = ({ peerInfo }: { peerInfo: ExtendedPeerInfo }) => {
* @param peerInfo - peerInfo provided by beacon Api + computed dAppId.
*/
const StoredPeerInfo = ({ peerInfo }: { peerInfo: ExtendedPeerInfo }) => {
const connectionInfo = useGetConnectionInfo(peerInfo.senderId);
const connectionInfo = useGetBeaconConnectionInfo(peerInfo.senderId);

if (!connectionInfo) {
return null;
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/utils/beacon/PermissionRequestModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
import { useDynamicModalContext } from "@umami/components";
import {
WalletClient,
useAddConnection,
useAddBeaconConnection,
useAsyncActionHandler,
useGetImplicitAccount,
} from "@umami/state";
Expand All @@ -38,7 +38,7 @@ import { OwnedImplicitAccountsAutocomplete } from "../../components/AddressAutoc
import colors from "../../style/colors";

export const PermissionRequestModal = ({ request }: { request: PermissionRequestOutput }) => {
const addConnectionToBeaconSlice = useAddConnection();
const addConnectionToBeaconSlice = useAddBeaconConnection();
const getAccount = useGetImplicitAccount();
const { onClose } = useDynamicModalContext();
const { handleAsyncAction } = useAsyncActionHandler();
Expand Down
6 changes: 3 additions & 3 deletions apps/desktop/src/utils/beacon/useHandleBeaconMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
useAsyncActionHandler,
useFindNetwork,
useGetOwnedAccountSafe,
useRemovePeerBySenderId,
useRemoveBeaconPeerBySenderId,
} from "@umami/state";
import { type Network } from "@umami/tezos";

Expand All @@ -23,15 +23,15 @@ import { BeaconSignPage } from "../../components/SendFlow/Beacon/BeaconSignPage"
/**
* @returns a function that handles a beacon message and opens a modal with the appropriate content
*
* For operation requests it will also try to convert the operation(s) to our {@link Operation} format,
* For operation requests it will also try to convert the operation(s)n to our {@link Operation} format,
* estimate the fee and open the BeaconSignPage only if it succeeds
*/
export const useHandleBeaconMessage = () => {
const { openWith } = useDynamicModalContext();
const { handleAsyncAction } = useAsyncActionHandler();
const getAccount = useGetOwnedAccountSafe();
const findNetwork = useFindNetwork();
const removePeer = useRemovePeerBySenderId();
const removePeer = useRemoveBeaconPeerBySenderId();

// we should confirm that we support the network that the beacon request is coming from
const checkNetwork = ({
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/components/Menu/AppsMenu/AppsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button, Divider, Text } from "@chakra-ui/react";
import { useAddPeer } from "@umami/state";

import { BeaconPeers } from "../../beacon";
import { useOnWalletConnect } from "../../WalletConnect";
import { WcPeers, useOnWalletConnect } from "../../WalletConnect";
import { DrawerContentWrapper } from "../DrawerContentWrapper";

export const AppsMenu = () => {
Expand Down Expand Up @@ -31,6 +31,7 @@ export const AppsMenu = () => {
</Button>
<Divider marginTop={{ base: "36px", md: "40px" }} />
<BeaconPeers />
<WcPeers />
</DrawerContentWrapper>
);
};
13 changes: 11 additions & 2 deletions apps/web/src/components/WalletConnect/SessionProposalModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ import {
} from "@chakra-ui/react";
import { type WalletKitTypes } from "@reown/walletkit";
import { useDynamicModalContext } from "@umami/components";
import { useAsyncActionHandler, useGetImplicitAccount, walletKit } from "@umami/state";
import {
useAddWcConnection,
useAsyncActionHandler,
useGetImplicitAccount,
walletKit,
} from "@umami/state";
import { type NetworkName } from "@umami/tezos";
import { type SessionTypes } from "@walletconnect/types";
import { buildApprovedNamespaces, getSdkError } from "@walletconnect/utils";
import { FormProvider, useForm } from "react-hook-form";

Expand All @@ -32,6 +39,7 @@ export const SessionProposalModal = ({
proposal: WalletKitTypes.SessionProposal;
network: NetworkType;
}) => {
const addConnectionToWcSlice = useAddWcConnection();
const getAccount = useGetImplicitAccount();

const { onClose } = useDynamicModalContext();
Expand Down Expand Up @@ -62,11 +70,12 @@ export const SessionProposalModal = ({
},
});

await walletKit.approveSession({
const session: SessionTypes.Struct = await walletKit.approveSession({
id: proposal.id,
namespaces,
sessionProperties: {},
});
addConnectionToWcSlice(session, account.address.pkh, network.split(":")[1] as NetworkName);
onClose();
});

Expand Down
128 changes: 128 additions & 0 deletions apps/web/src/components/WalletConnect/WalletConnectPeers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Center, Divider, Flex, Heading, IconButton, Image, Text, VStack } from "@chakra-ui/react";
import { useGetWcConnectionInfo, useRemoveWcPeer, useWcPeers } from "@umami/state";
import { parsePkh } from "@umami/tezos";
import { type SessionTypes } from "@walletconnect/types";
import { getSdkError } from "@walletconnect/utils";
import capitalize from "lodash/capitalize";

import { CodeSandboxIcon, StubIcon as TrashIcon } from "../../assets/icons";
import { useColor } from "../../styles/useColor";
import { AddressPill } from "../AddressPill/AddressPill";
import { EmptyMessage } from "../EmptyMessage";

/**
* Component displaying a list of connected dApps.
*
* Loads dApps data from {@link useWcPeers} hook & zips it with generated dAppIds.
*/
export const WcPeers = () => {
// const wcPeers: Record<string, SessionTypes.Struct> = walletKit.getActiveSessions();
const { peers: wcPeers } = useWcPeers();

console.log("wcPeers", wcPeers);

if (Object.keys(wcPeers).length === 0) {
return (
<EmptyMessage
alignItems="flex-start"
marginTop="40px"
data-testid="wc-peers-empty"
subtitle="No WalltConnect Apps to show"
title="Your WalletConnect Apps will appear here..."
/>
);
}

return (
<VStack
alignItems="flex-start"
gap="24px"
marginTop="24px"
data-testid="wc-peers"
divider={<Divider />}
spacing="0"
>
{
// loop peers and print PeerRow
Object.entries(wcPeers).map(([topic, peerInfo]) => (
<PeerRow key={topic} peerInfo={peerInfo} />
))
}
</VStack>
);
};

/**
* Component for displaying info about single connected dApp.
*
* @param peerInfo - peerInfo provided by wc Api + computed dAppId.
* @param onRemove - action for deleting dApp connection.
*/
const PeerRow = ({ peerInfo }: { peerInfo: SessionTypes.Struct }) => {
const color = useColor();
const removeWcPeer = useRemoveWcPeer();

return (
<Center
alignItems="center"
justifyContent="space-between"
width="full"
height="60px"
data-testid="peer-row"
>
<Flex height="100%">
<Center width="60px" marginRight="12px">
<Image
objectFit="cover"
fallback={<CodeSandboxIcon width="36px" height="36px" />}
src={peerInfo.peer.metadata.icons[0]}
/>
</Center>
<Center alignItems="flex-start" flexDirection="column" gap="6px">
<Heading color={color("900")} size="lg">
{peerInfo.peer.metadata.name}
</Heading>
<StoredPeerInfo peerInfo={peerInfo} />
</Center>
</Flex>
<IconButton
color={color("500")}
aria-label="Remove Peer"
icon={<TrashIcon />}
onClick={() =>
removeWcPeer({ topic: peerInfo.topic, reason: getSdkError("USER_DISCONNECTED") })
}
variant="iconButtonSolid"
/>
</Center>
);
};

/**
* Component for displaying additional info about connection with a dApp.
*
* Displays {@link AddressPill} with a connected account and network type,
* if information about the connection is stored in {@link wcSlice}.
*
* @param peerInfo - peerInfo provided by wc Api + computed dAppId.
*/
const StoredPeerInfo = ({ peerInfo }: { peerInfo: SessionTypes.Struct }) => {
const connectionInfo = useGetWcConnectionInfo(peerInfo.topic);

if (!connectionInfo) {
return null;
}

return (
<Flex>
<AddressPill marginRight="10px" address={parsePkh(connectionInfo.accountPkh)} />
<Divider marginRight="10px" orientation="vertical" />
<Text marginTop="2px" marginRight="4px" fontWeight={600} size="sm">
Network:
</Text>
<Text marginTop="2px" data-testid="dapp-connection-network" size="sm">
{capitalize(connectionInfo.networkName)}
</Text>
</Flex>
);
};
16 changes: 13 additions & 3 deletions apps/web/src/components/WalletConnect/WalletConnectProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
createWalletKit,
useAsyncActionHandler,
useAvailableNetworks,
useRemoveWcConnection,
useWcPeers,
walletKit,
} from "@umami/state";
import { type Network } from "@umami/tezos";
Expand Down Expand Up @@ -89,12 +91,20 @@ const useOnSessionRequest = () => {

// dApp can release WalletConnect session at any time and then the Wallet is notified by the WalletConnect server.
const useOnSessionDelete = () => {
const { handleAsyncAction } = useAsyncActionHandler();
const { peers, refresh } = useWcPeers();
const removeWcPeer = useRemoveWcConnection();
const toast = useToast();

return (event: WalletKitTypes.SessionDelete) =>
toast({
description: `dApp ${event.topic} released the connection.`,
status: "info",
handleAsyncAction(async () => {
const { topic } = event;
toast({
description: `Session deleted by dApp ${peers[topic].peer.metadata.name}`,
status: "info",
});
removeWcPeer(topic);
await refresh();
});
};

Expand Down
1 change: 1 addition & 0 deletions apps/web/src/components/WalletConnect/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./WalletConnectProvider";
export * from "./WalletConnectPeers";
10 changes: 5 additions & 5 deletions apps/web/src/components/beacon/BeaconPeers.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type ExtendedPeerInfo } from "@airgap/beacon-wallet";
import { Center, Divider, Flex, Heading, IconButton, Image, Text, VStack } from "@chakra-ui/react";
import { useGetConnectionInfo, usePeers, useRemovePeer } from "@umami/state";
import { useBeaconPeers, useGetBeaconConnectionInfo, useRemoveBeaconPeer } from "@umami/state";
import { parsePkh } from "@umami/tezos";
import capitalize from "lodash/capitalize";

Expand All @@ -12,10 +12,10 @@ import { EmptyMessage } from "../EmptyMessage";
/**
* Component displaying a list of connected dApps.
*
* Loads dApps data from {@link usePeers} hook & zips it with generated dAppIds.
* Loads dApps data from {@link useBeaconPeers} hook & zips it with generated dAppIds.
*/
export const BeaconPeers = () => {
const { peers } = usePeers();
const { peers } = useBeaconPeers();

if (peers.length === 0) {
return (
Expand Down Expand Up @@ -53,7 +53,7 @@ export const BeaconPeers = () => {
*/
const PeerRow = ({ peerInfo }: { peerInfo: ExtendedPeerInfo }) => {
const color = useColor();
const removePeer = useRemovePeer();
const removePeer = useRemoveBeaconPeer();

return (
<Center
Expand Down Expand Up @@ -98,7 +98,7 @@ const PeerRow = ({ peerInfo }: { peerInfo: ExtendedPeerInfo }) => {
* @param peerInfo - peerInfo provided by beacon Api + computed dAppId.
*/
const StoredPeerInfo = ({ peerInfo }: { peerInfo: ExtendedPeerInfo }) => {
const connectionInfo = useGetConnectionInfo(peerInfo.senderId);
const connectionInfo = useGetBeaconConnectionInfo(peerInfo.senderId);

if (!connectionInfo) {
return null;
Expand Down
Loading

0 comments on commit a18d63b

Please sign in to comment.