/ ui / src / components / WalletBridge.tsx
WalletBridge.tsx
  1  import React, { useRef } from 'react';
  2  import { BridgeProtocol } from '@agoric/web-components';
  3  import { makeReactDappWalletBridge } from '@agoric/web-components/react';
  4  import { useSetAtom, useAtomValue } from 'jotai';
  5  import {
  6    bridgeHrefAtom,
  7    chainConnectionAtom,
  8    chainStorageWatcherAtom,
  9    offerSignerAtom,
 10    setIsDappApprovedAtom,
 11    walletUiHrefAtom,
 12  } from 'store/app';
 13  import { Box, ToastId, useToast } from '@chakra-ui/react';
 14  import { dappConfig } from 'config';
 15  
 16  type BridgeReadyMessage = {
 17    detail: {
 18      data: {
 19        type: string;
 20      };
 21      isDappApproved: boolean;
 22      requestDappConnection: (petname: string) => void;
 23      addOffer: (offer: any) => void;
 24    };
 25  };
 26  
 27  type BridgeMessage = {
 28    detail: {
 29      data: {
 30        type: string;
 31        isDappApproved: boolean;
 32      };
 33    };
 34  };
 35  
 36  type BridgeError = {
 37    detail: {
 38      type: string;
 39      e: Error;
 40    };
 41  };
 42  
 43  // Create a wrapper for dapp-wallet-bridge that is specific to
 44  // the app's instance of React.
 45  const DappWalletBridge = makeReactDappWalletBridge(React);
 46  
 47  const WalletBridge = () => {
 48    const setIsDappApproved = useSetAtom(setIsDappApprovedAtom);
 49    const setOfferSigner = useSetAtom(offerSignerAtom);
 50    const chainConnection = useAtomValue(chainConnectionAtom);
 51    const bridgeHref = useAtomValue(bridgeHrefAtom);
 52    const walletUiHref = useAtomValue(walletUiHrefAtom);
 53    const watcher = useAtomValue(chainStorageWatcherAtom);
 54  
 55    const toastId = useRef<ToastId | null>(null);
 56    const connectionSuccessfulToastId = useRef<ToastId | null>(null);
 57    const toast = useToast({
 58      position: 'bottom-right',
 59      isClosable: true,
 60      variant: 'left-accent',
 61    });
 62  
 63    const clearCurrentToast = () =>
 64      toastId.current && toast.close(toastId.current);
 65  
 66    const clearConnectionSuccessfulToast = () =>
 67      connectionSuccessfulToastId.current &&
 68      toast.close(connectionSuccessfulToastId.current);
 69  
 70    const showWarningToast = () => {
 71      clearConnectionSuccessfulToast();
 72      toastId.current = toast({
 73        description: (
 74          <p>
 75            Dapp is in read-only mode. Enable the connection at{' '}
 76            <a
 77              className="underline text-blue-500"
 78              href={walletUiHref}
 79              target="_blank"
 80              rel="noreferrer"
 81            >
 82              {walletUiHref}
 83            </a>{' '}
 84            to access markets.
 85          </p>
 86        ),
 87        status: 'warning',
 88      });
 89    };
 90  
 91    const showConnectionSuccessfulToast = () => {
 92      clearCurrentToast();
 93      connectionSuccessfulToastId.current = toast({
 94        title: (
 95          <p>
 96            Successfully connected to Agoric wallet at{' '}
 97            <a
 98              className="underline text-blue-500"
 99              href={walletUiHref}
100              target="_blank"
101              rel="noreferrer"
102            >
103              {walletUiHref}
104            </a>
105          </p>
106        ),
107        status: 'success',
108      });
109    };
110  
111    const onBridgeReady = (ev: BridgeReadyMessage) => {
112      const {
113        detail: { isDappApproved, requestDappConnection, addOffer },
114      } = ev;
115      setOfferSigner({ addOffer, isDappApproved });
116      if (isDappApproved) {
117        showConnectionSuccessfulToast();
118      } else {
119        requestDappConnection(dappConfig.DAPP_PETNAME);
120        showWarningToast();
121      }
122    };
123  
124    const onError = (ev: BridgeError) => {
125      const message = ev.detail.e.message;
126      //@todo clear current toast first?
127      toast({
128        description: (
129          <div>
130            <p>
131              Could not connect to Agoric wallet at{' '}
132              <a
133                className="underline text-blue-500"
134                href={walletUiHref}
135                target="_blank"
136                rel="noreferrer"
137              >
138                {walletUiHref}
139              </a>
140              {message && `: ${message}`}
141            </p>
142          </div>
143        ),
144        status: 'error',
145      });
146    };
147  
148    const onBridgeMessage = (ev: BridgeMessage) => {
149      const data = ev.detail.data;
150      const type = data.type;
151      switch (type) {
152        case BridgeProtocol.dappApprovalChanged:
153          setIsDappApproved(data.isDappApproved);
154          if (data.isDappApproved) {
155            showConnectionSuccessfulToast();
156          } else {
157            showWarningToast();
158          }
159          break;
160        default:
161          console.warn('Unhandled bridge message', data);
162      }
163    };
164  
165    return (
166      <Box display="none">
167        {chainConnection && watcher && (
168          <DappWalletBridge
169            bridgeHref={bridgeHref}
170            onBridgeMessage={onBridgeMessage}
171            onBridgeReady={onBridgeReady}
172            onError={onError}
173            address={chainConnection.address}
174            chainId={watcher.chainId}
175            // chainId={chainConnection.chainId}
176          />
177        )}
178      </Box>
179    );
180  };
181  
182  export default WalletBridge;