Files
zeekayandHanzo Dev dcf0d9a048 fix(explore/web3): null-safe useProvider().data in 4 consumers (v1.1.5)
v1.1.4 changed useProvider to return null (react-query v5 forbids undefined)
but only switched the lib/web3 hooks. Four UI consumers kept
'const { data: { wallet } = {} } = useProvider()'; '= {}' defaults only
undefined, not null, so null data threw 'Cannot destructure property wallet
of {} as it is null' -> whole-page error boundary. Fixed to
'useProvider().data ?? {}' in ChainWidget, MultichainEcosystemsTableItem,
NetworkAddToWallet, AddressAddToWallet (+ wrap a pre-existing 169-char line).

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-14 08:02:11 -07:00

127 lines
4.7 KiB
TypeScript

import { Heading } from '@luxfi/ui/heading';
import { IconButton } from '@luxfi/ui/icon-button';
import { Skeleton } from '@luxfi/ui/skeleton';
import { Tooltip } from '@luxfi/ui/tooltip';
import React from 'react';
import type * as multichain from '@luxfi/multichain-aggregator-types';
import type { ClusterChainConfig } from 'types/multichain';
import useIsMobile from 'lib/hooks/useIsMobile';
import { cn } from 'lib/utils/cn';
import useAddChainClick from 'lib/web3/useAddChainClick';
import useProvider from 'lib/web3/useProvider';
import { WALLETS_INFO } from 'lib/web3/wallets';
import { LinkBox, LinkOverlay } from 'toolkit/next/link';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import ChainIcon from 'ui/shared/externalChains/ChainIcon';
import IconSvg from 'ui/shared/IconSvg';
import RollupStageBadge from 'ui/snippets/navigation/RollupStageBadge';
interface Props {
data: ClusterChainConfig;
isLoading: boolean;
metrics: multichain.ChainMetrics | undefined;
}
const ChainWidget = ({ data, isLoading, metrics }: Props) => {
const isMobile = useIsMobile();
const { wallet } = useProvider().data ?? {};
const walletIcon = wallet ? WALLETS_INFO[wallet].icon : undefined;
const handleAddToWalletClick = useAddChainClick({ source: 'Chain widget' });
const chainStats = (
<div className="flex flex-col gap-2 items-start font-medium">
<div className="flex gap-2">
<Skeleton loading={ isLoading } color="text.secondary">
<span>Chain ID</span>
</Skeleton>
<Skeleton loading={ isLoading }>{ data.id }</Skeleton>
<CopyToClipboard text={ String(data.id) } className="ml-0" isLoading={ isLoading }/>
</div>
{ metrics?.active_accounts?.current_full_week && (
<div className="flex gap-2">
<Skeleton loading={ isLoading } color="text.secondary">
<span>Active accounts</span>
</Skeleton>
<Skeleton loading={ isLoading }>{ Number(metrics.active_accounts.current_full_week).toLocaleString() }</Skeleton>
</div>
) }
{ metrics?.tps && (
<div className="flex gap-2">
<Skeleton loading={ isLoading } color="text.secondary">
<span>TPS</span>
</Skeleton>
<Skeleton loading={ isLoading }>{ metrics.tps }</Skeleton>
</div>
) }
</div>
);
if (isMobile) {
return (
<LinkBox
className={
'bg-[rgba(246,246,248,0.5)] dark:bg-white/5 rounded-xl border border-solid ' +
'border-black/20 dark:border-white/20 p-4 basis-full text-sm overflow-hidden'
}
>
<div className="flex justify-between mb-2">
<div className="flex min-w-0">
<ChainIcon data={ data } boxSize={ 5 } isLoading={ isLoading } noTooltip/>
<Heading level="3" className="min-w-0">
<LinkOverlay href={ data.explorer_url } external loading={ isLoading } className="group-hover:text-[var(--color-hover)]">
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
{ data.name }
</div>
</LinkOverlay>
</Heading>
</div>
<RollupStageBadge chainConfig={ data.app_config } isLoading={ isLoading }/>
</div>
{ chainStats }
</LinkBox>
);
}
return (
<LinkBox
className={ cn(
'group rounded-xl border border-solid p-6 text-sm overflow-hidden',
'bg-[rgba(246,246,248,0.5)] dark:bg-white/5',
'border-black/20 dark:border-white/20',
'basis-[calc((100%-3*12px)/4)]',
!isLoading && 'hover:bg-gray-50 dark:hover:bg-white/10 hover:border-[var(--color-hover)]',
) }
>
<div className="flex justify-between">
<ChainIcon data={ data } boxSize="30px" isLoading={ isLoading } noTooltip/>
{ walletIcon && (
<Tooltip content="Add to wallet">
<IconButton
onClick={ handleAddToWalletClick }
size="md"
variant="icon_background"
className="z-[1] bg-black/5 dark:bg-white/5"
loadingSkeleton={ isLoading }
>
<IconSvg name={ walletIcon } className="w-5 h-5"/>
</IconButton>
</Tooltip>
) }
</div>
<Heading level="3" className="my-3">
<LinkOverlay href={ data.explorer_url } external loading={ isLoading } className="group-hover:text-[var(--color-hover)]">
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
{ data.name }
</div>
</LinkOverlay>
</Heading>
<RollupStageBadge chainConfig={ data.app_config } isLoading={ isLoading } mb={ 2.5 }/>
{ chainStats }
</LinkBox>
);
};
export default React.memo(ChainWidget);