Skip to content

Commit

Permalink
refactor: move explorer link generation logic to core
Browse files Browse the repository at this point in the history
  • Loading branch information
VmMad committed Nov 7, 2024
1 parent 889fe4e commit 92f2afa
Show file tree
Hide file tree
Showing 20 changed files with 154 additions and 82 deletions.
File renamed without changes.
4 changes: 4 additions & 0 deletions apps/core/src/enums/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './ExplorerLinkType';
1 change: 1 addition & 0 deletions apps/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

export * from './api';
export * from './enums';
export * from './components';
export * from './utils';
export * from './hooks';
Expand Down
File renamed without changes.
58 changes: 58 additions & 0 deletions apps/core/src/utils/getExplorerLink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Mysten Labs, Inc.
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { getCustomNetwork } from '.';
import { getNetwork, Network, NetworkId } from '@iota/iota-sdk/client';
import { getAddressUrl, getObjectUrl, getTransactionUrl, getValidatorUrl } from '.';
import { ExplorerLinkType } from '../enums';

export type ExplorerLinkConfig =
| {
type: ExplorerLinkType.Address;
address?: string;
useActiveAddress?: false;
}
| {
type: ExplorerLinkType.Address;
useActiveAddress: true;
}
| { type: ExplorerLinkType.Object; objectID: string; moduleName?: string }
| { type: ExplorerLinkType.Transaction; transactionID: string }
| { type: ExplorerLinkType.Validator; validator: string };

function getAddress(linkConfig: ExplorerLinkConfig, activeAddress: string | null) {
const { type } = linkConfig;
const isAddress = type === ExplorerLinkType.Address;
const isProvidedAddress = isAddress && !linkConfig.useActiveAddress;
return isProvidedAddress ? linkConfig.address : activeAddress;
}

export function getExplorerLink(
linkConfig: ExplorerLinkConfig,
activeAddress: string | null,
network: NetworkId,
) {
const { type } = linkConfig;
const address = getAddress(linkConfig, activeAddress);
const objectID = type === ExplorerLinkType.Object ? linkConfig.objectID : null;
const transactionID = type === ExplorerLinkType.Transaction ? linkConfig.transactionID : null;
const validator = type === ExplorerLinkType.Validator ? linkConfig.validator : null;
const moduleName = type === ExplorerLinkType.Object ? linkConfig.moduleName : null;

// fallback to localhost if customRPC is not set
const customExplorer =
network === Network.Custom ? getCustomNetwork().explorer : getNetwork(network).explorer;

if (!address) return null;
switch (type) {
case ExplorerLinkType.Address:
return address && getAddressUrl(address, network, customExplorer);
case ExplorerLinkType.Object:
return objectID && getObjectUrl(objectID, network, customExplorer, moduleName);
case ExplorerLinkType.Transaction:
return transactionID && getTransactionUrl(transactionID, network, customExplorer);
case ExplorerLinkType.Validator:
return validator && getValidatorUrl(validator, network, customExplorer);
}
}
47 changes: 47 additions & 0 deletions apps/core/src/utils/getExplorerPaths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Mysten Labs, Inc.
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { getNetwork, Network, NetworkId } from '@iota/iota-sdk/client';

function getExplorerUrl(
path: string,
network: NetworkId,
customExplorer: string,
getUrlWithDeviceId: (url: URL) => URL = (url) => url,
) {
const networkConfig = getNetwork(network);
const explorer = network === Network.Custom ? customExplorer : networkConfig?.explorer;

const url = getUrlWithDeviceId(new URL(path, explorer));
if (explorer) {
url.searchParams.append('network', network);
}

return url.href;
}

export function getObjectUrl(
objectID: string,
network: NetworkId,
customExplorer: string,
moduleName?: string | null,
) {
return getExplorerUrl(
`/object/${objectID}${moduleName ? `?module=${moduleName}` : ''}`,
network,
customExplorer,
);
}

export function getTransactionUrl(txDigest: string, network: NetworkId, customExplorer: string) {
return getExplorerUrl(`/txblock/${encodeURIComponent(txDigest)}`, network, customExplorer);
}

export function getAddressUrl(address: string, network: NetworkId, customExplorer: string) {
return getExplorerUrl(`/address/${address}`, network, customExplorer);
}

export function getValidatorUrl(address: string, network: NetworkId, customExplorer: string) {
return getExplorerUrl(`/validator/${address}`, network, customExplorer);
}
3 changes: 3 additions & 0 deletions apps/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export * from './filterAndSortTokenBalances';
export * from './getOwnerDisplay';
export * from './parseAmount';
export * from './parseObjectDetails';
export * from './api-env';
export * from './getExplorerPaths';
export * from './getExplorerLink';

