react-router#matchPath TypeScript Examples

The following examples show how to use react-router#matchPath. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx    From interbtc-ui with Apache License 2.0 4 votes vote down vote up
Navigation = ({
  onSmallScreen = false,
  className,
  ...rest
}: CustomProps & React.ComponentPropsWithRef<'nav'>): JSX.Element => {
  const location = useLocation();
  const { t } = useTranslation();
  const { vaultClientLoaded, address } = useSelector((state: StoreType) => state.general);

  const NAVIGATION_ITEMS = React.useMemo(
    () => [
      {
        name: 'nav_bridge',
        link: PAGES.BRIDGE,
        icon: RefreshIcon,
        hidden: false
      },
      {
        name: 'nav_transfer',
        link: PAGES.TRANSFER,
        icon: SwitchHorizontalIcon
      },
      {
        name: 'nav_transactions',
        link: PAGES.TRANSACTIONS,
        icon: ClipboardListIcon,
        hidden: false
      },
      {
        name: 'nav_staking',
        link: PAGES.STAKING,
        icon: CashIcon
      },
      {
        name: 'nav_dashboard',
        link: PAGES.DASHBOARD,
        icon: ChartSquareBarIcon,
        hidden: false
      },
      {
        name: 'nav_vaults',
        link: `${PAGES.VAULTS.replace(`:${URL_PARAMETERS.VAULT.ACCOUNT}`, address)}`,
        icon: ChipIcon,
        hidden: !vaultClientLoaded
      },
      {
        name: 'nav_earn',
        link: EARN_LINK,
        icon: CurrencyDollarIcon,
        hidden: !EARN_LINK,
        external: true,
        rest: {
          target: '_blank',
          rel: 'noopener noreferrer'
        }
      },
      {
        name: 'separator',
        link: '#',
        icon: () => null,
        separator: true
      },
      {
        name: 'nav_crowdloan',
        link: CROWDLOAN_LINK,
        icon: CashIcon,
        external: true,
        // This will suppress the link on testnet
        hidden: process.env.REACT_APP_BITCOIN_NETWORK !== 'mainnet' || !CROWDLOAN_LINK,
        rest: {
          target: '_blank',
          rel: 'noopener noreferrer'
        }
      },
      {
        name: 'nav_docs',
        link: INTERLAY_DOCS_LINK,
        icon: BookOpenIcon,
        external: true,
        rest: {
          target: '_blank',
          rel: 'noopener noreferrer'
        }
      },
      {
        name: 'nav_governance',
        link: GOVERNANCE_LINK,
        icon: ScaleIcon,
        external: true,
        hidden: !GOVERNANCE_LINK,
        rest: {
          target: '_blank',
          rel: 'noopener noreferrer'
        }
      },
      {
        name: 'nav_terms_and_conditions',
        link: TERMS_AND_CONDITIONS_LINK,
        icon: DocumentTextIcon,
        external: true,
        hidden: !TERMS_AND_CONDITIONS_LINK,
        rest: {
          target: '_blank',
          rel: 'noopener noreferrer'
        }
      }
    ],
    [address, vaultClientLoaded]
  );

  return (
    <nav className={clsx('px-2', 'space-y-1', { 'flex-1': !onSmallScreen }, className)} {...rest}>
      {NAVIGATION_ITEMS.map((navigationItem) => {
        if (navigationItem.separator) {
          return <Hr2 key={navigationItem.name} />;
        }

        if (navigationItem.hidden) {
          return null;
        }

        const match = matchPath(location.pathname, {
          path: navigationItem.link,
          exact: true,
          strict: false
        });

        return (
          <SidebarNavLink
            key={navigationItem.name}
            external={!!navigationItem.external}
            {...navigationItem.rest}
            href={navigationItem.link}
            className={clsx(
              match?.isExact
                ? clsx(
                    TEXT_CLASSES_FOR_SELECTED,
                    { 'bg-interlayHaiti-50': process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT },
                    { 'dark:bg-white': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA }
                  )
                : clsx(
                    TEXT_CLASSES_FOR_UNSELECTED,
                    { 'hover:bg-interlayHaiti-50': process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT },
                    { 'dark:hover:bg-white': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA },
                    { 'dark:hover:bg-opacity-10': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA }
                  ),
              'group',
              'flex',
              'items-center',
              'px-2',
              'py-2',
              onSmallScreen ? 'text-base' : 'text-sm',
              'font-medium',
              'rounded-md'
            )}
          >
            <navigationItem.icon
              className={clsx(
                match?.isExact ? TEXT_CLASSES_FOR_SELECTED : TEXT_CLASSES_FOR_UNSELECTED,
                onSmallScreen ? 'mr-4' : 'mr-3',
                'flex-shrink-0',
                'w-6',
                'h-6'
              )}
              aria-hidden='true'
            />
            {navigationItem.link === CROWDLOAN_LINK
              ? // TODO: not the nicest way of handling contextual navigation text, but
                // other solutions involve substantial refactoring of the navigation
                t(navigationItem.name, { governanceTokenSymbol: GOVERNANCE_TOKEN_SYMBOL })
              : t(navigationItem.name)}
          </SidebarNavLink>
        );
      })}
    </nav>
  );
}
Example #2
Source File: App.tsx    From polkabtc-ui with Apache License 2.0 4 votes vote down vote up
function App(): JSX.Element {
  const polkaBtcLoaded = useSelector((state: StoreType) => state.general.polkaBtcLoaded);
  const address = useSelector((state: StoreType) => state.general.address);
  const [isLoading, setIsLoading] = React.useState(true);
  const dispatch = useDispatch();
  const store = useStore();

  // Load the main PolkaBTC API - connection to the PolkaBTC bridge
  const loadPolkaBTC = React.useCallback(async (): Promise<void> => {
    try {
      window.polkaBTC = await connectToParachain();
      dispatch(isPolkaBtcLoaded(true));
      setIsLoading(false);
    } catch (error) {
      toast.warn('Unable to connect to the BTC-Parachain.');
      console.log('Unable to connect to the BTC-Parachain.');
      console.log('error.message => ', error.message);
    }

    try {
      startFetchingLiveData(dispatch, store);
    } catch (error) {
      console.log('Error fetching live data.');
      console.log('error.message => ', error.message);
    }
  }, [
    dispatch,
    store
  ]);

  // Load the connection to the faucet - only for testnet purposes
  const loadFaucet = React.useCallback(async (): Promise<void> => {
    try {
      window.faucet = new FaucetClient(constants.FAUCET_URL);
      dispatch(isFaucetLoaded(true));
    } catch (error) {
      console.log('Unable to connect to the faucet.');
      console.log('error.message => ', error.message);
    }
  }, [
    dispatch
  ]);

  React.useEffect(() => {
    if (!polkaBtcLoaded) return;
    if (!address) return;

    const id = window.polkaBTC.api.createType(ACCOUNT_ID_TYPE_NAME, address);

    // Maybe load the vault client - only if the current address is also registered as a vault
    (async () => {
      try {
        dispatch(isVaultClientLoaded(false));
        const vault = await window.polkaBTC.vaults.get(id);
        dispatch(isVaultClientLoaded(!!vault));
      } catch (error) {
        // TODO: should add error handling
        console.log('No PolkaBTC vault found for the account in the connected Polkadot wallet.');
        console.log('error.message => ', error.message);
      }
    })();

    // Maybe load the staked relayer client - only if the current address is also registered as a vault
    (async () => {
      try {
        dispatch(isStakedRelayerLoaded(false));
        const stakedRelayers = await window.polkaBTC.stakedRelayer.list();
        dispatch(isStakedRelayerLoaded(stakedRelayers.includes(id)));
      } catch (error) {
        // TODO: should add error handling
        console.log('No PolkaBTC staked relayer found for the account in the connected Polkadot wallet.');
        console.log('error.message => ', error.message);
      }
    })();
  }, [
    polkaBtcLoaded,
    address,
    dispatch
  ]);

  React.useEffect(() => {
    // Do not load data if showing static landing page only
    if (checkStaticPage()) return;
    if (!polkaBtcLoaded) return;

    // Initialize data on app bootstrap
    (async () => {
      try {
        const [
          totalPolkaBTC,
          totalLockedDOT,
          btcRelayHeight,
          bitcoinHeight,
          state
        ] = await Promise.all([
          window.polkaBTC.treasury.total(),
          window.polkaBTC.collateral.totalLocked(),
          window.polkaBTC.btcRelay.getLatestBlockHeight(),
          window.polkaBTC.electrsAPI.getLatestBlockHeight(),
          window.polkaBTC.stakedRelayer.getCurrentStateOfBTCParachain()
        ]);

        const parachainStatus = (state: StatusCode) => {
          if (state.isError) {
            return ParachainStatus.Error;
          } else if (state.isRunning) {
            return ParachainStatus.Running;
          } else if (state.isShutdown) {
            return ParachainStatus.Shutdown;
          } else {
            return ParachainStatus.Loading;
          }
        };

        dispatch(
          initGeneralDataAction(
            displayBtcAmount(totalPolkaBTC),
            displayDotAmount(totalLockedDOT),
            Number(btcRelayHeight),
            bitcoinHeight,
            parachainStatus(state)
          )
        );
      } catch (error) {
        // TODO: should have error handling instead of console
        console.log(error);
      }
    })();
  }, [
    dispatch,
    polkaBtcLoaded
  ]);

  // Loads the address for the currently select account and maybe loads the vault and staked relayer dashboards
  React.useEffect(() => {
    if (checkStaticPage()) return; // Do not load data if showing static landing page only
    if (!polkaBtcLoaded) return;

    (async () => {
      const theExtensions = await web3Enable(APP_NAME);
      if (theExtensions.length === 0) return;

      dispatch(setInstalledExtensionAction(theExtensions.map(extension => extension.name)));

      const accounts = await web3Accounts();
      if (accounts.length === 0) {
        dispatch(changeAddressAction(''));
        return;
      }

      const matchedAccount = accounts.find(account => account.address === address);
      const newAddress = matchedAccount ? address : accounts[0].address;

      const { signer } = await web3FromAddress(newAddress);
      // TODO: could store the active address just in one place (either in `window` object or in redux)
      window.polkaBTC.setAccount(newAddress, signer);
      dispatch(changeAddressAction(newAddress));
    })();
  }, [
    address,
    polkaBtcLoaded,
    dispatch
  ]);

  // Loads the PolkaBTC bridge and the faucet
  React.useEffect(() => {
    // Do not load data if showing static landing page only
    if (checkStaticPage()) return;
    if (polkaBtcLoaded) return;

    (async () => {
      try {
        // TODO: should avoid any race condition
        setTimeout(() => {
          if (isLoading) setIsLoading(false);
        }, 3000);
        await loadPolkaBTC();
        await loadFaucet();
        keyring.loadAll({});
      } catch (error) {
        console.log(error.message);
      }
    })();
    startFetchingLiveData(dispatch, store);
  }, [
    loadPolkaBTC,
    loadFaucet,
    isLoading,
    polkaBtcLoaded,
    dispatch,
    store
  ]);

  return (
    <>
      <ToastContainer
        position='top-right'
        autoClose={5000}
        hideProgressBar={false} />
      <Layout>
        <LazyLoadingErrorBoundary>
          <Route
            render={({ location }) => {
              if (checkStaticPage()) {
                const pageURLs = [
                  PAGES.stakedRelayer,
                  PAGES.vaults,
                  PAGES.challenges,
                  PAGES.parachain,
                  PAGES.oracles,
                  PAGES.issue,
                  PAGES.redeem,
                  PAGES.relay,
                  PAGES.dashboard,
                  PAGES.vault,
                  PAGES.feedback,
                  PAGES.application
                ];

                for (const pageURL of pageURLs) {
                  if (matchPath(location.pathname, { path: pageURL })) {
                    return <Redirect to={PAGES.home} />;
                  }
                }
              }

              return (
                // TODO: block for now
                // <TransitionWrapper location={location}>
                // TODO: should use loading spinner instead of `Loading...`
                <React.Suspense fallback={<div>Loading...</div>}>
                  <Switch location={location}>
                    <Route path={PAGES.stakedRelayer}>
                      <StakedRelayer />
                    </Route>
                    <Route path={PAGES.vaults}>
                      <VaultsDashboard />
                    </Route>
                    <Route path={PAGES.challenges}>
                      <Challenges />
                    </Route>
                    <Route path={PAGES.parachain}>
                      <ParachainDashboard />
                    </Route>
                    <Route path={PAGES.oracles}>
                      <OraclesDashboard />
                    </Route>
                    <Route path={PAGES.issue}>
                      <IssueRequests />
                    </Route>
                    <Route path={PAGES.redeem}>
                      <RedeemRequests />
                    </Route>
                    <Route path={PAGES.relay}>
                      <RelayDashboard />
                    </Route>
                    <Route path={PAGES.dashboard}>
                      <Dashboard />
                    </Route>
                    <Route path={PAGES.vault}>
                      <VaultDashboard />
                    </Route>
                    <Route path={PAGES.feedback}>
                      <Feedback />
                    </Route>
                    <Route
                      exact
                      path={PAGES.application}>
                      <Application />
                    </Route>
                    <Route
                      path={PAGES.home}
                      exact>
                      <LandingPage />
                    </Route>
                    <Route path='*'>
                      <NoMatch />
                    </Route>
                  </Switch>
                </React.Suspense>
                // </TransitionWrapper>
              );
            }} />
        </LazyLoadingErrorBoundary>
      </Layout>
    </>
  );
}