diff --git a/packages/widget-react/src/components/Image.tsx b/packages/widget-react/src/components/Image.tsx index 8de99df..baa7c71 100644 --- a/packages/widget-react/src/components/Image.tsx +++ b/packages/widget-react/src/components/Image.tsx @@ -23,6 +23,7 @@ const Image = ({ src, alt, placeholder, classNames, circle, ...attrs }: Props) = {alt} { + return ( +
+ + +
+ ) +} + +export default Images diff --git a/packages/widget-react/src/components/form/AssetOnChainButton.module.css b/packages/widget-react/src/components/form/AssetOnChainButton.module.css index e424da6..e763e45 100644 --- a/packages/widget-react/src/components/form/AssetOnChainButton.module.css +++ b/packages/widget-react/src/components/form/AssetOnChainButton.module.css @@ -15,20 +15,6 @@ overflow: hidden; } -.logo { - flex: none; - position: relative; - - .chain { - position: absolute; - bottom: 0; - right: -6px; - - border-radius: 50%; - border: 1px solid var(--bg); - } -} - .details { flex: 1; overflow: hidden; diff --git a/packages/widget-react/src/components/form/AssetOnChainButton.tsx b/packages/widget-react/src/components/form/AssetOnChainButton.tsx index 4d39f90..6ff02b4 100644 --- a/packages/widget-react/src/components/form/AssetOnChainButton.tsx +++ b/packages/widget-react/src/components/form/AssetOnChainButton.tsx @@ -1,5 +1,5 @@ import { IconChevronDown } from "@initia/icons-react" -import Image from "../Image" +import Images from "../Images" import type { BaseAsset, BaseChain } from "./types" import styles from "./AssetOnChainButton.module.css" @@ -12,10 +12,7 @@ interface Props { const AssetOnChainButton = ({ asset, chain, readOnly }: Props) => { const content = (
-
- - -
+
{asset.symbol}
diff --git a/packages/widget-react/src/pages/bridge/BridgeHistoryItem.module.css b/packages/widget-react/src/pages/bridge/BridgeHistoryItem.module.css index 3f729e1..035d5a2 100644 --- a/packages/widget-react/src/pages/bridge/BridgeHistoryItem.module.css +++ b/packages/widget-react/src/pages/bridge/BridgeHistoryItem.module.css @@ -3,7 +3,6 @@ display: flex; flex-direction: column; - align-items: flex-start; gap: 20px; color: inherit; @@ -20,8 +19,12 @@ } .header { - display: grid; - gap: 8px; + display: flex; + align-items: center; + justify-content: space-between; + + font-size: 12px; + font-weight: 500; .title { display: flex; @@ -37,36 +40,16 @@ color: var(--error); } - .badge { - background-color: var(--gray-6); - border-radius: 4px; - color: var(--gray-0); - font-size: 11px; - font-weight: 600; - padding: 2px 8px; - } - - .meta { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 12px; - + .date { color: var(--gray-2); - font-size: 12px; - font-weight: 500; - white-space: nowrap; } - .item { + .explorer { display: flex; align-items: center; - gap: 4px; + gap: 2px; - :last-of-type { - color: var(--gray-1); - font-family: var(--monospace); - } + color: var(--gray-3); } .divider { @@ -76,6 +59,24 @@ } } +.fees { + display: flex; + align-items: center; + gap: 4px; + + font-size: 12px; + font-weight: 500; + + .label { + color: var(--gray-2); + } + + .content { + color: var(--gray-1); + font-family: var(--monospace); + } +} + .route { display: grid; gap: 4px; @@ -84,7 +85,7 @@ .row { display: flex; align-items: center; - gap: 8px; + gap: 12px; } .asset { @@ -101,9 +102,19 @@ } .chain { + display: flex; + align-items: center; + gap: 8px; + color: var(--gray-2); font-size: 12px; font-weight: 500; + + .account { + display: flex; + align-items: center; + gap: 4px; + } } .arrow { diff --git a/packages/widget-react/src/pages/bridge/BridgeHistoryItem.tsx b/packages/widget-react/src/pages/bridge/BridgeHistoryItem.tsx index b4ec7e7..61900d0 100644 --- a/packages/widget-react/src/pages/bridge/BridgeHistoryItem.tsx +++ b/packages/widget-react/src/pages/bridge/BridgeHistoryItem.tsx @@ -1,10 +1,18 @@ +import { useAccount } from "wagmi" import { intlFormatDistance } from "date-fns" -import { useEffect, useMemo } from "react" +import { useEffect, useMemo, type ReactNode } from "react" import type { StatusResponseJson } from "@skip-go/client" -import { IconArrowDown, IconCheckCircleFilled, IconWarningFilled } from "@initia/icons-react" -import { formatAmount, truncate } from "@/public/utils" +import { + IconArrowDown, + IconCheckCircleFilled, + IconExternalLink, + IconWallet, + IconWarningFilled, +} from "@initia/icons-react" +import { AddressUtils, formatAmount, truncate } from "@/public/utils" import Loader from "@/components/Loader" import Image from "@/components/Image" +import Images from "@/components/Images" import ExplorerLink from "@/components/ExplorerLink" import { formatFees } from "./data/format" import type { RouterChainJson } from "./data/chains" @@ -14,6 +22,7 @@ import { useSkipAsset } from "./data/assets" import { BridgeType, getBridgeType, useTrackTxQuery, useTxStatusQuery } from "./data/tx" import type { TxIdentifier } from "./data/history" import { useBridgeHistoryDetails } from "./data/history" +import { useCosmosWallets } from "./data/cosmos" import styles from "./BridgeHistoryItem.module.css" const BridgeHistoryItem = ({ tx }: { tx: TxIdentifier }) => { @@ -28,6 +37,9 @@ const BridgeHistoryItem = ({ tx }: { tx: TxIdentifier }) => { const { data: txStatus } = useTxStatusQuery({ ...details, txHash: trackedTxHash }) const state = details.state ?? getState(txStatus) + const { address: connectedAddress = "", connector } = useAccount() + const { find } = useCosmosWallets() + useEffect(() => { if (trackedTxHash) { setDetails((prev) => { @@ -74,7 +86,6 @@ const BridgeHistoryItem = ({ tx }: { tx: TxIdentifier }) => { source_asset_denom: srcDenom, dest_asset_chain_id: dstChainId, dest_asset_denom: dstDenom, - operations, estimated_fees = [], } = route @@ -83,22 +94,39 @@ const BridgeHistoryItem = ({ tx }: { tx: TxIdentifier }) => { const srcAsset = useSkipAsset(srcDenom, srcChainId) const dstAsset = useSkipAsset(dstDenom, dstChainId) + const getWalletIcon = (address: string, image?: string) => { + if (image) { + return + } + + if (AddressUtils.equals(address, connectedAddress)) { + return + } + + return + } + const renderRow = ( amount: string, { symbol, decimals, logo_uri }: RouterAsset, - { chain_name, pretty_name }: RouterChainJson, + { chain_name, pretty_name, ...chain }: RouterChainJson, address: string, + walletIcon: ReactNode, ) => { return (
- +
{formatAmount(amount, { decimals })} {symbol}
- on {pretty_name || chain_name} ({truncate(address)}) + on {pretty_name || chain_name} +
+ {walletIcon} + {truncate(address)} +
@@ -106,59 +134,61 @@ const BridgeHistoryItem = ({ tx }: { tx: TxIdentifier }) => { } const type = getBridgeType(route) + const linkLabel = useMemo(() => { + switch (type) { + case BridgeType.SKIP: + return "Skip Explorer" + case BridgeType.OP_WITHDRAW: + return "Initia Scan" + } + }, [type]) const content = ( <>
{renderIcon()} - {type} +
+ {intlFormatDistance(new Date(timestamp), new Date(), { locale: "en-US" })} +
-
-
{intlFormatDistance(new Date(timestamp), new Date(), { locale: "en-US" })}
- {estimated_fees.length > 0 && ( - <> -
-
- Fees - {formatFees(estimated_fees)} -
- - )} - {operations.some((operation) => "swap" in operation) && ( - <> -
-
- Slippage - {values.slippagePercent}% -
- - )} +
+ {linkLabel} +
- {renderRow(amount_in, srcAsset, srcChain, values.sender)} + {renderRow( + amount_in, + srcAsset, + srcChain, + values.sender, + getWalletIcon(values.sender, find(values.cosmosWalletName)?.image), + )} +
- {renderRow(amount_out, dstAsset, dstChain, values.recipient)} + + {renderRow( + amount_out, + dstAsset, + dstChain, + values.recipient, + getWalletIcon(values.recipient), + )}
+ + {estimated_fees.length > 0 && ( +
+ Fees + {formatFees(estimated_fees)} +
+ )} ) - const explorerLink = useMemo(() => { - switch (type) { - case BridgeType.LZ: { - return new URL(`/tx/${txHash.toLowerCase()}`, "https://layerzeroscan.com").toString() - } - case BridgeType.SKIP: { - const searchParams = new URLSearchParams({ tx_hash: txHash, chain_id: chainId }) - return new URL(`?${searchParams.toString()}`, "https://explorer.skip.build").toString() - } - } - }, [chainId, txHash, type]) - if (type === BridgeType.OP_WITHDRAW) { return ( @@ -167,8 +197,11 @@ const BridgeHistoryItem = ({ tx }: { tx: TxIdentifier }) => { ) } + const searchParams = new URLSearchParams({ tx_hash: txHash, chain_id: chainId }) + const skipExplorerUrl = new URL(`?${searchParams.toString()}`, "https://explorer.skip.build") + return ( - + {content} ) diff --git a/packages/widget-react/src/pages/bridge/OperationItem.module.css b/packages/widget-react/src/pages/bridge/OperationItem.module.css index e7a04e1..d3ee779 100644 --- a/packages/widget-react/src/pages/bridge/OperationItem.module.css +++ b/packages/widget-react/src/pages/bridge/OperationItem.module.css @@ -1,7 +1,7 @@ .content { display: flex; align-items: center; - gap: 8px; + gap: 12px; .info { flex: 1; diff --git a/packages/widget-react/src/pages/bridge/OperationItem.tsx b/packages/widget-react/src/pages/bridge/OperationItem.tsx index 7c2280f..48602cb 100644 --- a/packages/widget-react/src/pages/bridge/OperationItem.tsx +++ b/packages/widget-react/src/pages/bridge/OperationItem.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from "react" import { IconArrowDown } from "@initia/icons-react" import { formatAmount, truncate } from "@/public/utils" import placeholder from "@/data/placeholder" -import Image from "@/components/Image" +import Images from "@/components/Images" import WidgetTooltip from "@/components/WidgetTooltip" import CopyButton from "@/components/CopyButton" import { useSkipAsset } from "./data/assets" @@ -18,7 +18,7 @@ interface ComponentProps extends Props { const OperationItemComponent = (props: ComponentProps) => { const { source, type, amount, denom, chainId, address, walletIcon } = props const { symbol = truncate(denom), decimals = 0, logo_uri = placeholder } = props - const { chain_name, pretty_name } = useSkipChain(chainId) + const { chain_name, pretty_name, ...chain } = useSkipChain(chainId) return (
@@ -41,7 +41,7 @@ const OperationItemComponent = (props: ComponentProps) => { )}
- +