export * from './stake';
export * from './transaction';
Expand Down
16 changes: 12 additions & 4 deletions apps/wallet-dashboard/components/tiles/AssetTileLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,39 @@ import { VisibilityOff } from '@iota/ui-icons';
import { VisualAssetTile } from '.';
import { IotaObjectData } from '@iota/iota-sdk/client';
import { NonVisualAssetCard } from './NonVisualAssetTile';
import { useExplorerLinkGenerator } from '@/hooks';
import { useExplorerLinkGetter } from '@/hooks';
import Link from 'next/link';
import { ExplorerLinkType } from '@iota/core';

interface AssetTileLinkProps {
asset: IotaObjectData;
type: AssetCategory;
}

export function AssetTileLink({ asset, type }: AssetTileLinkProps): React.JSX.Element {
const getExplorerLink = useExplorerLinkGenerator();
const getExplorerLink = useExplorerLinkGetter();
const linkProps = getAssetLinkProps(asset);

function getAssetLinkProps(asset: IotaObjectData): React.ComponentProps<typeof Link> {
if (type === AssetCategory.Visual) {
return { href: ASSETS_ROUTE.path + `/${asset.objectId}` };
} else {
const explorerLink =
getExplorerLink({
type: ExplorerLinkType.Object,
objectID: asset.objectId,
}) ?? '';

return {
href: getExplorerLink(asset.objectId),
href: explorerLink,
target: '_blank',
rel: 'noopener noreferrer',
};
}
}

return (
<Link {...getAssetLinkProps(asset)}>
<Link {...linkProps}>
{type === AssetCategory.Visual ? (
<VisualAssetTile asset={asset} icon={<VisibilityOff />} />
) : (
Expand Down
2 changes: 1 addition & 1 deletion apps/wallet-dashboard/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ export * from './useSendCoinTransaction';
export * from './useCreateSendAssetTransaction';
export * from './useGetCurrentEpochStartTimestamp';
export * from './useTimelockedUnstakeTransaction';
export * from './useExplorerLinkGenerator';
export * from './useExplorerLinkGetter';
12 changes: 0 additions & 12 deletions apps/wallet-dashboard/hooks/useExplorerLinkGenerator.ts

This file was deleted.

14 changes: 14 additions & 0 deletions apps/wallet-dashboard/hooks/useExplorerLinkGetter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { getExplorerLink } from '@iota/core';
import { useCurrentAccount, useIotaClientContext } from '@iota/dapp-kit';

export function useExplorerLinkGetter(): (
linkConfig: Parameters<typeof getExplorerLink>[0],
) => ReturnType<typeof getExplorerLink> {
const { network } = useIotaClientContext();
const address = useCurrentAccount()?.address || null;

return (linkConfig) => getExplorerLink(linkConfig, address, network);
}
2 changes: 1 addition & 1 deletion apps/wallet/src/dapp-interface/WalletStandardInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type {
SignTransactionRequest,
SignTransactionResponse,
} from '_payloads/transactions';
import { getCustomNetwork, type NetworkEnvType } from '_src/shared/api-env';
import { getCustomNetwork, type NetworkEnvType } from '@iota/core';
import { type SignMessageRequest } from '_src/shared/messaging/messages/payloads/transactions/SignMessage';
import { isWalletStatusChangePayload } from '_src/shared/messaging/messages/payloads/wallet-status-change';
import { getNetwork, Network, type ChainType } from '@iota/iota-sdk/client';
Expand Down
5 changes: 3 additions & 2 deletions apps/wallet/src/ui/app/components/explorer-link/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

import { ExternalLink } from '_components';
import type { ReactNode } from 'react';
import { useExplorerLink, type ExplorerLinkConfig } from '../../hooks/useExplorerLink';
import { type ExplorerLinkConfig, ExplorerLinkType } from '@iota/core';
import { useExplorerLink } from '_hooks';
import st from './ExplorerLink.module.scss';
import clsx from 'clsx';
import { ArrowTopRight } from '@iota/ui-icons';
Expand Down Expand Up @@ -43,6 +44,6 @@ export function ExplorerLink({
);
}

export * from './ExplorerLinkType';
export { ExplorerLinkType };

export default ExplorerLink;
1 change: 0 additions & 1 deletion apps/wallet/src/ui/app/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export * from './coin-icon';
export * from './error-boundary';
export * from './explorer-link';
export * from './explorer-link/Explorer';
export * from './explorer-link/ExplorerLinkType';
export * from './external-link';
export * from './iota-apps';
export * from './iota-apps/IotaApp';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { useNextMenuUrl, Overlay } from '_components';
import { useAppSelector } from '_hooks';
import { getCustomNetwork } from '_src/shared/api-env';
import { getCustomNetwork } from '@iota/core';
import { FAQ_LINK, ToS_LINK } from '_src/shared/constants';
import { formatAutoLock, useAutoLockMinutes } from '_src/ui/app/hooks/useAutoLockMinutes';
import FaucetRequestButton from '_src/ui/app/shared/faucet/FaucetRequestButton';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { useAppDispatch, useAppSelector } from '_hooks';
import { changeActiveNetwork } from '_redux/slices/app';
import { ampli } from '_src/shared/analytics/ampli';
import { getCustomNetwork } from '_src/shared/api-env';
import { getCustomNetwork } from '@iota/core';
import { getAllNetworks, Network, type NetworkConfiguration } from '@iota/iota-sdk/client';
import { AnimatePresence, motion } from 'framer-motion';
import { useEffect, useMemo, useState } from 'react';
Expand Down
1 change: 1 addition & 0 deletions apps/wallet/src/ui/app/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export { useGetTxnRecipientAddress } from './useGetTxnRecipientAddress';
export { useGetTransferAmount } from './useGetTransferAmount';
export { useOwnedNFT } from './useOwnedNFT';
export { useCopyToClipboard } from './useCopyToClipboard';
export * from './useExplorerLink';

export * from './useTransactionData';
export * from './useActiveAddress';
Expand Down
62 changes: 5 additions & 57 deletions apps/wallet/src/ui/app/hooks/useExplorerLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,13 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { getCustomNetwork } from '_src/shared/api-env';
import { getNetwork, Network } from '@iota/iota-sdk/client';
import { useMemo } from 'react';

import {
getAddressUrl,
getObjectUrl,
getTransactionUrl,
getValidatorUrl,
ExplorerLinkType,
} from '_components';
import { type ExplorerLinkConfig, getExplorerLink as useGetExplorerLink } from '@iota/core';
import { useActiveAddress } from './useActiveAddress';
import useAppSelector from './useAppSelector';

export type ExplorerLinkConfig =
| {
type: ExplorerLinkType.Address;
address?: string;
useActiveAddress?: false;
}
| {
type: ExplorerLinkType.Address;
useActiveAddress: true;
}
| { type: ExplorerLinkType.Object; objectID: string; moduleName?: string }
| { type: ExplorerLinkType.Transaction; transactionID: string }
| { type: ExplorerLinkType.Validator; validator: string };

function useAddress(linkConfig: ExplorerLinkConfig) {
const { type } = linkConfig;
const isAddress = type === ExplorerLinkType.Address;
const isProvidedAddress = isAddress && !linkConfig.useActiveAddress;
const activeAddress = useActiveAddress();
return isProvidedAddress ? linkConfig.address : activeAddress;
}

export function useExplorerLink(linkConfig: ExplorerLinkConfig) {
const { type } = linkConfig;
const address = useAddress(linkConfig);
const network = useAppSelector(({ app }) => app.network);
const objectID = type === ExplorerLinkType.Object ? linkConfig.objectID : null;
const transactionID = type === ExplorerLinkType.Transaction ? linkConfig.transactionID : null;
const validator = type === ExplorerLinkType.Validator ? linkConfig.validator : null;
const moduleName = type === ExplorerLinkType.Object ? linkConfig.moduleName : null;

// fallback to localhost if customRPC is not set
const customExplorer =
network === Network.Custom ? getCustomNetwork().explorer : getNetwork(network).explorer;
return useMemo(() => {
if (!address) return null;
switch (type) {
case ExplorerLinkType.Address:
return address && getAddressUrl(address, network, customExplorer);
case ExplorerLinkType.Object:
return objectID && getObjectUrl(objectID, network, customExplorer, moduleName);
case ExplorerLinkType.Transaction:
return transactionID && getTransactionUrl(transactionID, network, customExplorer);
case ExplorerLinkType.Validator:
return validator && getValidatorUrl(validator, network, customExplorer);
}
}, [type, address, network, customExplorer, moduleName, objectID, transactionID, validator]);
const app = useAppSelector(({ app }) => app);
const activeAddress = useActiveAddress();
const link = useGetExplorerLink(linkConfig, activeAddress, app.network);
return link;
}
2 changes: 1 addition & 1 deletion apps/wallet/src/ui/app/hooks/useInitialPageView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

import { ampli } from '_src/shared/analytics/ampli';
import { getCustomNetwork } from '_src/shared/api-env';
import { getCustomNetwork } from '@iota/core';
import { getNetwork } from '@iota/iota-sdk/client';
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0

import { useAppSelector } from '_hooks';
import { getCustomNetwork } from '_src/shared/api-env';
import { getCustomNetwork } from '@iota/core';
import { getNetwork } from '@iota/iota-sdk/client';
import { FaucetRateLimitError } from '@iota/iota-sdk/faucet';
import { toast } from 'react-hot-toast';
Expand Down

0 comments on commit 92f2afa

Please sign in to comment.