Show add chain menu when metadata is required in tx card

Tweak footer fonts
pull/114/head
J M Rossy 2 months ago
parent bc6f4f6235
commit 1878af0892
  1. 6
      src/components/nav/Footer.tsx
  2. 8
      src/components/nav/Header.tsx
  3. 52
      src/components/search/SearchFilterBar.tsx
  4. 40
      src/features/chains/ChainSearchModal.tsx
  5. 7
      src/features/chains/MissingChainConfigToast.tsx
  6. 2
      src/features/chains/queries/useScrapedChains.ts
  7. 40
      src/features/messages/cards/TransactionCard.tsx

@ -36,15 +36,15 @@ export function Footer() {
<footer className="text-white px-8 pt-14 pb-5 bg-gradient-to-b from-transparent to-black/40"> <footer className="text-white px-8 pt-14 pb-5 bg-gradient-to-b from-transparent to-black/40">
<div className="flex flex-col sm:flex-row gap-10 items-center justify-between"> <div className="flex flex-col sm:flex-row gap-10 items-center justify-between">
<div className="flex items-center justify-center"> <div className="flex items-center justify-center">
<div className="ml-2 w-14 sm:w-16 h-14 sm:h-16"> <div className="ml-2 w-12 sm:w-14 h-12 sm:h-14">
<HyperlaneLogo color={Color.white} /> <HyperlaneLogo color={Color.white} />
</div> </div>
<div className="text-xl sm:text-2xl font-medium ml-6 space-y-1 "> <div className="text-lg sm:text-xl font-medium ml-6 space-y-1 ">
<div>Go interchain</div> <div>Go interchain</div>
<div>with Hyperlane</div> <div>with Hyperlane</div>
</div> </div>
</div> </div>
<nav className="flex text-lg font-medium"> <nav className="flex font-medium">
<ul className={`${styles.linkCol} mr-14`}> <ul className={`${styles.linkCol} mr-14`}>
{footerLinks1.map((item) => ( {footerLinks1.map((item) => (
<li className="" key={item.title}> <li className="" key={item.title}>

@ -63,9 +63,6 @@ export function Header({ pathName }: { pathName: string }) {
> >
Docs Docs
</a> </a>
{/* <Link href="/settings" className={navLinkClass('/settings')}>
Settings
</Link> */}
{showSearch && <MiniSearchBar />} {showSearch && <MiniSearchBar />}
</nav> </nav>
{/* Dropdown menu, used on mobile */} {/* Dropdown menu, used on mobile */}
@ -79,11 +76,6 @@ export function Header({ pathName }: { pathName: string }) {
Home Home
</MobileNavLink> </MobileNavLink>
), ),
// ({ close }) => (
// <MobileNavLink href="/settings" closeDropdown={close} key="Settings">
// Settings
// </MobileNavLink>
// ),
// ({ close }) => ( // ({ close }) => (
// <MobileNavLink href="/api" closeDropdown={c} key="API"> // <MobileNavLink href="/api" closeDropdown={c} key="API">
// API // API

@ -3,18 +3,11 @@ import { useState } from 'react';
import { ChainMetadata } from '@hyperlane-xyz/sdk'; import { ChainMetadata } from '@hyperlane-xyz/sdk';
import { trimToLength } from '@hyperlane-xyz/utils'; import { trimToLength } from '@hyperlane-xyz/utils';
import { import { ChevronIcon, IconButton, Popover, XIcon, useModal } from '@hyperlane-xyz/widgets';
ChainSearchMenu,
ChevronIcon, import { ChainSearchModal } from '../../features/chains/ChainSearchModal';
IconButton,
Modal,
Popover,
XIcon,
} from '@hyperlane-xyz/widgets';
import { useScrapedEvmChains } from '../../features/chains/queries/useScrapedChains';
import { getChainDisplayName } from '../../features/chains/utils'; import { getChainDisplayName } from '../../features/chains/utils';
import { useMultiProvider, useStore } from '../../store'; import { useMultiProvider } from '../../store';
import { Color } from '../../styles/Color'; import { Color } from '../../styles/Color';
import { SolidButton } from '../buttons/SolidButton'; import { SolidButton } from '../buttons/SolidButton';
import { TextButton } from '../buttons/TextButton'; import { TextButton } from '../buttons/TextButton';
@ -68,31 +61,22 @@ function ChainSelector({
value: ChainId | null; value: ChainId | null;
onChangeValue: (value: string | null) => void; onChangeValue: (value: string | null) => void;
}) { }) {
const { isOpen, open, close } = useModal();
const multiProvider = useMultiProvider(); const multiProvider = useMultiProvider();
const { chains } = useScrapedEvmChains(multiProvider); const chainName = value
const { chainMetadataOverrides, setChainMetadataOverrides } = useStore((s) => ({ ? trimToLength(getChainDisplayName(multiProvider, value, true), 12)
chainMetadataOverrides: s.chainMetadataOverrides, : undefined;
setChainMetadataOverrides: s.setChainMetadataOverrides,
}));
const [showModal, setShowModal] = useState(false);
const closeModal = () => {
setShowModal(false);
};
const onClickChain = (c: ChainMetadata) => { const onClickChain = (c: ChainMetadata) => {
onChangeValue(c.chainId.toString()); onChangeValue(c.chainId.toString());
closeModal(); close();
}; };
const onClear = () => { const onClear = () => {
onChangeValue(null); onChangeValue(null);
}; };
const chainName = value
? trimToLength(getChainDisplayName(multiProvider, value, true), 12)
: undefined;
return ( return (
<div className="relative"> <div className="relative">
<button <button
@ -101,7 +85,7 @@ function ChainSelector({
'text-sm sm:min-w-[5.8rem] px-1.5 sm:px-2.5 py-1 flex items-center justify-center font-medium rounded-lg border border-pink-500 hover:opacity-80 active:opacity-70 transition-all', 'text-sm sm:min-w-[5.8rem] px-1.5 sm:px-2.5 py-1 flex items-center justify-center font-medium rounded-lg border border-pink-500 hover:opacity-80 active:opacity-70 transition-all',
value ? 'bg-pink-500 text-white pr-7 sm:pr-8' : 'text-pink-500', value ? 'bg-pink-500 text-white pr-7 sm:pr-8' : 'text-pink-500',
)} )}
onClick={() => setShowModal(!showModal)} onClick={open}
> >
<span>{chainName || text} </span> <span>{chainName || text} </span>
{!value && ( {!value && (
@ -115,19 +99,7 @@ function ChainSelector({
)} )}
</button> </button>
{value && <ClearButton onClick={onClear} />} {value && <ClearButton onClick={onClear} />}
<Modal <ChainSearchModal isOpen={isOpen} close={close} onClickChain={onClickChain} />
isOpen={showModal}
close={closeModal}
panelClassname="p-4 sm:p-5 max-w-lg min-h-[40vh]"
>
<ChainSearchMenu
chainMetadata={chains}
onClickChain={onClickChain}
overrideChainMetadata={chainMetadataOverrides}
onChangeOverrideMetadata={setChainMetadataOverrides}
showAddChainButton={true}
/>
</Modal>
</div> </div>
); );
} }

@ -0,0 +1,40 @@
import { ChainMetadata } from '@hyperlane-xyz/sdk';
import { ChainSearchMenu, Modal } from '@hyperlane-xyz/widgets';
import { useMultiProvider, useStore } from '../../store';
import { useScrapedEvmChains } from './queries/useScrapedChains';
export function ChainSearchModal({
isOpen,
close,
onClickChain,
showAddChainMenu,
}: {
isOpen: boolean;
close: () => void;
onClickChain?: (metadata: ChainMetadata) => void;
showAddChainMenu?: boolean;
}) {
const multiProvider = useMultiProvider();
const { chains } = useScrapedEvmChains(multiProvider);
const { chainMetadataOverrides, setChainMetadataOverrides } = useStore((s) => ({
chainMetadataOverrides: s.chainMetadataOverrides,
setChainMetadataOverrides: s.setChainMetadataOverrides,
}));
const onClick = onClickChain || (() => {});
return (
<Modal isOpen={isOpen} close={close} panelClassname="p-4 sm:p-5 max-w-lg min-h-[40vh]">
<ChainSearchMenu
chainMetadata={chains}
onClickChain={onClick}
overrideChainMetadata={chainMetadataOverrides}
onChangeOverrideMetadata={setChainMetadataOverrides}
showAddChainButton={true}
showAddChainMenu={showAddChainMenu}
/>
</Modal>
);
}

@ -1,5 +1,3 @@
import Link from 'next/link';
export function MissingChainConfigToast({ export function MissingChainConfigToast({
domainId, domainId,
chainId, chainId,
@ -14,10 +12,7 @@ export function MissingChainConfigToast({
: 'unknown message chain'; : 'unknown message chain';
return ( return (
<div> <div>
<span>{`No chain config found for ${errorDesc}. `}</span> <span>{`No chain config found for ${errorDesc}. You can add a config in the origin/destination chain selector.`}</span>
<Link href="/settings" className="underline">
Add a config
</Link>
</div> </div>
); );
} }

@ -50,7 +50,7 @@ export function useScrapedEvmChains(multiProvider: MultiProvider) {
); );
// Return only evmChains because of graphql only accept query non-evm chains (with bigint type not string) // Return only evmChains because of graphql only accept query non-evm chains (with bigint type not string)
return { chains: scrapedEvmChains }; return { chains: scrapedEvmChains };
}, [multiProvider, scrapedChains]); }, [multiProvider, chainMetadata, scrapedChains]);
return { chains, isFetching, isError }; return { chains, isFetching, isError };
} }

@ -1,10 +1,9 @@
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import Link from 'next/link';
import { PropsWithChildren, ReactNode, useState } from 'react'; import { PropsWithChildren, ReactNode, useState } from 'react';
import { MultiProvider } from '@hyperlane-xyz/sdk'; import { MultiProvider } from '@hyperlane-xyz/sdk';
import { isAddress, isZeroish } from '@hyperlane-xyz/utils'; import { isAddress, isZeroish } from '@hyperlane-xyz/utils';
import { Modal } from '@hyperlane-xyz/widgets'; import { Modal, useModal } from '@hyperlane-xyz/widgets';
import { Spinner } from '../../../components/animations/Spinner'; import { Spinner } from '../../../components/animations/Spinner';
import { ChainLogo } from '../../../components/icons/ChainLogo'; import { ChainLogo } from '../../../components/icons/ChainLogo';
@ -14,6 +13,7 @@ import { links } from '../../../consts/links';
import { useMultiProvider } from '../../../store'; import { useMultiProvider } from '../../../store';
import { MessageStatus, MessageTx } from '../../../types'; import { MessageStatus, MessageTx } from '../../../types';
import { getDateTimeString, getHumanReadableTimeString } from '../../../utils/time'; import { getDateTimeString, getHumanReadableTimeString } from '../../../utils/time';
import { ChainSearchModal } from '../../chains/ChainSearchModal';
import { getChainDisplayName, isEvmChain } from '../../chains/utils'; import { getChainDisplayName, isEvmChain } from '../../chains/utils';
import { debugStatusToDesc } from '../../debugger/strings'; import { debugStatusToDesc } from '../../debugger/strings';
import { MessageDebugResult } from '../../debugger/types'; import { MessageDebugResult } from '../../debugger/types';
@ -70,6 +70,8 @@ export function DestinationTransactionCard({
const isDestinationEvmChain = isEvmChain(multiProvider, chainId); const isDestinationEvmChain = isEvmChain(multiProvider, chainId);
const { isOpen, open, close } = useModal();
let content: ReactNode; let content: ReactNode;
if (transaction) { if (transaction) {
content = ( content = (
@ -104,22 +106,26 @@ export function DestinationTransactionCard({
); );
} else if (!hasChainConfig) { } else if (!hasChainConfig) {
content = ( content = (
<DeliveryStatus> <>
<div className="flex flex-col items-center"> <DeliveryStatus>
<div>Delivery status is unknown.</div> <div className="flex flex-col items-center">
<div className="mt-2 text-sm max-w-xs"> <div>Delivery status is unknown.</div>
Permissionless Interoperability (PI) chains require a config. <div className="mt-2 text-sm max-w-xs">
</div> Permissionless Interoperability (PI) chains require a config.
<div className="mt-2 mb-6 text-sm max-w-xs"> </div>
Please{' '} <div className="mt-2 mb-6 text-sm max-w-xs">
<Link href="/settings" className="underline underline-offset-2"> Please{' '}
add a config <button className="underline underline-offset-2" onClick={open}>
</Link>{' '} add metadata
for this chain. </button>{' '}
for this chain.
</div>
<CallDataModal debugResult={debugResult} />
</div> </div>
<CallDataModal debugResult={debugResult} /> </DeliveryStatus>
</div> {/* TODO get modal to auto-close after adding chain metadata */}
</DeliveryStatus> <ChainSearchModal isOpen={isOpen} close={close} showAddChainMenu={true} />
</>
); );
} else if (status === MessageStatus.Pending) { } else if (status === MessageStatus.Pending) {
if (isDestinationEvmChain) { if (isDestinationEvmChain) {

Loading…
Cancel
Save