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 Nov 15, 2024
1 parent 022c811 commit 3227347
Show file tree
Hide file tree
Showing 23 changed files with 268 additions and 60 deletions.
4 changes: 2 additions & 2 deletions apps/desktop/src/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* 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 } 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,7 +59,7 @@ const LoggedInRouterWithPolling = () => {
};

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

useEffect(() => {
WalletClient.destroy().then(resetBeaconConnections).catch(noop);
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 @@ -27,7 +27,7 @@ import {
import { useDynamicModalContext } from "@umami/components";
import {
WalletClient,
useAddConnection,
useAddBeaconConnection,
useAsyncActionHandler,
useGetImplicitAccount,
} from "@umami/state";
Expand All @@ -39,7 +39,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, 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 @@ -36,6 +36,7 @@ export const AppsMenu = () => {
title="Apps"
>
<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,13 @@ import {
} from "@chakra-ui/react";
import { type WalletKitTypes } from "@reown/walletkit";
import { useDynamicModalContext } from "@umami/components";
import { useAsyncActionHandler, useGetImplicitAccount, walletKit } from "@umami/state";
import {
useAsyncActionHandler,
useGetImplicitAccount,
useToggleWcPeerListUpdated,
walletKit,
} from "@umami/state";
import { type SessionTypes } from "@walletconnect/types";
import { buildApprovedNamespaces, getSdkError } from "@walletconnect/utils";
import { FormProvider, useForm } from "react-hook-form";

Expand All @@ -34,6 +40,7 @@ export const SessionProposalModal = ({
network: NetworkType;
}) => {
const getAccount = useGetImplicitAccount();
const toggleWcPeerListUpdated = useToggleWcPeerListUpdated();
const color = useColor();

const { onClose } = useDynamicModalContext();
Expand Down Expand Up @@ -64,11 +71,13 @@ export const SessionProposalModal = ({
},
});

await walletKit.approveSession({
const session: SessionTypes.Struct = await walletKit.approveSession({
id: proposal.id,
namespaces,
sessionProperties: {},
});
console.log("Approved session", session);
toggleWcPeerListUpdated();
onClose();
});

Expand Down
125 changes: 125 additions & 0 deletions apps/web/src/components/WalletConnect/WalletConnectPeers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { Center, Divider, Flex, IconButton, Image, Text, VStack } from "@chakra-ui/react";
import { useDisconnectWalletConnectPeer, useGetWcPeerListToggle, walletKit } 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";
import { EmptyMessage } from "../EmptyMessage";

/**
* Component displaying a list of connected dApps.
*
* Loads dApps data from WalletConnct API and displays it in a list.
*/
export const WcPeers = () => {
const sessions: Record<string, SessionTypes.Struct> = walletKit.getActiveSessions();
useGetWcPeerListToggle();

if (Object.keys(sessions).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(sessions).map(([topic, sessionInfo]) => (
<PeerRow key={topic} sessionInfo={sessionInfo} />
))
}
</VStack>
);
};

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

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={sessionInfo.peer.metadata.icons[0]}
/>
</Center>
<Center alignItems="flex-start" flexDirection="column" gap="6px">
<Text color={color("900")} size="lg">
{sessionInfo.peer.metadata.name}
</Text>
<StoredSessionInfo sessionInfo={sessionInfo} />
</Center>
</Flex>
<IconButton
color={color("500")}
aria-label="Remove Peer"
icon={<TrashIcon />}
onClick={() =>
disconnectWalletConnectPeer({
topic: sessionInfo.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.
*
* @param sessionInfo - sessionInfo provided by WalletConnect Api.
* Account is stored in format: tezos:ghostnet:tz1...
* Network is stored in format: tezos:mainnet
*/
const StoredSessionInfo = ({ sessionInfo }: { sessionInfo: SessionTypes.Struct }) => (
<Flex>
<AddressPill
marginRight="10px"
address={parsePkh(sessionInfo.namespaces.tezos.accounts[0].split(":")[2])}
/>
<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(sessionInfo.namespaces.tezos.chains?.[0].split(":")[1] ?? "")}
</Text>
</Flex>
);
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
createWalletKit,
useAsyncActionHandler,
useAvailableNetworks,
useToggleWcPeerListUpdated,
walletKit,
} from "@umami/state";
import { type Network } from "@umami/tezos";
Expand All @@ -20,6 +21,7 @@ export const WalletConnectProvider = ({ children }: PropsWithChildren) => {
const [isWalletKitCreated, setWalletKitCreated] = useState(false);
const { handleAsyncActionUnsafe } = useAsyncActionHandler();
const { openWith } = useDynamicModalContext();
const toggleWcPeerListUpdated = useToggleWcPeerListUpdated();
const toast = useToast();

const availableNetworks: Network[] = useAvailableNetworks();
Expand Down Expand Up @@ -69,8 +71,10 @@ export const WalletConnectProvider = ({ children }: PropsWithChildren) => {
description: "Session deleted by peer dApp",
status: "info",
});
// now re-render peer list
toggleWcPeerListUpdated();
},
[toast]
[toast, toggleWcPeerListUpdated]
);

const onSessionRequest = useCallback(
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
4 changes: 2 additions & 2 deletions apps/web/src/components/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 @@ -41,7 +41,7 @@ import { JsValueWrap } from "../JsValueWrap";

export const PermissionRequestModal = ({ request }: { request: PermissionRequestOutput }) => {
const color = useColor();
const addConnectionToBeaconSlice = useAddConnection();
const addConnectionToBeaconSlice = useAddBeaconConnection();
const getAccount = useGetImplicitAccount();
const { onClose } = useDynamicModalContext();
const { handleAsyncAction } = useAsyncActionHandler();
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/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 @@ -31,7 +31,7 @@ export const useHandleBeaconMessage = () => {
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
Loading

0 comments on commit 3227347

Please sign in to comment.