@lifi/widget 4.7.0 → 4.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/config/version.d.ts +1 -1
- package/dist/esm/config/version.js +1 -1
- package/dist/esm/config/version.js.map +1 -1
- package/dist/esm/hooks/useExplorer.d.ts +1 -1
- package/dist/esm/hooks/useExplorer.d.ts.map +1 -1
- package/dist/esm/hooks/useExplorer.js +7 -3
- package/dist/esm/hooks/useExplorer.js.map +1 -1
- package/dist/esm/pages/SendToWallet/BookmarksPage.js +2 -1
- package/dist/esm/pages/SendToWallet/BookmarksPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js +2 -1
- package/dist/esm/pages/SendToWallet/ConnectedWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js +2 -1
- package/dist/esm/pages/SendToWallet/RecentWalletsPage.js.map +1 -1
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js +2 -1
- package/dist/esm/pages/SendToWallet/SendToConfiguredWalletPage.js.map +1 -1
- package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js +2 -1
- package/dist/esm/pages/TransactionDetailsPage/TransactionDetailsPage.js.map +1 -1
- package/dist/esm/pages/TransactionDetailsPage/TransferIdCard.js +2 -1
- package/dist/esm/pages/TransactionDetailsPage/TransferIdCard.js.map +1 -1
- package/dist/esm/shared.d.ts +2 -1
- package/dist/esm/shared.js +2 -1
- package/dist/esm/utils/url.d.ts +5 -0
- package/dist/esm/utils/url.d.ts.map +1 -0
- package/dist/esm/utils/url.js +17 -0
- package/dist/esm/utils/url.js.map +1 -0
- package/package.json +1 -1
- package/src/config/version.ts +1 -1
- package/src/hooks/useExplorer.test.tsx +106 -0
- package/src/hooks/useExplorer.ts +11 -5
- package/src/pages/SendToWallet/BookmarksPage.tsx +3 -3
- package/src/pages/SendToWallet/ConnectedWalletsPage.tsx +3 -3
- package/src/pages/SendToWallet/RecentWalletsPage.tsx +3 -3
- package/src/pages/SendToWallet/SendToConfiguredWalletPage.tsx +3 -3
- package/src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx +5 -5
- package/src/pages/TransactionDetailsPage/TransferIdCard.tsx +2 -1
- package/src/shared.ts +1 -0
- package/src/utils/url.ts +17 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.7.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../../../src/config/version.ts"],"sourcesContent":["export const name = '@lifi/widget'\nexport const version = '4.7.1'\n"],"mappings":";AAAA,MAAa,OAAO;AACpB,MAAa,UAAU"}
|
|
@@ -7,7 +7,7 @@ type TransactionLinkProps = {
|
|
|
7
7
|
};
|
|
8
8
|
export declare const useExplorer: () => {
|
|
9
9
|
getTransactionLink: (props: TransactionLinkProps) => string | undefined;
|
|
10
|
-
getAddressLink: (address: string, chain?: Chain | number) => string;
|
|
10
|
+
getAddressLink: (address: string, chain?: Chain | number) => string | undefined;
|
|
11
11
|
};
|
|
12
12
|
//#endregion
|
|
13
13
|
//# sourceMappingURL=useExplorer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useExplorer.d.ts","names":[],"sources":["../../../src/hooks/useExplorer.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"useExplorer.d.ts","names":[],"sources":["../../../src/hooks/useExplorer.ts"],"mappings":";;KAoBK;EACH,QAAQ;EACR;EACA;;qBAGW;EACX,qBAAqB,OAAO;EAC5B,iBACE,iBACA,QAAQ"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useWidgetConfig } from "../providers/WidgetProvider/WidgetProvider.js";
|
|
2
2
|
import "../config/constants.js";
|
|
3
3
|
import { useAvailableChains } from "./useAvailableChains.js";
|
|
4
|
+
import { isHttpUrl } from "../utils/url.js";
|
|
4
5
|
import { ChainId, ChainType, isHex } from "@lifi/sdk";
|
|
5
6
|
import { useCallback } from "react";
|
|
6
7
|
//#region src/hooks/useExplorer.ts
|
|
@@ -47,13 +48,16 @@ const useExplorer = () => {
|
|
|
47
48
|
getTransactionLink: useCallback(({ txHash, txLink, chain }) => {
|
|
48
49
|
if (txHash) {
|
|
49
50
|
const config = getExplorerConfig(chain);
|
|
50
|
-
|
|
51
|
+
const validForEvm = config.resolvedChain?.chainType !== ChainType.EVM || config.hasOverride || isHex(txHash, { strict: true });
|
|
52
|
+
const link = `${config.url}/${config.txPath}/${txHash}`;
|
|
53
|
+
if (validForEvm && isHttpUrl(link)) return link;
|
|
51
54
|
}
|
|
52
|
-
return txLink;
|
|
55
|
+
return isHttpUrl(txLink) ? txLink : void 0;
|
|
53
56
|
}, [getExplorerConfig]),
|
|
54
57
|
getAddressLink: useCallback((address, chain) => {
|
|
55
58
|
const config = getExplorerConfig(chain);
|
|
56
|
-
|
|
59
|
+
const link = `${config.url}/${config.addressPath}/${address}`;
|
|
60
|
+
return isHttpUrl(link) ? link : void 0;
|
|
57
61
|
}, [getExplorerConfig])
|
|
58
62
|
};
|
|
59
63
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useExplorer.js","names":[],"sources":["../../../src/hooks/useExplorer.ts"],"sourcesContent":["import type { Chain } from '@lifi/sdk'\nimport { ChainId, ChainType, isHex } from '@lifi/sdk'\nimport { useCallback } from 'react'\nimport { internalExplorerUrl } from '../config/constants.js'\nimport { useAvailableChains } from '../hooks/useAvailableChains.js'\nimport { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'\n\nconst sanitiseBaseUrl = (baseUrl: string) => baseUrl.trim().replace(/\\/+$/, '')\n\nconst explorerPathOverrides: Partial<\n Record<ChainType | ChainId, { txPath: string; addressPath: string }>\n> = {\n [ChainId.SUI]: { txPath: 'txblock', addressPath: 'coin' },\n [ChainId.LTR]: { txPath: 'logs', addressPath: 'accounts' },\n [ChainType.TVM]: { txPath: '#/transaction', addressPath: '#/address' },\n // StellarExpert and steexp both route accounts at /account, never /address.\n [ChainType.STL]: { txPath: 'tx', addressPath: 'account' },\n}\n\ntype TransactionLinkProps = {\n chain?: Chain | number\n txHash?: string\n txLink?: string\n}\n\nexport const useExplorer = (): {\n getTransactionLink: (props: TransactionLinkProps) => string | undefined\n getAddressLink: (address: string
|
|
1
|
+
{"version":3,"file":"useExplorer.js","names":[],"sources":["../../../src/hooks/useExplorer.ts"],"sourcesContent":["import type { Chain } from '@lifi/sdk'\nimport { ChainId, ChainType, isHex } from '@lifi/sdk'\nimport { useCallback } from 'react'\nimport { internalExplorerUrl } from '../config/constants.js'\nimport { useAvailableChains } from '../hooks/useAvailableChains.js'\nimport { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'\nimport { isHttpUrl } from '../utils/url.js'\n\nconst sanitiseBaseUrl = (baseUrl: string) => baseUrl.trim().replace(/\\/+$/, '')\n\nconst explorerPathOverrides: Partial<\n Record<ChainType | ChainId, { txPath: string; addressPath: string }>\n> = {\n [ChainId.SUI]: { txPath: 'txblock', addressPath: 'coin' },\n [ChainId.LTR]: { txPath: 'logs', addressPath: 'accounts' },\n [ChainType.TVM]: { txPath: '#/transaction', addressPath: '#/address' },\n // StellarExpert and steexp both route accounts at /account, never /address.\n [ChainType.STL]: { txPath: 'tx', addressPath: 'account' },\n}\n\ntype TransactionLinkProps = {\n chain?: Chain | number\n txHash?: string\n txLink?: string\n}\n\nexport const useExplorer = (): {\n getTransactionLink: (props: TransactionLinkProps) => string | undefined\n getAddressLink: (\n address: string,\n chain?: Chain | number\n ) => string | undefined\n} => {\n const { explorerUrls } = useWidgetConfig()\n const { getChainById } = useAvailableChains()\n\n const getExplorerConfig = useCallback(\n (chain?: Chain | number) => {\n const resolvedChain = Number.isFinite(chain)\n ? getChainById(chain as number)\n : (chain as Chain)\n\n const explorerUrl =\n (resolvedChain\n ? (explorerUrls?.[resolvedChain.id]?.[0] ??\n resolvedChain.metamask.blockExplorerUrls[0])\n : explorerUrls?.internal?.[0]) || internalExplorerUrl\n\n const baseUrl =\n typeof explorerUrl === 'string' ? explorerUrl : explorerUrl.url\n\n const overrides =\n explorerPathOverrides[resolvedChain?.id as ChainId] ??\n explorerPathOverrides[resolvedChain?.chainType as ChainType]\n\n const defaultTxPath = overrides?.txPath ?? 'tx'\n const defaultAddressPath = overrides?.addressPath ?? 'address'\n const txPath =\n typeof explorerUrl === 'string'\n ? defaultTxPath\n : explorerUrl.txPath || defaultTxPath\n const addressPath =\n typeof explorerUrl === 'string'\n ? defaultAddressPath\n : explorerUrl.addressPath || defaultAddressPath\n\n return {\n url: sanitiseBaseUrl(baseUrl),\n txPath,\n addressPath,\n resolvedChain,\n hasOverride: Boolean(overrides),\n }\n },\n [explorerUrls, getChainById]\n )\n\n const getTransactionLink = useCallback(\n ({ txHash, txLink, chain }: TransactionLinkProps): string | undefined => {\n if (txHash) {\n const config = getExplorerConfig(chain)\n // For EVM chains, sanity-check the transaction hash as some relayers return custom task hashes that are not visible on-chain.\n // Skip the check when a chain-specific path override is registered — that's an explicit signal the chain's\n // explorer accepts a non-hex hash (e.g. Lighter, whose hash has no 0x prefix).\n const validForEvm =\n config.resolvedChain?.chainType !== ChainType.EVM ||\n config.hasOverride ||\n isHex(txHash, { strict: true })\n const link = `${config.url}/${config.txPath}/${txHash}`\n if (validForEvm && isHttpUrl(link)) {\n return link\n }\n }\n return isHttpUrl(txLink) ? txLink : undefined\n },\n [getExplorerConfig]\n )\n\n const getAddressLink = useCallback(\n (address: string, chain?: Chain | number) => {\n const config = getExplorerConfig(chain)\n const link = `${config.url}/${config.addressPath}/${address}`\n return isHttpUrl(link) ? link : undefined\n },\n [getExplorerConfig]\n )\n\n return {\n getTransactionLink,\n getAddressLink,\n }\n}\n"],"mappings":";;;;;;;AAQA,MAAM,mBAAmB,YAAoB,QAAQ,KAAK,CAAC,CAAC,QAAQ,QAAQ,EAAE;AAE9E,MAAM,wBAEF;EACD,QAAQ,MAAM;EAAE,QAAQ;EAAW,aAAa;CAAO;EACvD,QAAQ,MAAM;EAAE,QAAQ;EAAQ,aAAa;CAAW;EACxD,UAAU,MAAM;EAAE,QAAQ;EAAiB,aAAa;CAAY;EAEpE,UAAU,MAAM;EAAE,QAAQ;EAAM,aAAa;CAAU;AAC1D;AAQA,MAAa,oBAMR;CACH,MAAM,EAAE,iBAAiB,gBAAgB;CACzC,MAAM,EAAE,iBAAiB,mBAAmB;CAE5C,MAAM,oBAAoB,aACvB,UAA2B;EAC1B,MAAM,gBAAgB,OAAO,SAAS,KAAK,IACvC,aAAa,KAAe,IAC3B;EAEL,MAAM,eACH,gBACI,eAAe,cAAc,GAAG,GAAG,MACpC,cAAc,SAAS,kBAAkB,KACzC,cAAc,WAAW,OAAA;EAE/B,MAAM,UACJ,OAAO,gBAAgB,WAAW,cAAc,YAAY;EAE9D,MAAM,YACJ,sBAAsB,eAAe,OACrC,sBAAsB,eAAe;EAEvC,MAAM,gBAAgB,WAAW,UAAU;EAC3C,MAAM,qBAAqB,WAAW,eAAe;EACrD,MAAM,SACJ,OAAO,gBAAgB,WACnB,gBACA,YAAY,UAAU;EAC5B,MAAM,cACJ,OAAO,gBAAgB,WACnB,qBACA,YAAY,eAAe;EAEjC,OAAO;GACL,KAAK,gBAAgB,OAAO;GAC5B;GACA;GACA;GACA,aAAa,QAAQ,SAAS;EAChC;CACF,GACA,CAAC,cAAc,YAAY,CAC7B;CAgCA,OAAO;EACL,oBA/ByB,aACxB,EAAE,QAAQ,QAAQ,YAAsD;GACvE,IAAI,QAAQ;IACV,MAAM,SAAS,kBAAkB,KAAK;IAItC,MAAM,cACJ,OAAO,eAAe,cAAc,UAAU,OAC9C,OAAO,eACP,MAAM,QAAQ,EAAE,QAAQ,KAAK,CAAC;IAChC,MAAM,OAAO,GAAG,OAAO,IAAI,GAAG,OAAO,OAAO,GAAG;IAC/C,IAAI,eAAe,UAAU,IAAI,GAC/B,OAAO;GAEX;GACA,OAAO,UAAU,MAAM,IAAI,SAAS,KAAA;EACtC,GACA,CAAC,iBAAiB,CAaD;EACjB,gBAXqB,aACpB,SAAiB,UAA2B;GAC3C,MAAM,SAAS,kBAAkB,KAAK;GACtC,MAAM,OAAO,GAAG,OAAO,IAAI,GAAG,OAAO,YAAY,GAAG;GACpD,OAAO,UAAU,IAAI,IAAI,OAAO,KAAA;EAClC,GACA,CAAC,iBAAiB,CAKL;CACf;AACF"}
|
|
@@ -6,6 +6,7 @@ import { PageContainer } from "../../components/PageContainer.js";
|
|
|
6
6
|
import { defaultChainIdsByType } from "../../utils/chainType.js";
|
|
7
7
|
import { useToAddressRequirements } from "../../hooks/useToAddressRequirements.js";
|
|
8
8
|
import { shortenAddress } from "../../utils/wallet.js";
|
|
9
|
+
import { openInNewTab } from "../../utils/url.js";
|
|
9
10
|
import { useExplorer } from "../../hooks/useExplorer.js";
|
|
10
11
|
import { useHeader } from "../../hooks/useHeader.js";
|
|
11
12
|
import { useListHeight } from "../../hooks/useListHeight.js";
|
|
@@ -88,7 +89,7 @@ const BookmarksPage = () => {
|
|
|
88
89
|
{
|
|
89
90
|
icon: /* @__PURE__ */ jsx(OpenInNewRounded, {}),
|
|
90
91
|
label: t("button.viewOnExplorer"),
|
|
91
|
-
onClick: () =>
|
|
92
|
+
onClick: () => openInNewTab(getAddressLink(bookmark.address, defaultChainIdsByType[bookmark.chainType]))
|
|
92
93
|
},
|
|
93
94
|
{
|
|
94
95
|
icon: /* @__PURE__ */ jsx(DeleteOutline, {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BookmarksPage.js","names":["ListItem","ListItemButton"],"sources":["../../../../src/pages/SendToWallet/BookmarksPage.tsx"],"sourcesContent":["import ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport DeleteOutline from '@mui/icons-material/DeleteOutlined'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport TurnedIn from '@mui/icons-material/TurnedIn'\nimport { Button, List, ListItemAvatar, ListItemText } from '@mui/material'\nimport { useNavigate } from '@tanstack/react-router'\nimport { type JSX, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { AccountAvatar } from '../../components/Avatar/AccountAvatar.js'\nimport type { BottomSheetBase } from '../../components/BottomSheet/types.js'\nimport { ContextMenu } from '../../components/ContextMenu.js'\nimport { EmptyListIndicator } from '../../components/EmptyListIndicator/EmptyListIndicator.js'\nimport { ListItem } from '../../components/ListItem/ListItem.js'\nimport { ListItemButton } from '../../components/ListItem/ListItemButton.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useListHeight } from '../../hooks/useListHeight.js'\nimport { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'\nimport type { Bookmark } from '../../stores/bookmarks/types.js'\nimport { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'\nimport { useBookmarks } from '../../stores/bookmarks/useBookmarks.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport { defaultChainIdsByType } from '../../utils/chainType.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { BookmarkAddressSheet } from './BookmarkAddressSheet.js'\nimport { BookmarkButtonContainer } from './SendToWalletPage.style.js'\n\nexport const BookmarksPage = (): JSX.Element => {\n const { t } = useTranslation()\n const bookmarkAddressSheetRef = useRef<BottomSheetBase>(null)\n const listParentRef = useRef<HTMLUListElement | null>(null)\n const buttonRef = useRef<HTMLDivElement | null>(null)\n const { bookmarks } = useBookmarks()\n const { requiredToChainType } = useToAddressRequirements()\n const { addBookmark, removeBookmark, setSelectedBookmark } =\n useBookmarkActions()\n const navigate = useNavigate()\n const { setFieldValue } = useFieldActions()\n const { getAddressLink } = useExplorer()\n\n useHeader(t('header.bookmarkedWallets'))\n\n const { listHeight } = useListHeight({\n listParentRef,\n headerRef: buttonRef,\n })\n\n const handleAddBookmark = () => {\n bookmarkAddressSheetRef.current?.open()\n }\n\n const handleBookmarkSelected = (bookmark: Bookmark) => {\n setFieldValue('toAddress', bookmark.address, {\n isTouched: true,\n isDirty: true,\n })\n setSelectedBookmark(bookmark)\n navigate({ to: navigationRoutes.home, replace: true })\n }\n\n return (\n <PageContainer disableGutters>\n <List\n className=\"long-list\"\n ref={listParentRef}\n style={{ height: listHeight, overflow: 'auto' }}\n disablePadding\n sx={{ paddingTop: 1.5 }}\n >\n {bookmarks.map((bookmark) => (\n <ListItem key={bookmark.address} sx={{ position: 'relative' }}>\n <ListItemButton\n onClick={() => handleBookmarkSelected(bookmark)}\n disabled={\n requiredToChainType &&\n requiredToChainType !== bookmark.chainType\n }\n >\n <ListItemAvatar>\n <AccountAvatar\n chainId={defaultChainIdsByType[bookmark.chainType]}\n />\n </ListItemAvatar>\n <ListItemText\n primary={bookmark.name}\n secondary={shortenAddress(bookmark.address)}\n />\n </ListItemButton>\n <ContextMenu\n disabled={\n !!(\n requiredToChainType &&\n requiredToChainType !== bookmark.chainType\n )\n }\n items={[\n {\n icon: <ContentCopyRounded />,\n label: t('button.copyAddress'),\n onClick: () =>\n navigator.clipboard.writeText(bookmark.address),\n },\n {\n icon: <OpenInNewRounded />,\n label: t('button.viewOnExplorer'),\n onClick: () =>\n
|
|
1
|
+
{"version":3,"file":"BookmarksPage.js","names":["ListItem","ListItemButton"],"sources":["../../../../src/pages/SendToWallet/BookmarksPage.tsx"],"sourcesContent":["import ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport DeleteOutline from '@mui/icons-material/DeleteOutlined'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport TurnedIn from '@mui/icons-material/TurnedIn'\nimport { Button, List, ListItemAvatar, ListItemText } from '@mui/material'\nimport { useNavigate } from '@tanstack/react-router'\nimport { type JSX, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { AccountAvatar } from '../../components/Avatar/AccountAvatar.js'\nimport type { BottomSheetBase } from '../../components/BottomSheet/types.js'\nimport { ContextMenu } from '../../components/ContextMenu.js'\nimport { EmptyListIndicator } from '../../components/EmptyListIndicator/EmptyListIndicator.js'\nimport { ListItem } from '../../components/ListItem/ListItem.js'\nimport { ListItemButton } from '../../components/ListItem/ListItemButton.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useListHeight } from '../../hooks/useListHeight.js'\nimport { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'\nimport type { Bookmark } from '../../stores/bookmarks/types.js'\nimport { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'\nimport { useBookmarks } from '../../stores/bookmarks/useBookmarks.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport { defaultChainIdsByType } from '../../utils/chainType.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { openInNewTab } from '../../utils/url.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { BookmarkAddressSheet } from './BookmarkAddressSheet.js'\nimport { BookmarkButtonContainer } from './SendToWalletPage.style.js'\n\nexport const BookmarksPage = (): JSX.Element => {\n const { t } = useTranslation()\n const bookmarkAddressSheetRef = useRef<BottomSheetBase>(null)\n const listParentRef = useRef<HTMLUListElement | null>(null)\n const buttonRef = useRef<HTMLDivElement | null>(null)\n const { bookmarks } = useBookmarks()\n const { requiredToChainType } = useToAddressRequirements()\n const { addBookmark, removeBookmark, setSelectedBookmark } =\n useBookmarkActions()\n const navigate = useNavigate()\n const { setFieldValue } = useFieldActions()\n const { getAddressLink } = useExplorer()\n\n useHeader(t('header.bookmarkedWallets'))\n\n const { listHeight } = useListHeight({\n listParentRef,\n headerRef: buttonRef,\n })\n\n const handleAddBookmark = () => {\n bookmarkAddressSheetRef.current?.open()\n }\n\n const handleBookmarkSelected = (bookmark: Bookmark) => {\n setFieldValue('toAddress', bookmark.address, {\n isTouched: true,\n isDirty: true,\n })\n setSelectedBookmark(bookmark)\n navigate({ to: navigationRoutes.home, replace: true })\n }\n\n return (\n <PageContainer disableGutters>\n <List\n className=\"long-list\"\n ref={listParentRef}\n style={{ height: listHeight, overflow: 'auto' }}\n disablePadding\n sx={{ paddingTop: 1.5 }}\n >\n {bookmarks.map((bookmark) => (\n <ListItem key={bookmark.address} sx={{ position: 'relative' }}>\n <ListItemButton\n onClick={() => handleBookmarkSelected(bookmark)}\n disabled={\n requiredToChainType &&\n requiredToChainType !== bookmark.chainType\n }\n >\n <ListItemAvatar>\n <AccountAvatar\n chainId={defaultChainIdsByType[bookmark.chainType]}\n />\n </ListItemAvatar>\n <ListItemText\n primary={bookmark.name}\n secondary={shortenAddress(bookmark.address)}\n />\n </ListItemButton>\n <ContextMenu\n disabled={\n !!(\n requiredToChainType &&\n requiredToChainType !== bookmark.chainType\n )\n }\n items={[\n {\n icon: <ContentCopyRounded />,\n label: t('button.copyAddress'),\n onClick: () =>\n navigator.clipboard.writeText(bookmark.address),\n },\n {\n icon: <OpenInNewRounded />,\n label: t('button.viewOnExplorer'),\n onClick: () =>\n openInNewTab(\n getAddressLink(\n bookmark.address,\n defaultChainIdsByType[bookmark.chainType]\n )\n ),\n },\n {\n icon: <DeleteOutline />,\n label: t('button.delete'),\n onClick: () => removeBookmark(bookmark.address),\n },\n ]}\n />\n </ListItem>\n ))}\n {!bookmarks.length && (\n <EmptyListIndicator\n icon={<TurnedIn />}\n title={t('sendToWallet.noBookmarkedWallets')}\n message={t('sendToWallet.noBookmarkedWalletsMessage')}\n />\n )}\n </List>\n <BookmarkButtonContainer ref={buttonRef}>\n <Button variant=\"contained\" onClick={handleAddBookmark}>\n {t('sendToWallet.addBookmark')}\n </Button>\n </BookmarkButtonContainer>\n <BookmarkAddressSheet\n ref={bookmarkAddressSheetRef}\n onAddBookmark={addBookmark}\n />\n </PageContainer>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,sBAAmC;CAC9C,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,0BAA0B,OAAwB,IAAI;CAC5D,MAAM,gBAAgB,OAAgC,IAAI;CAC1D,MAAM,YAAY,OAA8B,IAAI;CACpD,MAAM,EAAE,cAAc,aAAa;CACnC,MAAM,EAAE,wBAAwB,yBAAyB;CACzD,MAAM,EAAE,aAAa,gBAAgB,wBACnC,mBAAmB;CACrB,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,kBAAkB,gBAAgB;CAC1C,MAAM,EAAE,mBAAmB,YAAY;CAEvC,UAAU,EAAE,0BAA0B,CAAC;CAEvC,MAAM,EAAE,eAAe,cAAc;EACnC;EACA,WAAW;CACb,CAAC;CAED,MAAM,0BAA0B;EAC9B,wBAAwB,SAAS,KAAK;CACxC;CAEA,MAAM,0BAA0B,aAAuB;EACrD,cAAc,aAAa,SAAS,SAAS;GAC3C,WAAW;GACX,SAAS;EACX,CAAC;EACD,oBAAoB,QAAQ;EAC5B,SAAS;GAAE,IAAI,iBAAiB;GAAM,SAAS;EAAK,CAAC;CACvD;CAEA,OACE,qBAAC,eAAD;EAAe,gBAAA;EAAf,UAAA;GACE,qBAAC,MAAD;IACE,WAAU;IACV,KAAK;IACL,OAAO;KAAE,QAAQ;KAAY,UAAU;IAAO;IAC9C,gBAAA;IACA,IAAI,EAAE,YAAY,IAAI;IALxB,UAAA,CAOG,UAAU,KAAK,aACd,qBAACA,YAAD;KAAiC,IAAI,EAAE,UAAU,WAAW;KAA5D,UAAA,CACE,qBAACC,kBAAD;MACE,eAAe,uBAAuB,QAAQ;MAC9C,UACE,uBACA,wBAAwB,SAAS;MAJrC,UAAA,CAOE,oBAAC,gBAAD,EAAA,UACE,oBAAC,eAAD,EACE,SAAS,sBAAsB,SAAS,WACzC,CAAA,EACa,CAAA,GAChB,oBAAC,cAAD;OACE,SAAS,SAAS;OAClB,WAAW,eAAe,SAAS,OAAO;MAC3C,CAAA,CACa;KAChB,CAAA,GAAA,oBAAC,aAAD;MACE,UACE,CAAC,EACC,uBACA,wBAAwB,SAAS;MAGrC,OAAO;OACL;QACE,MAAM,oBAAC,oBAAD,CAAqB,CAAA;QAC3B,OAAO,EAAE,oBAAoB;QAC7B,eACE,UAAU,UAAU,UAAU,SAAS,OAAO;OAClD;OACA;QACE,MAAM,oBAAC,kBAAD,CAAmB,CAAA;QACzB,OAAO,EAAE,uBAAuB;QAChC,eACE,aACE,eACE,SAAS,SACT,sBAAsB,SAAS,UACjC,CACF;OACJ;OACA;QACE,MAAM,oBAAC,eAAD,CAAgB,CAAA;QACtB,OAAO,EAAE,eAAe;QACxB,eAAe,eAAe,SAAS,OAAO;OAChD;MACF;KACD,CAAA,CACO;IAlDK,GAAA,SAAS,OAkDd,CACX,GACA,CAAC,UAAU,UACV,oBAAC,oBAAD;KACE,MAAM,oBAAC,UAAD,CAAW,CAAA;KACjB,OAAO,EAAE,kCAAkC;KAC3C,SAAS,EAAE,yCAAyC;IACrD,CAAA,CAEC;;GACN,oBAAC,yBAAD;IAAyB,KAAK;IAC5B,UAAA,oBAAC,QAAD;KAAQ,SAAQ;KAAY,SAAS;KAClC,UAAA,EAAE,0BAA0B;IACvB,CAAA;GACe,CAAA;GACzB,oBAAC,sBAAD;IACE,KAAK;IACL,eAAe;GAChB,CAAA;EACY;;AAEnB"}
|
|
@@ -4,6 +4,7 @@ import { useFieldActions } from "../../stores/form/useFieldActions.js";
|
|
|
4
4
|
import { PageContainer } from "../../components/PageContainer.js";
|
|
5
5
|
import { useToAddressRequirements } from "../../hooks/useToAddressRequirements.js";
|
|
6
6
|
import { shortenAddress } from "../../utils/wallet.js";
|
|
7
|
+
import { openInNewTab } from "../../utils/url.js";
|
|
7
8
|
import { useExplorer } from "../../hooks/useExplorer.js";
|
|
8
9
|
import { useHeader } from "../../hooks/useHeader.js";
|
|
9
10
|
import { useListHeight } from "../../hooks/useListHeight.js";
|
|
@@ -72,7 +73,7 @@ const ConnectedWalletsPage = () => {
|
|
|
72
73
|
icon: /* @__PURE__ */ jsx(OpenInNewRounded, {}),
|
|
73
74
|
label: t("button.viewOnExplorer"),
|
|
74
75
|
onClick: () => {
|
|
75
|
-
if (account.address)
|
|
76
|
+
if (account.address) openInNewTab(getAddressLink(account.address, account.chainId));
|
|
76
77
|
}
|
|
77
78
|
}] : []];
|
|
78
79
|
return /* @__PURE__ */ jsxs(ListItem$1, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConnectedWalletsPage.js","names":["ListItem","ListItemButton"],"sources":["../../../../src/pages/SendToWallet/ConnectedWalletsPage.tsx"],"sourcesContent":["import { useAccount } from '@lifi/wallet-management'\nimport type { Account } from '@lifi/widget-provider'\nimport ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport Wallet from '@mui/icons-material/Wallet'\nimport { List, ListItemAvatar, ListItemText } from '@mui/material'\nimport { useNavigate } from '@tanstack/react-router'\nimport { type JSX, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { AccountAvatar } from '../../components/Avatar/AccountAvatar.js'\nimport { ContextMenu } from '../../components/ContextMenu.js'\nimport { EmptyListIndicator } from '../../components/EmptyListIndicator/EmptyListIndicator.js'\nimport { ListItem } from '../../components/ListItem/ListItem.js'\nimport { ListItemButton } from '../../components/ListItem/ListItemButton.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useListHeight } from '../../hooks/useListHeight.js'\nimport { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'\nimport { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { shortenAddress } from '../../utils/wallet.js'\n\nexport const ConnectedWalletsPage = (): JSX.Element => {\n const { t } = useTranslation()\n const { accounts } = useAccount()\n const { setSelectedBookmark } = useBookmarkActions()\n const { requiredToChainType } = useToAddressRequirements()\n const navigate = useNavigate()\n const { setFieldValue } = useFieldActions()\n const { getAddressLink } = useExplorer()\n const listParentRef = useRef<HTMLUListElement | null>(null)\n\n useHeader(t('sendToWallet.connectedWallets'))\n\n const { listHeight } = useListHeight({ listParentRef })\n\n const handleWalletSelected = (account: Account) => {\n setFieldValue('toAddress', account.address!, {\n isTouched: true,\n isDirty: true,\n })\n setSelectedBookmark({\n name: account.connector?.name,\n address: account.address!,\n chainType: account.chainType!,\n isConnectedAccount: true,\n })\n navigate({\n to: navigationRoutes.home,\n replace: true,\n })\n }\n\n return (\n <PageContainer disableGutters>\n <List\n className=\"long-list\"\n ref={listParentRef}\n style={{ height: listHeight, overflow: 'auto' }}\n disablePadding\n sx={{ paddingTop: 1.5 }}\n >\n {accounts.map((account) => {\n const walletAddress = shortenAddress(account.address)\n const menuItems = [\n {\n icon: <ContentCopyRounded />,\n label: t('button.copyAddress'),\n onClick: () => {\n if (account.address) {\n navigator.clipboard.writeText(account.address)\n }\n },\n },\n ...(account.chainId\n ? [\n {\n icon: <OpenInNewRounded />,\n label: t('button.viewOnExplorer'),\n onClick: () => {\n if (account.address) {\n
|
|
1
|
+
{"version":3,"file":"ConnectedWalletsPage.js","names":["ListItem","ListItemButton"],"sources":["../../../../src/pages/SendToWallet/ConnectedWalletsPage.tsx"],"sourcesContent":["import { useAccount } from '@lifi/wallet-management'\nimport type { Account } from '@lifi/widget-provider'\nimport ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport Wallet from '@mui/icons-material/Wallet'\nimport { List, ListItemAvatar, ListItemText } from '@mui/material'\nimport { useNavigate } from '@tanstack/react-router'\nimport { type JSX, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { AccountAvatar } from '../../components/Avatar/AccountAvatar.js'\nimport { ContextMenu } from '../../components/ContextMenu.js'\nimport { EmptyListIndicator } from '../../components/EmptyListIndicator/EmptyListIndicator.js'\nimport { ListItem } from '../../components/ListItem/ListItem.js'\nimport { ListItemButton } from '../../components/ListItem/ListItemButton.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useListHeight } from '../../hooks/useListHeight.js'\nimport { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'\nimport { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { openInNewTab } from '../../utils/url.js'\nimport { shortenAddress } from '../../utils/wallet.js'\n\nexport const ConnectedWalletsPage = (): JSX.Element => {\n const { t } = useTranslation()\n const { accounts } = useAccount()\n const { setSelectedBookmark } = useBookmarkActions()\n const { requiredToChainType } = useToAddressRequirements()\n const navigate = useNavigate()\n const { setFieldValue } = useFieldActions()\n const { getAddressLink } = useExplorer()\n const listParentRef = useRef<HTMLUListElement | null>(null)\n\n useHeader(t('sendToWallet.connectedWallets'))\n\n const { listHeight } = useListHeight({ listParentRef })\n\n const handleWalletSelected = (account: Account) => {\n setFieldValue('toAddress', account.address!, {\n isTouched: true,\n isDirty: true,\n })\n setSelectedBookmark({\n name: account.connector?.name,\n address: account.address!,\n chainType: account.chainType!,\n isConnectedAccount: true,\n })\n navigate({\n to: navigationRoutes.home,\n replace: true,\n })\n }\n\n return (\n <PageContainer disableGutters>\n <List\n className=\"long-list\"\n ref={listParentRef}\n style={{ height: listHeight, overflow: 'auto' }}\n disablePadding\n sx={{ paddingTop: 1.5 }}\n >\n {accounts.map((account) => {\n const walletAddress = shortenAddress(account.address)\n const menuItems = [\n {\n icon: <ContentCopyRounded />,\n label: t('button.copyAddress'),\n onClick: () => {\n if (account.address) {\n navigator.clipboard.writeText(account.address)\n }\n },\n },\n ...(account.chainId\n ? [\n {\n icon: <OpenInNewRounded />,\n label: t('button.viewOnExplorer'),\n onClick: () => {\n if (account.address) {\n openInNewTab(\n getAddressLink(account.address, account.chainId)\n )\n }\n },\n },\n ]\n : []),\n ]\n\n return (\n <ListItem key={account.address} sx={{ position: 'relative' }}>\n <ListItemButton\n onClick={() => handleWalletSelected(account)}\n disabled={\n requiredToChainType &&\n requiredToChainType !== account.chainType\n }\n >\n <ListItemAvatar>\n <AccountAvatar chainId={account.chainId} account={account} />\n </ListItemAvatar>\n <ListItemText\n primary={account.connector?.name}\n secondary={walletAddress}\n />\n </ListItemButton>\n <ContextMenu\n items={menuItems}\n disabled={\n !!(\n requiredToChainType &&\n requiredToChainType !== account.chainType\n )\n }\n />\n </ListItem>\n )\n })}\n {!accounts.length && (\n <EmptyListIndicator\n icon={<Wallet />}\n title={t('sendToWallet.noConnectedWallets')}\n message={t('sendToWallet.noConnectedWalletsMessage')}\n />\n )}\n </List>\n </PageContainer>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,6BAA0C;CACrD,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,EAAE,aAAa,WAAW;CAChC,MAAM,EAAE,wBAAwB,mBAAmB;CACnD,MAAM,EAAE,wBAAwB,yBAAyB;CACzD,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,kBAAkB,gBAAgB;CAC1C,MAAM,EAAE,mBAAmB,YAAY;CACvC,MAAM,gBAAgB,OAAgC,IAAI;CAE1D,UAAU,EAAE,+BAA+B,CAAC;CAE5C,MAAM,EAAE,eAAe,cAAc,EAAE,cAAc,CAAC;CAEtD,MAAM,wBAAwB,YAAqB;EACjD,cAAc,aAAa,QAAQ,SAAU;GAC3C,WAAW;GACX,SAAS;EACX,CAAC;EACD,oBAAoB;GAClB,MAAM,QAAQ,WAAW;GACzB,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB,oBAAoB;EACtB,CAAC;EACD,SAAS;GACP,IAAI,iBAAiB;GACrB,SAAS;EACX,CAAC;CACH;CAEA,OACE,oBAAC,eAAD;EAAe,gBAAA;EACb,UAAA,qBAAC,MAAD;GACE,WAAU;GACV,KAAK;GACL,OAAO;IAAE,QAAQ;IAAY,UAAU;GAAO;GAC9C,gBAAA;GACA,IAAI,EAAE,YAAY,IAAI;GALxB,UAAA,CAOG,SAAS,KAAK,YAAY;IACzB,MAAM,gBAAgB,eAAe,QAAQ,OAAO;IACpD,MAAM,YAAY,CAChB;KACE,MAAM,oBAAC,oBAAD,CAAqB,CAAA;KAC3B,OAAO,EAAE,oBAAoB;KAC7B,eAAe;MACb,IAAI,QAAQ,SACV,UAAU,UAAU,UAAU,QAAQ,OAAO;KAEjD;IACF,GACA,GAAI,QAAQ,UACR,CACE;KACE,MAAM,oBAAC,kBAAD,CAAmB,CAAA;KACzB,OAAO,EAAE,uBAAuB;KAChC,eAAe;MACb,IAAI,QAAQ,SACV,aACE,eAAe,QAAQ,SAAS,QAAQ,OAAO,CACjD;KAEJ;IACF,CACF,IACA,CAAC,CACP;IAEA,OACE,qBAACA,YAAD;KAAgC,IAAI,EAAE,UAAU,WAAW;KAA3D,UAAA,CACE,qBAACC,kBAAD;MACE,eAAe,qBAAqB,OAAO;MAC3C,UACE,uBACA,wBAAwB,QAAQ;MAJpC,UAAA,CAOE,oBAAC,gBAAD,EAAA,UACE,oBAAC,eAAD;OAAe,SAAS,QAAQ;OAAkB;MAAU,CAAA,EAC9C,CAAA,GAChB,oBAAC,cAAD;OACE,SAAS,QAAQ,WAAW;OAC5B,WAAW;MACZ,CAAA,CACa;KAChB,CAAA,GAAA,oBAAC,aAAD;MACE,OAAO;MACP,UACE,CAAC,EACC,uBACA,wBAAwB,QAAQ;KAGrC,CAAA,CACO;IAzBK,GAAA,QAAQ,OAyBb;GAEd,CAAC,GACA,CAAC,SAAS,UACT,oBAAC,oBAAD;IACE,MAAM,oBAAC,QAAD,CAAS,CAAA;IACf,OAAO,EAAE,iCAAiC;IAC1C,SAAS,EAAE,wCAAwC;GACpD,CAAA,CAEC;;CACO,CAAA;AAEnB"}
|
|
@@ -6,6 +6,7 @@ import { PageContainer } from "../../components/PageContainer.js";
|
|
|
6
6
|
import { defaultChainIdsByType } from "../../utils/chainType.js";
|
|
7
7
|
import { useToAddressRequirements } from "../../hooks/useToAddressRequirements.js";
|
|
8
8
|
import { shortenAddress } from "../../utils/wallet.js";
|
|
9
|
+
import { openInNewTab } from "../../utils/url.js";
|
|
9
10
|
import { useExplorer } from "../../hooks/useExplorer.js";
|
|
10
11
|
import { useHeader } from "../../hooks/useHeader.js";
|
|
11
12
|
import { useListHeight } from "../../hooks/useListHeight.js";
|
|
@@ -89,7 +90,7 @@ const RecentWalletsPage = () => {
|
|
|
89
90
|
{
|
|
90
91
|
icon: /* @__PURE__ */ jsx(OpenInNewRounded, {}),
|
|
91
92
|
label: t("button.viewOnExplorer"),
|
|
92
|
-
onClick: () =>
|
|
93
|
+
onClick: () => openInNewTab(getAddressLink(recentWallet.address, defaultChainIdsByType[recentWallet.chainType]))
|
|
93
94
|
},
|
|
94
95
|
{
|
|
95
96
|
icon: /* @__PURE__ */ jsx(TurnedInNot, {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecentWalletsPage.js","names":["ListItem","ListItemButton"],"sources":["../../../../src/pages/SendToWallet/RecentWalletsPage.tsx"],"sourcesContent":["import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'\nimport ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport DeleteOutline from '@mui/icons-material/DeleteOutlined'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport TurnedInNot from '@mui/icons-material/TurnedInNot'\nimport { List, ListItemAvatar, ListItemText } from '@mui/material'\nimport { useNavigate } from '@tanstack/react-router'\nimport { type JSX, useRef, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { AccountAvatar } from '../../components/Avatar/AccountAvatar.js'\nimport type { BottomSheetBase } from '../../components/BottomSheet/types.js'\nimport { ContextMenu } from '../../components/ContextMenu.js'\nimport { EmptyListIndicator } from '../../components/EmptyListIndicator/EmptyListIndicator.js'\nimport { ListItem } from '../../components/ListItem/ListItem.js'\nimport { ListItemButton } from '../../components/ListItem/ListItemButton.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useListHeight } from '../../hooks/useListHeight.js'\nimport { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'\nimport type { Bookmark } from '../../stores/bookmarks/types.js'\nimport { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'\nimport { useBookmarks } from '../../stores/bookmarks/useBookmarks.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport { defaultChainIdsByType } from '../../utils/chainType.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { BookmarkAddressSheet } from './BookmarkAddressSheet.js'\n\nexport const RecentWalletsPage = (): JSX.Element => {\n const { t } = useTranslation()\n const navigate = useNavigate()\n const [bookmarkTarget, setBookmarkTarget] = useState<Bookmark>()\n const bookmarkAddressSheetRef = useRef<BottomSheetBase>(null)\n const listParentRef = useRef<HTMLUListElement | null>(null)\n const { recentWallets } = useBookmarks()\n const { requiredToChainType } = useToAddressRequirements()\n const {\n removeRecentWallet,\n addBookmark,\n setSelectedBookmark,\n addRecentWallet,\n } = useBookmarkActions()\n const { setFieldValue } = useFieldActions()\n const { getAddressLink } = useExplorer()\n\n useHeader(t('header.recentWallets'))\n\n const { listHeight } = useListHeight({ listParentRef })\n\n const handleRecentSelected = (recentWallet: Bookmark) => {\n addRecentWallet(recentWallet)\n setFieldValue('toAddress', recentWallet.address, {\n isTouched: true,\n isDirty: true,\n })\n setSelectedBookmark(recentWallet)\n navigate({\n to: navigationRoutes.home,\n replace: true,\n })\n }\n\n const handleAddBookmark = (bookmark: Bookmark) => {\n addBookmark(bookmark)\n navigate({ to: navigationRoutes.bookmarks, replace: true })\n }\n\n return (\n <PageContainer disableGutters>\n <List\n className=\"long-list\"\n ref={listParentRef}\n style={{ height: listHeight, overflow: 'auto' }}\n disablePadding\n sx={{ paddingTop: 1.5 }}\n >\n {recentWallets.map((recentWallet) => (\n <ListItem key={recentWallet.address} sx={{ position: 'relative' }}>\n <ListItemButton\n disabled={\n requiredToChainType &&\n requiredToChainType !== recentWallet.chainType\n }\n onClick={() => handleRecentSelected(recentWallet)}\n >\n <ListItemAvatar>\n <AccountAvatar\n chainId={defaultChainIdsByType[recentWallet.chainType]}\n />\n </ListItemAvatar>\n <ListItemText\n primary={\n recentWallet.name || shortenAddress(recentWallet.address)\n }\n secondary={\n recentWallet.name\n ? shortenAddress(recentWallet.address)\n : undefined\n }\n />\n </ListItemButton>\n <ContextMenu\n disabled={\n !!(\n requiredToChainType &&\n requiredToChainType !== recentWallet.chainType\n )\n }\n items={[\n {\n icon: <ContentCopyRounded />,\n label: t('button.copyAddress'),\n onClick: () =>\n navigator.clipboard.writeText(recentWallet.address),\n },\n {\n icon: <OpenInNewRounded />,\n label: t('button.viewOnExplorer'),\n onClick: () =>\n
|
|
1
|
+
{"version":3,"file":"RecentWalletsPage.js","names":["ListItem","ListItemButton"],"sources":["../../../../src/pages/SendToWallet/RecentWalletsPage.tsx"],"sourcesContent":["import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'\nimport ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport DeleteOutline from '@mui/icons-material/DeleteOutlined'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport TurnedInNot from '@mui/icons-material/TurnedInNot'\nimport { List, ListItemAvatar, ListItemText } from '@mui/material'\nimport { useNavigate } from '@tanstack/react-router'\nimport { type JSX, useRef, useState } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { AccountAvatar } from '../../components/Avatar/AccountAvatar.js'\nimport type { BottomSheetBase } from '../../components/BottomSheet/types.js'\nimport { ContextMenu } from '../../components/ContextMenu.js'\nimport { EmptyListIndicator } from '../../components/EmptyListIndicator/EmptyListIndicator.js'\nimport { ListItem } from '../../components/ListItem/ListItem.js'\nimport { ListItemButton } from '../../components/ListItem/ListItemButton.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useListHeight } from '../../hooks/useListHeight.js'\nimport { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'\nimport type { Bookmark } from '../../stores/bookmarks/types.js'\nimport { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'\nimport { useBookmarks } from '../../stores/bookmarks/useBookmarks.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport { defaultChainIdsByType } from '../../utils/chainType.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { openInNewTab } from '../../utils/url.js'\nimport { shortenAddress } from '../../utils/wallet.js'\nimport { BookmarkAddressSheet } from './BookmarkAddressSheet.js'\n\nexport const RecentWalletsPage = (): JSX.Element => {\n const { t } = useTranslation()\n const navigate = useNavigate()\n const [bookmarkTarget, setBookmarkTarget] = useState<Bookmark>()\n const bookmarkAddressSheetRef = useRef<BottomSheetBase>(null)\n const listParentRef = useRef<HTMLUListElement | null>(null)\n const { recentWallets } = useBookmarks()\n const { requiredToChainType } = useToAddressRequirements()\n const {\n removeRecentWallet,\n addBookmark,\n setSelectedBookmark,\n addRecentWallet,\n } = useBookmarkActions()\n const { setFieldValue } = useFieldActions()\n const { getAddressLink } = useExplorer()\n\n useHeader(t('header.recentWallets'))\n\n const { listHeight } = useListHeight({ listParentRef })\n\n const handleRecentSelected = (recentWallet: Bookmark) => {\n addRecentWallet(recentWallet)\n setFieldValue('toAddress', recentWallet.address, {\n isTouched: true,\n isDirty: true,\n })\n setSelectedBookmark(recentWallet)\n navigate({\n to: navigationRoutes.home,\n replace: true,\n })\n }\n\n const handleAddBookmark = (bookmark: Bookmark) => {\n addBookmark(bookmark)\n navigate({ to: navigationRoutes.bookmarks, replace: true })\n }\n\n return (\n <PageContainer disableGutters>\n <List\n className=\"long-list\"\n ref={listParentRef}\n style={{ height: listHeight, overflow: 'auto' }}\n disablePadding\n sx={{ paddingTop: 1.5 }}\n >\n {recentWallets.map((recentWallet) => (\n <ListItem key={recentWallet.address} sx={{ position: 'relative' }}>\n <ListItemButton\n disabled={\n requiredToChainType &&\n requiredToChainType !== recentWallet.chainType\n }\n onClick={() => handleRecentSelected(recentWallet)}\n >\n <ListItemAvatar>\n <AccountAvatar\n chainId={defaultChainIdsByType[recentWallet.chainType]}\n />\n </ListItemAvatar>\n <ListItemText\n primary={\n recentWallet.name || shortenAddress(recentWallet.address)\n }\n secondary={\n recentWallet.name\n ? shortenAddress(recentWallet.address)\n : undefined\n }\n />\n </ListItemButton>\n <ContextMenu\n disabled={\n !!(\n requiredToChainType &&\n requiredToChainType !== recentWallet.chainType\n )\n }\n items={[\n {\n icon: <ContentCopyRounded />,\n label: t('button.copyAddress'),\n onClick: () =>\n navigator.clipboard.writeText(recentWallet.address),\n },\n {\n icon: <OpenInNewRounded />,\n label: t('button.viewOnExplorer'),\n onClick: () =>\n openInNewTab(\n getAddressLink(\n recentWallet.address,\n defaultChainIdsByType[recentWallet.chainType]\n )\n ),\n },\n {\n icon: <TurnedInNot />,\n label: t('button.bookmark'),\n onClick: () => {\n setBookmarkTarget(recentWallet)\n bookmarkAddressSheetRef.current?.open()\n },\n },\n {\n icon: <DeleteOutline />,\n label: t('button.delete'),\n onClick: () => removeRecentWallet(recentWallet.address),\n },\n ]}\n />\n </ListItem>\n ))}\n {!recentWallets.length && (\n <EmptyListIndicator\n icon={<AccountBalanceWalletIcon />}\n title={t('sendToWallet.noRecentWallets')}\n message={t('sendToWallet.noRecentWalletsMessage')}\n />\n )}\n </List>\n <BookmarkAddressSheet\n ref={bookmarkAddressSheetRef}\n validatedWallet={bookmarkTarget}\n onAddBookmark={handleAddBookmark}\n />\n </PageContainer>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,0BAAuC;CAClD,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,CAAC,gBAAgB,qBAAqB,SAAmB;CAC/D,MAAM,0BAA0B,OAAwB,IAAI;CAC5D,MAAM,gBAAgB,OAAgC,IAAI;CAC1D,MAAM,EAAE,kBAAkB,aAAa;CACvC,MAAM,EAAE,wBAAwB,yBAAyB;CACzD,MAAM,EACJ,oBACA,aACA,qBACA,oBACE,mBAAmB;CACvB,MAAM,EAAE,kBAAkB,gBAAgB;CAC1C,MAAM,EAAE,mBAAmB,YAAY;CAEvC,UAAU,EAAE,sBAAsB,CAAC;CAEnC,MAAM,EAAE,eAAe,cAAc,EAAE,cAAc,CAAC;CAEtD,MAAM,wBAAwB,iBAA2B;EACvD,gBAAgB,YAAY;EAC5B,cAAc,aAAa,aAAa,SAAS;GAC/C,WAAW;GACX,SAAS;EACX,CAAC;EACD,oBAAoB,YAAY;EAChC,SAAS;GACP,IAAI,iBAAiB;GACrB,SAAS;EACX,CAAC;CACH;CAEA,MAAM,qBAAqB,aAAuB;EAChD,YAAY,QAAQ;EACpB,SAAS;GAAE,IAAI,iBAAiB;GAAW,SAAS;EAAK,CAAC;CAC5D;CAEA,OACE,qBAAC,eAAD;EAAe,gBAAA;EAAf,UAAA,CACE,qBAAC,MAAD;GACE,WAAU;GACV,KAAK;GACL,OAAO;IAAE,QAAQ;IAAY,UAAU;GAAO;GAC9C,gBAAA;GACA,IAAI,EAAE,YAAY,IAAI;GALxB,UAAA,CAOG,cAAc,KAAK,iBAClB,qBAACA,YAAD;IAAqC,IAAI,EAAE,UAAU,WAAW;IAAhE,UAAA,CACE,qBAACC,kBAAD;KACE,UACE,uBACA,wBAAwB,aAAa;KAEvC,eAAe,qBAAqB,YAAY;KALlD,UAAA,CAOE,oBAAC,gBAAD,EAAA,UACE,oBAAC,eAAD,EACE,SAAS,sBAAsB,aAAa,WAC7C,CAAA,EACa,CAAA,GAChB,oBAAC,cAAD;MACE,SACE,aAAa,QAAQ,eAAe,aAAa,OAAO;MAE1D,WACE,aAAa,OACT,eAAe,aAAa,OAAO,IACnC,KAAA;KAEP,CAAA,CACa;IAChB,CAAA,GAAA,oBAAC,aAAD;KACE,UACE,CAAC,EACC,uBACA,wBAAwB,aAAa;KAGzC,OAAO;MACL;OACE,MAAM,oBAAC,oBAAD,CAAqB,CAAA;OAC3B,OAAO,EAAE,oBAAoB;OAC7B,eACE,UAAU,UAAU,UAAU,aAAa,OAAO;MACtD;MACA;OACE,MAAM,oBAAC,kBAAD,CAAmB,CAAA;OACzB,OAAO,EAAE,uBAAuB;OAChC,eACE,aACE,eACE,aAAa,SACb,sBAAsB,aAAa,UACrC,CACF;MACJ;MACA;OACE,MAAM,oBAAC,aAAD,CAAc,CAAA;OACpB,OAAO,EAAE,iBAAiB;OAC1B,eAAe;QACb,kBAAkB,YAAY;QAC9B,wBAAwB,SAAS,KAAK;OACxC;MACF;MACA;OACE,MAAM,oBAAC,eAAD,CAAgB,CAAA;OACtB,OAAO,EAAE,eAAe;OACxB,eAAe,mBAAmB,aAAa,OAAO;MACxD;KACF;IACD,CAAA,CACO;GAhEK,GAAA,aAAa,OAgElB,CACX,GACA,CAAC,cAAc,UACd,oBAAC,oBAAD;IACE,MAAM,oBAAC,0BAAD,CAA2B,CAAA;IACjC,OAAO,EAAE,8BAA8B;IACvC,SAAS,EAAE,qCAAqC;GACjD,CAAA,CAEC;EACN,CAAA,GAAA,oBAAC,sBAAD;GACE,KAAK;GACL,iBAAiB;GACjB,eAAe;EAChB,CAAA,CACY;;AAEnB"}
|
|
@@ -6,6 +6,7 @@ import { defaultChainIdsByType } from "../../utils/chainType.js";
|
|
|
6
6
|
import { useToAddressRequirements } from "../../hooks/useToAddressRequirements.js";
|
|
7
7
|
import { useNavigateBack } from "../../hooks/useNavigateBack.js";
|
|
8
8
|
import { shortenAddress } from "../../utils/wallet.js";
|
|
9
|
+
import { openInNewTab } from "../../utils/url.js";
|
|
9
10
|
import { useExplorer } from "../../hooks/useExplorer.js";
|
|
10
11
|
import { useHeader } from "../../hooks/useHeader.js";
|
|
11
12
|
import { useListHeight } from "../../hooks/useListHeight.js";
|
|
@@ -71,7 +72,7 @@ const SendToConfiguredWalletPage = () => {
|
|
|
71
72
|
}, {
|
|
72
73
|
icon: /* @__PURE__ */ jsx(OpenInNewRounded, {}),
|
|
73
74
|
label: t("button.viewOnExplorer"),
|
|
74
|
-
onClick: () =>
|
|
75
|
+
onClick: () => openInNewTab(getAddressLink(toAddress.address, defaultChainIdsByType[toAddress.chainType]))
|
|
75
76
|
}]
|
|
76
77
|
})]
|
|
77
78
|
}, toAddress.address))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SendToConfiguredWalletPage.js","names":["ListItem","ListItemButton"],"sources":["../../../../src/pages/SendToWallet/SendToConfiguredWalletPage.tsx"],"sourcesContent":["import ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport { List, ListItemAvatar, ListItemText } from '@mui/material'\nimport { type JSX, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { AccountAvatar } from '../../components/Avatar/AccountAvatar.js'\nimport { ContextMenu } from '../../components/ContextMenu.js'\nimport { ListItem } from '../../components/ListItem/ListItem.js'\nimport { ListItemButton } from '../../components/ListItem/ListItemButton.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useListHeight } from '../../hooks/useListHeight.js'\nimport { useNavigateBack } from '../../hooks/useNavigateBack.js'\nimport { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport type { ToAddress } from '../../types/widget.js'\nimport { defaultChainIdsByType } from '../../utils/chainType.js'\nimport { shortenAddress } from '../../utils/wallet.js'\n\nexport const SendToConfiguredWalletPage = (): JSX.Element => {\n const { t } = useTranslation()\n const navigateBack = useNavigateBack()\n const { toAddresses } = useWidgetConfig()\n const { requiredToChainType } = useToAddressRequirements()\n const { setSelectedBookmark } = useBookmarkActions()\n const { setFieldValue } = useFieldActions()\n const { getAddressLink } = useExplorer()\n const listParentRef = useRef<HTMLUListElement | null>(null)\n\n useHeader(t('header.sendToWallet'))\n\n const { listHeight } = useListHeight({ listParentRef })\n\n const handleCuratedSelected = (toAddress: ToAddress) => {\n setSelectedBookmark(toAddress)\n setFieldValue('toAddress', toAddress.address, {\n isTouched: true,\n isDirty: true,\n })\n navigateBack()\n }\n\n return (\n <PageContainer disableGutters>\n <List\n className=\"long-list\"\n ref={listParentRef}\n style={{ height: listHeight, overflow: 'auto' }}\n disablePadding\n sx={{ paddingTop: 1.5 }}\n >\n {toAddresses?.map((toAddress) => (\n <ListItem key={toAddress.address} sx={{ position: 'relative' }}>\n <ListItemButton\n disabled={\n requiredToChainType &&\n requiredToChainType !== toAddress.chainType\n }\n onClick={() => handleCuratedSelected(toAddress)}\n >\n <ListItemAvatar>\n <AccountAvatar\n chainId={defaultChainIdsByType[toAddress.chainType]}\n toAddress={toAddress}\n />\n </ListItemAvatar>\n <ListItemText\n primary={toAddress.name || shortenAddress(toAddress.address)}\n secondary={\n toAddress.name ? shortenAddress(toAddress.address) : undefined\n }\n />\n </ListItemButton>\n <ContextMenu\n disabled={\n !!(\n requiredToChainType &&\n requiredToChainType !== toAddress.chainType\n )\n }\n items={[\n {\n icon: <ContentCopyRounded />,\n label: t('button.copyAddress'),\n onClick: () =>\n navigator.clipboard.writeText(toAddress.address),\n },\n {\n icon: <OpenInNewRounded />,\n label: t('button.viewOnExplorer'),\n onClick: () =>\n
|
|
1
|
+
{"version":3,"file":"SendToConfiguredWalletPage.js","names":["ListItem","ListItemButton"],"sources":["../../../../src/pages/SendToWallet/SendToConfiguredWalletPage.tsx"],"sourcesContent":["import ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport OpenInNewRounded from '@mui/icons-material/OpenInNewRounded'\nimport { List, ListItemAvatar, ListItemText } from '@mui/material'\nimport { type JSX, useRef } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { AccountAvatar } from '../../components/Avatar/AccountAvatar.js'\nimport { ContextMenu } from '../../components/ContextMenu.js'\nimport { ListItem } from '../../components/ListItem/ListItem.js'\nimport { ListItemButton } from '../../components/ListItem/ListItemButton.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useListHeight } from '../../hooks/useListHeight.js'\nimport { useNavigateBack } from '../../hooks/useNavigateBack.js'\nimport { useToAddressRequirements } from '../../hooks/useToAddressRequirements.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'\nimport { useFieldActions } from '../../stores/form/useFieldActions.js'\nimport type { ToAddress } from '../../types/widget.js'\nimport { defaultChainIdsByType } from '../../utils/chainType.js'\nimport { openInNewTab } from '../../utils/url.js'\nimport { shortenAddress } from '../../utils/wallet.js'\n\nexport const SendToConfiguredWalletPage = (): JSX.Element => {\n const { t } = useTranslation()\n const navigateBack = useNavigateBack()\n const { toAddresses } = useWidgetConfig()\n const { requiredToChainType } = useToAddressRequirements()\n const { setSelectedBookmark } = useBookmarkActions()\n const { setFieldValue } = useFieldActions()\n const { getAddressLink } = useExplorer()\n const listParentRef = useRef<HTMLUListElement | null>(null)\n\n useHeader(t('header.sendToWallet'))\n\n const { listHeight } = useListHeight({ listParentRef })\n\n const handleCuratedSelected = (toAddress: ToAddress) => {\n setSelectedBookmark(toAddress)\n setFieldValue('toAddress', toAddress.address, {\n isTouched: true,\n isDirty: true,\n })\n navigateBack()\n }\n\n return (\n <PageContainer disableGutters>\n <List\n className=\"long-list\"\n ref={listParentRef}\n style={{ height: listHeight, overflow: 'auto' }}\n disablePadding\n sx={{ paddingTop: 1.5 }}\n >\n {toAddresses?.map((toAddress) => (\n <ListItem key={toAddress.address} sx={{ position: 'relative' }}>\n <ListItemButton\n disabled={\n requiredToChainType &&\n requiredToChainType !== toAddress.chainType\n }\n onClick={() => handleCuratedSelected(toAddress)}\n >\n <ListItemAvatar>\n <AccountAvatar\n chainId={defaultChainIdsByType[toAddress.chainType]}\n toAddress={toAddress}\n />\n </ListItemAvatar>\n <ListItemText\n primary={toAddress.name || shortenAddress(toAddress.address)}\n secondary={\n toAddress.name ? shortenAddress(toAddress.address) : undefined\n }\n />\n </ListItemButton>\n <ContextMenu\n disabled={\n !!(\n requiredToChainType &&\n requiredToChainType !== toAddress.chainType\n )\n }\n items={[\n {\n icon: <ContentCopyRounded />,\n label: t('button.copyAddress'),\n onClick: () =>\n navigator.clipboard.writeText(toAddress.address),\n },\n {\n icon: <OpenInNewRounded />,\n label: t('button.viewOnExplorer'),\n onClick: () =>\n openInNewTab(\n getAddressLink(\n toAddress.address,\n defaultChainIdsByType[toAddress.chainType]\n )\n ),\n },\n ]}\n />\n </ListItem>\n ))}\n </List>\n </PageContainer>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,mCAAgD;CAC3D,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,eAAe,gBAAgB;CACrC,MAAM,EAAE,gBAAgB,gBAAgB;CACxC,MAAM,EAAE,wBAAwB,yBAAyB;CACzD,MAAM,EAAE,wBAAwB,mBAAmB;CACnD,MAAM,EAAE,kBAAkB,gBAAgB;CAC1C,MAAM,EAAE,mBAAmB,YAAY;CACvC,MAAM,gBAAgB,OAAgC,IAAI;CAE1D,UAAU,EAAE,qBAAqB,CAAC;CAElC,MAAM,EAAE,eAAe,cAAc,EAAE,cAAc,CAAC;CAEtD,MAAM,yBAAyB,cAAyB;EACtD,oBAAoB,SAAS;EAC7B,cAAc,aAAa,UAAU,SAAS;GAC5C,WAAW;GACX,SAAS;EACX,CAAC;EACD,aAAa;CACf;CAEA,OACE,oBAAC,eAAD;EAAe,gBAAA;EACb,UAAA,oBAAC,MAAD;GACE,WAAU;GACV,KAAK;GACL,OAAO;IAAE,QAAQ;IAAY,UAAU;GAAO;GAC9C,gBAAA;GACA,IAAI,EAAE,YAAY,IAAI;GAErB,UAAA,aAAa,KAAK,cACjB,qBAACA,YAAD;IAAkC,IAAI,EAAE,UAAU,WAAW;IAA7D,UAAA,CACE,qBAACC,kBAAD;KACE,UACE,uBACA,wBAAwB,UAAU;KAEpC,eAAe,sBAAsB,SAAS;KALhD,UAAA,CAOE,oBAAC,gBAAD,EAAA,UACE,oBAAC,eAAD;MACE,SAAS,sBAAsB,UAAU;MAC9B;KACZ,CAAA,EACa,CAAA,GAChB,oBAAC,cAAD;MACE,SAAS,UAAU,QAAQ,eAAe,UAAU,OAAO;MAC3D,WACE,UAAU,OAAO,eAAe,UAAU,OAAO,IAAI,KAAA;KAExD,CAAA,CACa;IAChB,CAAA,GAAA,oBAAC,aAAD;KACE,UACE,CAAC,EACC,uBACA,wBAAwB,UAAU;KAGtC,OAAO,CACL;MACE,MAAM,oBAAC,oBAAD,CAAqB,CAAA;MAC3B,OAAO,EAAE,oBAAoB;MAC7B,eACE,UAAU,UAAU,UAAU,UAAU,OAAO;KACnD,GACA;MACE,MAAM,oBAAC,kBAAD,CAAmB,CAAA;MACzB,OAAO,EAAE,uBAAuB;MAChC,eACE,aACE,eACE,UAAU,SACV,sBAAsB,UAAU,UAClC,CACF;KACJ,CACF;IACD,CAAA,CACO;GAhDK,GAAA,UAAU,OAgDf,CACX;EACG,CAAA;CACO,CAAA;AAEnB"}
|
|
@@ -5,6 +5,7 @@ import { PageContainer } from "../../components/PageContainer.js";
|
|
|
5
5
|
import { getSourceTxHash } from "../../stores/routes/utils.js";
|
|
6
6
|
import { useRouteExecutionStore } from "../../stores/routes/RouteExecutionStore.js";
|
|
7
7
|
import { useTools } from "../../hooks/useTools.js";
|
|
8
|
+
import { isHttpUrl } from "../../utils/url.js";
|
|
8
9
|
import { useExplorer } from "../../hooks/useExplorer.js";
|
|
9
10
|
import { useHeader } from "../../hooks/useHeader.js";
|
|
10
11
|
import { buildRouteFromTxHistory } from "../../utils/converters.js";
|
|
@@ -52,7 +53,7 @@ const TransactionDetailsPage = () => {
|
|
|
52
53
|
let supportId = getSourceTxHash(routeExecution?.route) ?? routeExecution?.route.id ?? "";
|
|
53
54
|
const internalTxLink = routeExecution?.route?.steps?.at(-1)?.execution?.internalTxLink;
|
|
54
55
|
const externalTxLink = routeExecution?.route?.steps?.at(-1)?.execution?.externalTxLink;
|
|
55
|
-
const txLink =
|
|
56
|
+
const txLink = [url ? internalTxLink?.replace("https://scan.li.fi", url) : internalTxLink, externalTxLink].find(isHttpUrl) ?? getTransactionLink({ txHash: supportId });
|
|
56
57
|
if (process.env.NODE_ENV === "development") supportId += `_${routeExecution?.route.id}`;
|
|
57
58
|
const startedAt = new Date(routeExecution?.route.steps[0].execution?.startedAt ?? 0);
|
|
58
59
|
if (isLoading && !storedRouteExecution) return /* @__PURE__ */ jsx(TransactionDetailsSkeleton, {});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransactionDetailsPage.js","names":[],"sources":["../../../../src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx"],"sourcesContent":["import type { FullStatusData } from '@lifi/sdk'\nimport { useLocation, useNavigate } from '@tanstack/react-router'\nimport { useEffect, useMemo } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { ContractComponent } from '../../components/ContractComponent/ContractComponent.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { TransactionCard } from '../../components/TransactionCard/TransactionCard.js'\nimport { internalExplorerUrl } from '../../config/constants.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useTools } from '../../hooks/useTools.js'\nimport { useTransactionDetails } from '../../hooks/useTransactionDetails.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useRouteExecutionStore } from '../../stores/routes/RouteExecutionStore.js'\nimport { getSourceTxHash } from '../../stores/routes/utils.js'\nimport { buildRouteFromTxHistory } from '../../utils/converters.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { ReceiptsCard } from './ReceiptsCard.js'\nimport { TransactionDetailsSkeleton } from './TransactionDetailsSkeleton.js'\nimport { TransferIdCard } from './TransferIdCard.js'\n\nexport const TransactionDetailsPage: React.FC = () => {\n const { t } = useTranslation()\n const navigate = useNavigate()\n const { mode, modeOptions, contractSecondaryComponent, explorerUrls } =\n useWidgetConfig()\n const { search }: any = useLocation()\n const { tools } = useTools()\n const { getTransactionLink } = useExplorer()\n const storedRouteExecution = useRouteExecutionStore((store) =>\n search?.routeId ? store.routes[search.routeId] : undefined\n )\n const { transaction, isLoading } = useTransactionDetails(\n !storedRouteExecution && search?.transactionHash\n )\n\n const title =\n mode === 'custom'\n ? t(`header.${modeOptions?.custom?.type ?? 'checkout'}Details`)\n : t('header.transactionDetails')\n useHeader(title)\n\n const routeExecution = useMemo(() => {\n if (storedRouteExecution) {\n return storedRouteExecution\n }\n if (isLoading) {\n return\n }\n if (transaction) {\n return buildRouteFromTxHistory(transaction as FullStatusData, tools)\n }\n }, [isLoading, storedRouteExecution, tools, transaction])\n\n useEffect(() => {\n if (!isLoading && !routeExecution) {\n navigate({ to: navigationRoutes.home })\n }\n }, [isLoading, navigate, routeExecution])\n\n const explorerUrl = explorerUrls?.internal?.[0]\n const url = typeof explorerUrl === 'string' ? explorerUrl : explorerUrl?.url\n\n const sourceTxHash = getSourceTxHash(routeExecution?.route)\n let supportId = sourceTxHash ?? routeExecution?.route.id ?? ''\n\n const internalTxLink =\n routeExecution?.route?.steps?.at(-1)?.execution?.internalTxLink\n const externalTxLink =\n routeExecution?.route?.steps?.at(-1)?.execution?.externalTxLink\n\n const txLink =\n
|
|
1
|
+
{"version":3,"file":"TransactionDetailsPage.js","names":[],"sources":["../../../../src/pages/TransactionDetailsPage/TransactionDetailsPage.tsx"],"sourcesContent":["import type { FullStatusData } from '@lifi/sdk'\nimport { useLocation, useNavigate } from '@tanstack/react-router'\nimport { useEffect, useMemo } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { ContractComponent } from '../../components/ContractComponent/ContractComponent.js'\nimport { PageContainer } from '../../components/PageContainer.js'\nimport { TransactionCard } from '../../components/TransactionCard/TransactionCard.js'\nimport { internalExplorerUrl } from '../../config/constants.js'\nimport { useExplorer } from '../../hooks/useExplorer.js'\nimport { useHeader } from '../../hooks/useHeader.js'\nimport { useTools } from '../../hooks/useTools.js'\nimport { useTransactionDetails } from '../../hooks/useTransactionDetails.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { useRouteExecutionStore } from '../../stores/routes/RouteExecutionStore.js'\nimport { getSourceTxHash } from '../../stores/routes/utils.js'\nimport { buildRouteFromTxHistory } from '../../utils/converters.js'\nimport { navigationRoutes } from '../../utils/navigationRoutes.js'\nimport { isHttpUrl } from '../../utils/url.js'\nimport { ReceiptsCard } from './ReceiptsCard.js'\nimport { TransactionDetailsSkeleton } from './TransactionDetailsSkeleton.js'\nimport { TransferIdCard } from './TransferIdCard.js'\n\nexport const TransactionDetailsPage: React.FC = () => {\n const { t } = useTranslation()\n const navigate = useNavigate()\n const { mode, modeOptions, contractSecondaryComponent, explorerUrls } =\n useWidgetConfig()\n const { search }: any = useLocation()\n const { tools } = useTools()\n const { getTransactionLink } = useExplorer()\n const storedRouteExecution = useRouteExecutionStore((store) =>\n search?.routeId ? store.routes[search.routeId] : undefined\n )\n const { transaction, isLoading } = useTransactionDetails(\n !storedRouteExecution && search?.transactionHash\n )\n\n const title =\n mode === 'custom'\n ? t(`header.${modeOptions?.custom?.type ?? 'checkout'}Details`)\n : t('header.transactionDetails')\n useHeader(title)\n\n const routeExecution = useMemo(() => {\n if (storedRouteExecution) {\n return storedRouteExecution\n }\n if (isLoading) {\n return\n }\n if (transaction) {\n return buildRouteFromTxHistory(transaction as FullStatusData, tools)\n }\n }, [isLoading, storedRouteExecution, tools, transaction])\n\n useEffect(() => {\n if (!isLoading && !routeExecution) {\n navigate({ to: navigationRoutes.home })\n }\n }, [isLoading, navigate, routeExecution])\n\n const explorerUrl = explorerUrls?.internal?.[0]\n const url = typeof explorerUrl === 'string' ? explorerUrl : explorerUrl?.url\n\n const sourceTxHash = getSourceTxHash(routeExecution?.route)\n let supportId = sourceTxHash ?? routeExecution?.route.id ?? ''\n\n const internalTxLink =\n routeExecution?.route?.steps?.at(-1)?.execution?.internalTxLink\n const externalTxLink =\n routeExecution?.route?.steps?.at(-1)?.execution?.externalTxLink\n\n const txLink =\n [\n url ? internalTxLink?.replace(internalExplorerUrl, url) : internalTxLink,\n externalTxLink,\n ].find(isHttpUrl) ?? getTransactionLink({ txHash: supportId })\n\n if (process.env.NODE_ENV === 'development') {\n supportId += `_${routeExecution?.route.id}`\n }\n\n const startedAt = new Date(\n routeExecution?.route.steps[0].execution?.startedAt ?? 0\n )\n\n if (isLoading && !storedRouteExecution) {\n return <TransactionDetailsSkeleton />\n }\n\n if (!routeExecution?.route) {\n return null\n }\n\n return (\n <PageContainer\n topGutters\n bottomGutters\n sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}\n >\n <TransactionCard route={routeExecution.route} date={startedAt} />\n {mode === 'custom' && contractSecondaryComponent ? (\n <ContractComponent>{contractSecondaryComponent}</ContractComponent>\n ) : null}\n <ReceiptsCard route={routeExecution.route} />\n {supportId ? (\n <TransferIdCard transferId={supportId} txLink={txLink} />\n ) : null}\n </PageContainer>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,+BAAyC;CACpD,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,MAAM,aAAa,4BAA4B,iBACrD,gBAAgB;CAClB,MAAM,EAAE,WAAgB,YAAY;CACpC,MAAM,EAAE,UAAU,SAAS;CAC3B,MAAM,EAAE,uBAAuB,YAAY;CAC3C,MAAM,uBAAuB,wBAAwB,UACnD,QAAQ,UAAU,MAAM,OAAO,OAAO,WAAW,KAAA,CACnD;CACA,MAAM,EAAE,aAAa,cAAc,sBACjC,CAAC,wBAAwB,QAAQ,eACnC;CAEA,MAAM,QACJ,SAAS,WACL,EAAE,UAAU,aAAa,QAAQ,QAAQ,WAAW,QAAQ,IAC5D,EAAE,2BAA2B;CACnC,UAAU,KAAK;CAEf,MAAM,iBAAiB,cAAc;EACnC,IAAI,sBACF,OAAO;EAET,IAAI,WACF;EAEF,IAAI,aACF,OAAO,wBAAwB,aAA+B,KAAK;CAEvE,GAAG;EAAC;EAAW;EAAsB;EAAO;CAAW,CAAC;CAExD,gBAAgB;EACd,IAAI,CAAC,aAAa,CAAC,gBACjB,SAAS,EAAE,IAAI,iBAAiB,KAAK,CAAC;CAE1C,GAAG;EAAC;EAAW;EAAU;CAAc,CAAC;CAExC,MAAM,cAAc,cAAc,WAAW;CAC7C,MAAM,MAAM,OAAO,gBAAgB,WAAW,cAAc,aAAa;CAGzE,IAAI,YADiB,gBAAgB,gBAAgB,KAC1B,KAAK,gBAAgB,MAAM,MAAM;CAE5D,MAAM,iBACJ,gBAAgB,OAAO,OAAO,GAAG,EAAE,CAAC,EAAE,WAAW;CACnD,MAAM,iBACJ,gBAAgB,OAAO,OAAO,GAAG,EAAE,CAAC,EAAE,WAAW;CAEnD,MAAM,SACJ,CACE,MAAM,gBAAgB,QAAA,sBAA6B,GAAG,IAAI,gBAC1D,cACF,CAAC,CAAC,KAAK,SAAS,KAAK,mBAAmB,EAAE,QAAQ,UAAU,CAAC;CAE/D,IAAI,QAAQ,IAAI,aAAa,eAC3B,aAAa,IAAI,gBAAgB,MAAM;CAGzC,MAAM,YAAY,IAAI,KACpB,gBAAgB,MAAM,MAAM,EAAE,CAAC,WAAW,aAAa,CACzD;CAEA,IAAI,aAAa,CAAC,sBAChB,OAAO,oBAAC,4BAAD,CAA6B,CAAA;CAGtC,IAAI,CAAC,gBAAgB,OACnB,OAAO;CAGT,OACE,qBAAC,eAAD;EACE,YAAA;EACA,eAAA;EACA,IAAI;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAE;EAHzD,UAAA;GAKE,oBAAC,iBAAD;IAAiB,OAAO,eAAe;IAAO,MAAM;GAAY,CAAA;GAC/D,SAAS,YAAY,6BACpB,oBAAC,mBAAD,EAAA,UAAoB,2BAA8C,CAAA,IAChE;GACJ,oBAAC,cAAD,EAAc,OAAO,eAAe,MAAQ,CAAA;GAC3C,YACC,oBAAC,gBAAD;IAAgB,YAAY;IAAmB;GAAS,CAAA,IACtD;EACS;;AAEnB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useWidgetConfig } from "../../providers/WidgetProvider/WidgetProvider.js";
|
|
2
2
|
import { Card as Card$1 } from "../../components/Card/Card.js";
|
|
3
3
|
import { CardIconButton } from "../../components/Card/CardIconButton.js";
|
|
4
|
+
import { openInNewTab } from "../../utils/url.js";
|
|
4
5
|
import { CardTitle } from "../../components/Card/CardTitle.js";
|
|
5
6
|
import { ContactSupportButton } from "./ContactSupportButton.js";
|
|
6
7
|
import { Box, Typography } from "@mui/material";
|
|
@@ -16,7 +17,7 @@ const TransferIdCard = ({ transferId, txLink }) => {
|
|
|
16
17
|
await navigator.clipboard.writeText(transferId);
|
|
17
18
|
};
|
|
18
19
|
const openTransferIdInExplorer = () => {
|
|
19
|
-
|
|
20
|
+
openInNewTab(txLink);
|
|
20
21
|
};
|
|
21
22
|
return /* @__PURE__ */ jsxs(Card$1, {
|
|
22
23
|
type: "default",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransferIdCard.js","names":["Card"],"sources":["../../../../src/pages/TransactionDetailsPage/TransferIdCard.tsx"],"sourcesContent":["import ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport OpenInNew from '@mui/icons-material/OpenInNew'\nimport { Box, Typography } from '@mui/material'\nimport type { JSX } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { Card } from '../../components/Card/Card.js'\nimport { CardIconButton } from '../../components/Card/CardIconButton.js'\nimport { CardTitle } from '../../components/Card/CardTitle.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { ContactSupportButton } from './ContactSupportButton.js'\n\ninterface TransferIdCardProps {\n transferId: string\n txLink?: string\n}\n\nexport const TransferIdCard = ({\n transferId,\n txLink,\n}: TransferIdCardProps): JSX.Element => {\n const { t } = useTranslation()\n const { hiddenUI } = useWidgetConfig()\n\n const copyTransferId = async () => {\n await navigator.clipboard.writeText(transferId)\n }\n\n const openTransferIdInExplorer = () => {\n
|
|
1
|
+
{"version":3,"file":"TransferIdCard.js","names":["Card"],"sources":["../../../../src/pages/TransactionDetailsPage/TransferIdCard.tsx"],"sourcesContent":["import ContentCopyRounded from '@mui/icons-material/ContentCopyRounded'\nimport OpenInNew from '@mui/icons-material/OpenInNew'\nimport { Box, Typography } from '@mui/material'\nimport type { JSX } from 'react'\nimport { useTranslation } from 'react-i18next'\nimport { Card } from '../../components/Card/Card.js'\nimport { CardIconButton } from '../../components/Card/CardIconButton.js'\nimport { CardTitle } from '../../components/Card/CardTitle.js'\nimport { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'\nimport { openInNewTab } from '../../utils/url.js'\nimport { ContactSupportButton } from './ContactSupportButton.js'\n\ninterface TransferIdCardProps {\n transferId: string\n txLink?: string\n}\n\nexport const TransferIdCard = ({\n transferId,\n txLink,\n}: TransferIdCardProps): JSX.Element => {\n const { t } = useTranslation()\n const { hiddenUI } = useWidgetConfig()\n\n const copyTransferId = async () => {\n await navigator.clipboard.writeText(transferId)\n }\n\n const openTransferIdInExplorer = () => {\n openInNewTab(txLink)\n }\n\n return (\n <Card type=\"default\" indented>\n <Box\n sx={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n }}\n >\n <CardTitle sx={{ p: 0 }}>{t('main.transferId')}</CardTitle>\n <Box\n sx={{\n gap: 1,\n display: 'flex',\n }}\n >\n <CardIconButton size=\"small\" onClick={copyTransferId}>\n <ContentCopyRounded fontSize=\"inherit\" />\n </CardIconButton>\n {txLink ? (\n <CardIconButton size=\"small\" onClick={openTransferIdInExplorer}>\n <OpenInNew fontSize=\"inherit\" />\n </CardIconButton>\n ) : null}\n {!hiddenUI?.contactSupport ? (\n <ContactSupportButton supportId={transferId} />\n ) : null}\n </Box>\n </Box>\n <Typography\n variant=\"body2\"\n sx={{\n pt: 2,\n wordBreak: 'break-all',\n }}\n >\n {transferId}\n </Typography>\n </Card>\n )\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAa,kBAAkB,EAC7B,YACA,aACsC;CACtC,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,EAAE,aAAa,gBAAgB;CAErC,MAAM,iBAAiB,YAAY;EACjC,MAAM,UAAU,UAAU,UAAU,UAAU;CAChD;CAEA,MAAM,iCAAiC;EACrC,aAAa,MAAM;CACrB;CAEA,OACE,qBAACA,QAAD;EAAM,MAAK;EAAU,UAAA;EAArB,UAAA,CACE,qBAAC,KAAD;GACE,IAAI;IACF,SAAS;IACT,YAAY;IACZ,gBAAgB;GAClB;GALF,UAAA,CAOE,oBAAC,WAAD;IAAW,IAAI,EAAE,GAAG,EAAE;IAAI,UAAA,EAAE,iBAAiB;GAAa,CAAA,GAC1D,qBAAC,KAAD;IACE,IAAI;KACF,KAAK;KACL,SAAS;IACX;IAJF,UAAA;KAME,oBAAC,gBAAD;MAAgB,MAAK;MAAQ,SAAS;MACpC,UAAA,oBAAC,oBAAD,EAAoB,UAAS,UAAW,CAAA;KAC1B,CAAA;KACf,SACC,oBAAC,gBAAD;MAAgB,MAAK;MAAQ,SAAS;MACpC,UAAA,oBAAC,WAAD,EAAW,UAAS,UAAW,CAAA;KACjB,CAAA,IACd;KACH,CAAC,UAAU,iBACV,oBAAC,sBAAD,EAAsB,WAAW,WAAa,CAAA,IAC5C;IACD;GACF,CAAA,CAAA;EACL,CAAA,GAAA,oBAAC,YAAD;GACE,SAAQ;GACR,IAAI;IACF,IAAI;IACJ,WAAW;GACb;GAEC,UAAA;EACS,CAAA,CACR;;AAEV"}
|
package/dist/esm/shared.d.ts
CHANGED
|
@@ -103,5 +103,6 @@ import { buildRouteFromTxHistory } from "./utils/converters.js";
|
|
|
103
103
|
import { ElementId, createElementId } from "./utils/elements.js";
|
|
104
104
|
import { hasEnumFlag } from "./utils/enum.js";
|
|
105
105
|
import { fitInputText } from "./utils/input.js";
|
|
106
|
+
import { isHttpUrl, openInNewTab } from "./utils/url.js";
|
|
106
107
|
import { shortenAddress } from "./utils/wallet.js";
|
|
107
|
-
export { ActionRow, AddressType, AmountInputEndAdornment, AvatarBadgedDefault, BaseTransactionButton, BookmarkStoreProvider, type BottomSheetBase, ButtonTertiary, Card, CardIconButton, CardTitle, ChainAvatar, ChainSelect, ConfirmToAddressSheet, ContactSupportButton, ContractComponent, DateLabelContainer, DateLabelText, DetailInfoIcon, DetailLabel, DetailLabelContainer, DetailRow, DetailValue, ElementId, ExchangeRateBottomSheet, type ExchangeRateBottomSheetBase, ExecutionDoneCard, type ExecutionRow, ExecutionStatusCard, ExternalLink, FeeBreakdownTooltip, FormControl, FormKeyHelper, type FormRef, type FormType, type FormTypeProps, I18nProvider, IconCircle, IconTypography, Input, InputCard, InputPriceButton, ListItemButton, MainWarningMessages, PageContainer, PoweredBy, ProgressToNextUpdate, QueryClientProvider, RouteCard, RouteCardSkeleton, RouteDetails, RouteExecutionStatus, RouteNotFoundCard, RouteTokens, RouteTracker, SDKClientProvider, SearchInput, SearchTokenInput, SelectChainPage, SendToWalletButton, SentToWalletRow, SettingsStoreProvider, Stack, StartTransactionButton, StatusIcon, StepActionsList, StoreProvider, Token, TokenAvatar, TokenNotFound, TokenRate, TokenValueBottomSheet, TransactionDoneButtons, VirtualizedTokenList, WalletProvider, WarningMessages, type WidgetConfig, WidgetEvent, WidgetProvider, type WidgetTheme, buildRouteFromTxHistory, calculateValueLossPercentage, createElementId, createTheme, fitInputText, formatDuration, formatInputAmount, formatTokenAmount, formatTokenPrice, getAccumulatedFeeCostsBreakdown, getPriceImpact, getSourceTxHash, getTokenValueLossThreshold, hasEnumFlag, isRouteActive, isRouteDone, isRouteFailed, navigationRoutes, priceToTokenAmount, shortenAddress, usdDecimals, useAddressActivity, useAddressValidation, useAvailableChains, useChain, useChainOrderStore, useContactSupport, useDebouncedWatch, useExecutionRows, useExplorer, useFieldActions, useFieldValues, useHeader, useHeaderStore, useInputModeStore, useListHeight, useNavigateBack, useRouteExecution, useRouteExecutionMessage, useRouteExecutionStore, useRouteExecutionStoreContext, useRoutes, useSDKClient, useSetHeaderHeight, useSettingsStoreContext, useToAddressRequirements, useToken, useTokenAddressBalance, useTokenBalances, useTokenList, useTokenSelect, useTools, useWidgetConfig, useWidgetEvents };
|
|
108
|
+
export { ActionRow, AddressType, AmountInputEndAdornment, AvatarBadgedDefault, BaseTransactionButton, BookmarkStoreProvider, type BottomSheetBase, ButtonTertiary, Card, CardIconButton, CardTitle, ChainAvatar, ChainSelect, ConfirmToAddressSheet, ContactSupportButton, ContractComponent, DateLabelContainer, DateLabelText, DetailInfoIcon, DetailLabel, DetailLabelContainer, DetailRow, DetailValue, ElementId, ExchangeRateBottomSheet, type ExchangeRateBottomSheetBase, ExecutionDoneCard, type ExecutionRow, ExecutionStatusCard, ExternalLink, FeeBreakdownTooltip, FormControl, FormKeyHelper, type FormRef, type FormType, type FormTypeProps, I18nProvider, IconCircle, IconTypography, Input, InputCard, InputPriceButton, ListItemButton, MainWarningMessages, PageContainer, PoweredBy, ProgressToNextUpdate, QueryClientProvider, RouteCard, RouteCardSkeleton, RouteDetails, RouteExecutionStatus, RouteNotFoundCard, RouteTokens, RouteTracker, SDKClientProvider, SearchInput, SearchTokenInput, SelectChainPage, SendToWalletButton, SentToWalletRow, SettingsStoreProvider, Stack, StartTransactionButton, StatusIcon, StepActionsList, StoreProvider, Token, TokenAvatar, TokenNotFound, TokenRate, TokenValueBottomSheet, TransactionDoneButtons, VirtualizedTokenList, WalletProvider, WarningMessages, type WidgetConfig, WidgetEvent, WidgetProvider, type WidgetTheme, buildRouteFromTxHistory, calculateValueLossPercentage, createElementId, createTheme, fitInputText, formatDuration, formatInputAmount, formatTokenAmount, formatTokenPrice, getAccumulatedFeeCostsBreakdown, getPriceImpact, getSourceTxHash, getTokenValueLossThreshold, hasEnumFlag, isHttpUrl, isRouteActive, isRouteDone, isRouteFailed, navigationRoutes, openInNewTab, priceToTokenAmount, shortenAddress, usdDecimals, useAddressActivity, useAddressValidation, useAvailableChains, useChain, useChainOrderStore, useContactSupport, useDebouncedWatch, useExecutionRows, useExplorer, useFieldActions, useFieldValues, useHeader, useHeaderStore, useInputModeStore, useListHeight, useNavigateBack, useRouteExecution, useRouteExecutionMessage, useRouteExecutionStore, useRouteExecutionStoreContext, useRoutes, useSDKClient, useSetHeaderHeight, useSettingsStoreContext, useToAddressRequirements, useToken, useTokenAddressBalance, useTokenBalances, useTokenList, useTokenSelect, useTools, useWidgetConfig, useWidgetEvents };
|
package/dist/esm/shared.js
CHANGED
|
@@ -44,6 +44,7 @@ import { RouteNotFoundCard } from "./components/RouteCard/RouteNotFoundCard.js";
|
|
|
44
44
|
import { useNavigateBack } from "./hooks/useNavigateBack.js";
|
|
45
45
|
import { useTools } from "./hooks/useTools.js";
|
|
46
46
|
import { shortenAddress } from "./utils/wallet.js";
|
|
47
|
+
import { isHttpUrl, openInNewTab } from "./utils/url.js";
|
|
47
48
|
import { useExplorer } from "./hooks/useExplorer.js";
|
|
48
49
|
import { AddressType, useAddressValidation } from "./hooks/useAddressValidation.js";
|
|
49
50
|
import { useHeader } from "./hooks/useHeader.js";
|
|
@@ -102,4 +103,4 @@ import { createTheme } from "./themes/createTheme.js";
|
|
|
102
103
|
import { WalletProvider } from "./providers/WalletProvider/WalletProvider.js";
|
|
103
104
|
import { StoreProvider } from "./stores/StoreProvider.js";
|
|
104
105
|
import { useTokenList } from "./hooks/useTokenList.js";
|
|
105
|
-
export { ActionRow, AddressType, AmountInputEndAdornment, AvatarBadgedDefault, BaseTransactionButton, BookmarkStoreProvider, ButtonTertiary, Card, CardIconButton, CardTitle, ChainAvatar, ChainSelect, ConfirmToAddressSheet, ContactSupportButton, ContractComponent, DateLabelContainer, DateLabelText, DetailInfoIcon, DetailLabel, DetailLabelContainer, DetailRow, DetailValue, ElementId, ExchangeRateBottomSheet, ExecutionDoneCard, ExecutionStatusCard, ExternalLink, FeeBreakdownTooltip, FormControl, FormKeyHelper, I18nProvider, IconCircle, IconTypography, Input, InputCard, InputPriceButton, ListItemButton, MainWarningMessages, PageContainer, PoweredBy, ProgressToNextUpdate, QueryClientProvider, RouteCard, RouteCardSkeleton, RouteDetails, RouteExecutionStatus, RouteNotFoundCard, RouteTokens, RouteTracker, SDKClientProvider, SearchInput, SearchTokenInput, SelectChainPage, SendToWalletButton, SentToWalletRow, SettingsStoreProvider, Stack, StartTransactionButton, StatusIcon, StepActionsList, StoreProvider, Token, TokenAvatar, TokenNotFound, TokenRate, TokenValueBottomSheet, TransactionDoneButtons, VirtualizedTokenList, WalletProvider, WarningMessages, WidgetEvent, WidgetProvider, buildRouteFromTxHistory, calculateValueLossPercentage, createElementId, createTheme, fitInputText, formatDuration, formatInputAmount, formatTokenAmount, formatTokenPrice, getAccumulatedFeeCostsBreakdown, getPriceImpact, getSourceTxHash, getTokenValueLossThreshold, hasEnumFlag, isRouteActive, isRouteDone, isRouteFailed, navigationRoutes, priceToTokenAmount, shortenAddress, usdDecimals, useAddressActivity, useAddressValidation, useAvailableChains, useChain, useChainOrderStore, useContactSupport, useDebouncedWatch, useExecutionRows, useExplorer, useFieldActions, useFieldValues, useHeader, useHeaderStore, useInputModeStore, useListHeight, useNavigateBack, useRouteExecution, useRouteExecutionMessage, useRouteExecutionStore, useRouteExecutionStoreContext, useRoutes, useSDKClient, useSetHeaderHeight, useSettingsStoreContext, useToAddressRequirements, useToken, useTokenAddressBalance, useTokenBalances, useTokenList, useTokenSelect, useTools, useWidgetConfig, useWidgetEvents };
|
|
106
|
+
export { ActionRow, AddressType, AmountInputEndAdornment, AvatarBadgedDefault, BaseTransactionButton, BookmarkStoreProvider, ButtonTertiary, Card, CardIconButton, CardTitle, ChainAvatar, ChainSelect, ConfirmToAddressSheet, ContactSupportButton, ContractComponent, DateLabelContainer, DateLabelText, DetailInfoIcon, DetailLabel, DetailLabelContainer, DetailRow, DetailValue, ElementId, ExchangeRateBottomSheet, ExecutionDoneCard, ExecutionStatusCard, ExternalLink, FeeBreakdownTooltip, FormControl, FormKeyHelper, I18nProvider, IconCircle, IconTypography, Input, InputCard, InputPriceButton, ListItemButton, MainWarningMessages, PageContainer, PoweredBy, ProgressToNextUpdate, QueryClientProvider, RouteCard, RouteCardSkeleton, RouteDetails, RouteExecutionStatus, RouteNotFoundCard, RouteTokens, RouteTracker, SDKClientProvider, SearchInput, SearchTokenInput, SelectChainPage, SendToWalletButton, SentToWalletRow, SettingsStoreProvider, Stack, StartTransactionButton, StatusIcon, StepActionsList, StoreProvider, Token, TokenAvatar, TokenNotFound, TokenRate, TokenValueBottomSheet, TransactionDoneButtons, VirtualizedTokenList, WalletProvider, WarningMessages, WidgetEvent, WidgetProvider, buildRouteFromTxHistory, calculateValueLossPercentage, createElementId, createTheme, fitInputText, formatDuration, formatInputAmount, formatTokenAmount, formatTokenPrice, getAccumulatedFeeCostsBreakdown, getPriceImpact, getSourceTxHash, getTokenValueLossThreshold, hasEnumFlag, isHttpUrl, isRouteActive, isRouteDone, isRouteFailed, navigationRoutes, openInNewTab, priceToTokenAmount, shortenAddress, usdDecimals, useAddressActivity, useAddressValidation, useAvailableChains, useChain, useChainOrderStore, useContactSupport, useDebouncedWatch, useExecutionRows, useExplorer, useFieldActions, useFieldValues, useHeader, useHeaderStore, useInputModeStore, useListHeight, useNavigateBack, useRouteExecution, useRouteExecutionMessage, useRouteExecutionStore, useRouteExecutionStoreContext, useRoutes, useSDKClient, useSetHeaderHeight, useSettingsStoreContext, useToAddressRequirements, useToken, useTokenAddressBalance, useTokenBalances, useTokenList, useTokenSelect, useTools, useWidgetConfig, useWidgetEvents };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","names":[],"sources":["../../../src/utils/url.ts"],"mappings":";qBAAa,YAAa,4BAA0B;qBAYvC,eAAgB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/utils/url.ts
|
|
2
|
+
const isHttpUrl = (url) => {
|
|
3
|
+
if (!url) return false;
|
|
4
|
+
try {
|
|
5
|
+
const { protocol } = new URL(url);
|
|
6
|
+
return protocol === "https:" || protocol === "http:";
|
|
7
|
+
} catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const openInNewTab = (url) => {
|
|
12
|
+
if (isHttpUrl(url)) window.open(url, "_blank", "noopener,noreferrer");
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { isHttpUrl, openInNewTab };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","names":[],"sources":["../../../src/utils/url.ts"],"sourcesContent":["export const isHttpUrl = (url: string | undefined): url is string => {\n if (!url) {\n return false\n }\n try {\n const { protocol } = new URL(url)\n return protocol === 'https:' || protocol === 'http:'\n } catch {\n return false\n }\n}\n\nexport const openInNewTab = (url: string | undefined): void => {\n if (isHttpUrl(url)) {\n window.open(url, '_blank', 'noopener,noreferrer')\n }\n}\n"],"mappings":";AAAA,MAAa,aAAa,QAA2C;CACnE,IAAI,CAAC,KACH,OAAO;CAET,IAAI;EACF,MAAM,EAAE,aAAa,IAAI,IAAI,GAAG;EAChC,OAAO,aAAa,YAAY,aAAa;CAC/C,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAa,gBAAgB,QAAkC;CAC7D,IAAI,UAAU,GAAG,GACf,OAAO,KAAK,KAAK,UAAU,qBAAqB;AAEpD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifi/widget",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.1",
|
|
4
4
|
"description": "LI.FI Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/esm/index.js",
|
package/src/config/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export const name = '@lifi/widget'
|
|
2
|
-
export const version = '4.7.
|
|
2
|
+
export const version = '4.7.1'
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/** @vitest-environment happy-dom */
|
|
2
|
+
|
|
3
|
+
import type { ExtendedChain } from '@lifi/sdk'
|
|
4
|
+
import { act } from 'react'
|
|
5
|
+
import { createRoot, type Root } from 'react-dom/client'
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
7
|
+
import type { WidgetConfig } from '../types/widget.js'
|
|
8
|
+
|
|
9
|
+
const txHash = `0x${'ab'.repeat(32)}`
|
|
10
|
+
|
|
11
|
+
let explorerUrls: WidgetConfig['explorerUrls']
|
|
12
|
+
let blockExplorerUrls: string[] = []
|
|
13
|
+
|
|
14
|
+
const getChainById = (id: number) =>
|
|
15
|
+
({
|
|
16
|
+
id,
|
|
17
|
+
chainType: 'EVM',
|
|
18
|
+
metamask: { blockExplorerUrls },
|
|
19
|
+
}) as unknown as ExtendedChain
|
|
20
|
+
|
|
21
|
+
vi.mock('../providers/WidgetProvider/WidgetProvider.js', () => ({
|
|
22
|
+
useWidgetConfig: () => ({ explorerUrls }),
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
vi.mock('./useAvailableChains.js', () => ({
|
|
26
|
+
useAvailableChains: () => ({ getChainById }),
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
const { useExplorer } = await import('./useExplorer.js')
|
|
30
|
+
|
|
31
|
+
let explorer: ReturnType<typeof useExplorer>
|
|
32
|
+
|
|
33
|
+
const ExplorerHandle = (): null => {
|
|
34
|
+
explorer = useExplorer()
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let root: Root
|
|
39
|
+
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
explorerUrls = undefined
|
|
42
|
+
blockExplorerUrls = ['https://etherscan.io/']
|
|
43
|
+
root = createRoot(document.createElement('div'))
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
act(() => {
|
|
48
|
+
root.unmount()
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const render = () => {
|
|
53
|
+
act(() => {
|
|
54
|
+
root.render(<ExplorerHandle />)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe('useExplorer', () => {
|
|
59
|
+
it('builds links from an https explorer base', () => {
|
|
60
|
+
render()
|
|
61
|
+
expect(explorer.getAddressLink('0xabc', 1)).toBe(
|
|
62
|
+
'https://etherscan.io/address/0xabc'
|
|
63
|
+
)
|
|
64
|
+
expect(explorer.getTransactionLink({ txHash, chain: 1 })).toBe(
|
|
65
|
+
`https://etherscan.io/tx/${txHash}`
|
|
66
|
+
)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('returns no link when chain metadata carries a javascript: explorer base', () => {
|
|
70
|
+
blockExplorerUrls = ['javascript:alert(document.domain)//']
|
|
71
|
+
render()
|
|
72
|
+
expect(explorer.getAddressLink('0xabc', 1)).toBeUndefined()
|
|
73
|
+
expect(explorer.getTransactionLink({ txHash, chain: 1 })).toBeUndefined()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('returns no link when the integrator config carries a javascript: explorer base', () => {
|
|
77
|
+
explorerUrls = {
|
|
78
|
+
1: ['javascript:alert(document.domain)'],
|
|
79
|
+
internal: ['javascript:alert(document.domain)'],
|
|
80
|
+
}
|
|
81
|
+
render()
|
|
82
|
+
expect(explorer.getAddressLink('0xabc', 1)).toBeUndefined()
|
|
83
|
+
expect(explorer.getTransactionLink({ txHash })).toBeUndefined()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('falls back to an https txLink when the hash link is rejected', () => {
|
|
87
|
+
blockExplorerUrls = ['javascript:alert(document.domain)//']
|
|
88
|
+
render()
|
|
89
|
+
expect(
|
|
90
|
+
explorer.getTransactionLink({
|
|
91
|
+
txHash,
|
|
92
|
+
txLink: 'https://scan.li.fi/tx/0xabc',
|
|
93
|
+
chain: 1,
|
|
94
|
+
})
|
|
95
|
+
).toBe('https://scan.li.fi/tx/0xabc')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('rejects a javascript: txLink fallback', () => {
|
|
99
|
+
render()
|
|
100
|
+
expect(
|
|
101
|
+
explorer.getTransactionLink({
|
|
102
|
+
txLink: 'javascript:alert(document.domain)',
|
|
103
|
+
})
|
|
104
|
+
).toBeUndefined()
|
|
105
|
+
})
|
|
106
|
+
})
|
package/src/hooks/useExplorer.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { useCallback } from 'react'
|
|
|
4
4
|
import { internalExplorerUrl } from '../config/constants.js'
|
|
5
5
|
import { useAvailableChains } from '../hooks/useAvailableChains.js'
|
|
6
6
|
import { useWidgetConfig } from '../providers/WidgetProvider/WidgetProvider.js'
|
|
7
|
+
import { isHttpUrl } from '../utils/url.js'
|
|
7
8
|
|
|
8
9
|
const sanitiseBaseUrl = (baseUrl: string) => baseUrl.trim().replace(/\/+$/, '')
|
|
9
10
|
|
|
@@ -25,7 +26,10 @@ type TransactionLinkProps = {
|
|
|
25
26
|
|
|
26
27
|
export const useExplorer = (): {
|
|
27
28
|
getTransactionLink: (props: TransactionLinkProps) => string | undefined
|
|
28
|
-
getAddressLink: (
|
|
29
|
+
getAddressLink: (
|
|
30
|
+
address: string,
|
|
31
|
+
chain?: Chain | number
|
|
32
|
+
) => string | undefined
|
|
29
33
|
} => {
|
|
30
34
|
const { explorerUrls } = useWidgetConfig()
|
|
31
35
|
const { getChainById } = useAvailableChains()
|
|
@@ -82,11 +86,12 @@ export const useExplorer = (): {
|
|
|
82
86
|
config.resolvedChain?.chainType !== ChainType.EVM ||
|
|
83
87
|
config.hasOverride ||
|
|
84
88
|
isHex(txHash, { strict: true })
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
const link = `${config.url}/${config.txPath}/${txHash}`
|
|
90
|
+
if (validForEvm && isHttpUrl(link)) {
|
|
91
|
+
return link
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
|
-
return txLink
|
|
94
|
+
return isHttpUrl(txLink) ? txLink : undefined
|
|
90
95
|
},
|
|
91
96
|
[getExplorerConfig]
|
|
92
97
|
)
|
|
@@ -94,7 +99,8 @@ export const useExplorer = (): {
|
|
|
94
99
|
const getAddressLink = useCallback(
|
|
95
100
|
(address: string, chain?: Chain | number) => {
|
|
96
101
|
const config = getExplorerConfig(chain)
|
|
97
|
-
|
|
102
|
+
const link = `${config.url}/${config.addressPath}/${address}`
|
|
103
|
+
return isHttpUrl(link) ? link : undefined
|
|
98
104
|
},
|
|
99
105
|
[getExplorerConfig]
|
|
100
106
|
)
|
|
@@ -23,6 +23,7 @@ import { useBookmarks } from '../../stores/bookmarks/useBookmarks.js'
|
|
|
23
23
|
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
24
24
|
import { defaultChainIdsByType } from '../../utils/chainType.js'
|
|
25
25
|
import { navigationRoutes } from '../../utils/navigationRoutes.js'
|
|
26
|
+
import { openInNewTab } from '../../utils/url.js'
|
|
26
27
|
import { shortenAddress } from '../../utils/wallet.js'
|
|
27
28
|
import { BookmarkAddressSheet } from './BookmarkAddressSheet.js'
|
|
28
29
|
import { BookmarkButtonContainer } from './SendToWalletPage.style.js'
|
|
@@ -106,12 +107,11 @@ export const BookmarksPage = (): JSX.Element => {
|
|
|
106
107
|
icon: <OpenInNewRounded />,
|
|
107
108
|
label: t('button.viewOnExplorer'),
|
|
108
109
|
onClick: () =>
|
|
109
|
-
|
|
110
|
+
openInNewTab(
|
|
110
111
|
getAddressLink(
|
|
111
112
|
bookmark.address,
|
|
112
113
|
defaultChainIdsByType[bookmark.chainType]
|
|
113
|
-
)
|
|
114
|
-
'_blank'
|
|
114
|
+
)
|
|
115
115
|
),
|
|
116
116
|
},
|
|
117
117
|
{
|
|
@@ -20,6 +20,7 @@ import { useToAddressRequirements } from '../../hooks/useToAddressRequirements.j
|
|
|
20
20
|
import { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js'
|
|
21
21
|
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
22
22
|
import { navigationRoutes } from '../../utils/navigationRoutes.js'
|
|
23
|
+
import { openInNewTab } from '../../utils/url.js'
|
|
23
24
|
import { shortenAddress } from '../../utils/wallet.js'
|
|
24
25
|
|
|
25
26
|
export const ConnectedWalletsPage = (): JSX.Element => {
|
|
@@ -81,9 +82,8 @@ export const ConnectedWalletsPage = (): JSX.Element => {
|
|
|
81
82
|
label: t('button.viewOnExplorer'),
|
|
82
83
|
onClick: () => {
|
|
83
84
|
if (account.address) {
|
|
84
|
-
|
|
85
|
-
getAddressLink(account.address, account.chainId)
|
|
86
|
-
'_blank'
|
|
85
|
+
openInNewTab(
|
|
86
|
+
getAddressLink(account.address, account.chainId)
|
|
87
87
|
)
|
|
88
88
|
}
|
|
89
89
|
},
|
|
@@ -24,6 +24,7 @@ import { useBookmarks } from '../../stores/bookmarks/useBookmarks.js'
|
|
|
24
24
|
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
25
25
|
import { defaultChainIdsByType } from '../../utils/chainType.js'
|
|
26
26
|
import { navigationRoutes } from '../../utils/navigationRoutes.js'
|
|
27
|
+
import { openInNewTab } from '../../utils/url.js'
|
|
27
28
|
import { shortenAddress } from '../../utils/wallet.js'
|
|
28
29
|
import { BookmarkAddressSheet } from './BookmarkAddressSheet.js'
|
|
29
30
|
|
|
@@ -118,12 +119,11 @@ export const RecentWalletsPage = (): JSX.Element => {
|
|
|
118
119
|
icon: <OpenInNewRounded />,
|
|
119
120
|
label: t('button.viewOnExplorer'),
|
|
120
121
|
onClick: () =>
|
|
121
|
-
|
|
122
|
+
openInNewTab(
|
|
122
123
|
getAddressLink(
|
|
123
124
|
recentWallet.address,
|
|
124
125
|
defaultChainIdsByType[recentWallet.chainType]
|
|
125
|
-
)
|
|
126
|
-
'_blank'
|
|
126
|
+
)
|
|
127
127
|
),
|
|
128
128
|
},
|
|
129
129
|
{
|
|
@@ -18,6 +18,7 @@ import { useBookmarkActions } from '../../stores/bookmarks/useBookmarkActions.js
|
|
|
18
18
|
import { useFieldActions } from '../../stores/form/useFieldActions.js'
|
|
19
19
|
import type { ToAddress } from '../../types/widget.js'
|
|
20
20
|
import { defaultChainIdsByType } from '../../utils/chainType.js'
|
|
21
|
+
import { openInNewTab } from '../../utils/url.js'
|
|
21
22
|
import { shortenAddress } from '../../utils/wallet.js'
|
|
22
23
|
|
|
23
24
|
export const SendToConfiguredWalletPage = (): JSX.Element => {
|
|
@@ -92,12 +93,11 @@ export const SendToConfiguredWalletPage = (): JSX.Element => {
|
|
|
92
93
|
icon: <OpenInNewRounded />,
|
|
93
94
|
label: t('button.viewOnExplorer'),
|
|
94
95
|
onClick: () =>
|
|
95
|
-
|
|
96
|
+
openInNewTab(
|
|
96
97
|
getAddressLink(
|
|
97
98
|
toAddress.address,
|
|
98
99
|
defaultChainIdsByType[toAddress.chainType]
|
|
99
|
-
)
|
|
100
|
-
'_blank'
|
|
100
|
+
)
|
|
101
101
|
),
|
|
102
102
|
},
|
|
103
103
|
]}
|
|
@@ -15,6 +15,7 @@ import { useRouteExecutionStore } from '../../stores/routes/RouteExecutionStore.
|
|
|
15
15
|
import { getSourceTxHash } from '../../stores/routes/utils.js'
|
|
16
16
|
import { buildRouteFromTxHistory } from '../../utils/converters.js'
|
|
17
17
|
import { navigationRoutes } from '../../utils/navigationRoutes.js'
|
|
18
|
+
import { isHttpUrl } from '../../utils/url.js'
|
|
18
19
|
import { ReceiptsCard } from './ReceiptsCard.js'
|
|
19
20
|
import { TransactionDetailsSkeleton } from './TransactionDetailsSkeleton.js'
|
|
20
21
|
import { TransferIdCard } from './TransferIdCard.js'
|
|
@@ -70,11 +71,10 @@ export const TransactionDetailsPage: React.FC = () => {
|
|
|
70
71
|
routeExecution?.route?.steps?.at(-1)?.execution?.externalTxLink
|
|
71
72
|
|
|
72
73
|
const txLink =
|
|
73
|
-
|
|
74
|
-
? internalTxLink?.replace(internalExplorerUrl, url)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
getTransactionLink({ txHash: supportId })
|
|
74
|
+
[
|
|
75
|
+
url ? internalTxLink?.replace(internalExplorerUrl, url) : internalTxLink,
|
|
76
|
+
externalTxLink,
|
|
77
|
+
].find(isHttpUrl) ?? getTransactionLink({ txHash: supportId })
|
|
78
78
|
|
|
79
79
|
if (process.env.NODE_ENV === 'development') {
|
|
80
80
|
supportId += `_${routeExecution?.route.id}`
|
|
@@ -7,6 +7,7 @@ import { Card } from '../../components/Card/Card.js'
|
|
|
7
7
|
import { CardIconButton } from '../../components/Card/CardIconButton.js'
|
|
8
8
|
import { CardTitle } from '../../components/Card/CardTitle.js'
|
|
9
9
|
import { useWidgetConfig } from '../../providers/WidgetProvider/WidgetProvider.js'
|
|
10
|
+
import { openInNewTab } from '../../utils/url.js'
|
|
10
11
|
import { ContactSupportButton } from './ContactSupportButton.js'
|
|
11
12
|
|
|
12
13
|
interface TransferIdCardProps {
|
|
@@ -26,7 +27,7 @@ export const TransferIdCard = ({
|
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
const openTransferIdInExplorer = () => {
|
|
29
|
-
|
|
30
|
+
openInNewTab(txLink)
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
return (
|
package/src/shared.ts
CHANGED
|
@@ -188,4 +188,5 @@ export {
|
|
|
188
188
|
export { getPriceImpact } from './utils/getPriceImpact.js'
|
|
189
189
|
export { fitInputText } from './utils/input.js'
|
|
190
190
|
export { navigationRoutes } from './utils/navigationRoutes.js'
|
|
191
|
+
export { isHttpUrl, openInNewTab } from './utils/url.js'
|
|
191
192
|
export { shortenAddress } from './utils/wallet.js'
|
package/src/utils/url.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const isHttpUrl = (url: string | undefined): url is string => {
|
|
2
|
+
if (!url) {
|
|
3
|
+
return false
|
|
4
|
+
}
|
|
5
|
+
try {
|
|
6
|
+
const { protocol } = new URL(url)
|
|
7
|
+
return protocol === 'https:' || protocol === 'http:'
|
|
8
|
+
} catch {
|
|
9
|
+
return false
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const openInNewTab = (url: string | undefined): void => {
|
|
14
|
+
if (isHttpUrl(url)) {
|
|
15
|
+
window.open(url, '_blank', 'noopener,noreferrer')
|
|
16
|
+
}
|
|
17
|
+
}
|