From 4074f2b85b3131a373981b58ebf8dc7ad595316f Mon Sep 17 00:00:00 2001 From: Panteleymonchuk Date: Fri, 15 Nov 2024 17:55:21 +0200 Subject: [PATCH 01/14] feat(wallet-dashboard): style selected visual Assets. --- apps/core/src/hooks/index.ts | 3 + .../src}/hooks/useFileExtensionType.ts | 0 .../ui/app => core/src}/hooks/useMediaUrl.ts | 0 .../app => core/src}/hooks/useNFTBasicData.ts | 2 +- apps/core/src/hooks/useNftDetails.ts | 89 ++++++++ .../ui/app => core/src}/hooks/useOwnedNFT.ts | 2 +- apps/core/src/utils/index.ts | 1 + .../src/utils}/truncateString.ts | 0 .../organisms/accordion/Accordion.tsx | 2 +- .../app/(protected)/assets/page.tsx | 21 +- .../components/AssetsList.tsx | 14 +- .../components/Collapsible/Collapsible.tsx | 41 ++++ .../Dialogs/Assets/AssetsDialog.tsx | 38 ++++ .../components/Dialogs/Assets/index.ts | 2 + .../Dialogs/Assets/views/DetailsView.tsx | 190 ++++++++++++++++++ .../Dialogs/Assets/views/SendView.tsx | 17 ++ .../components/Dialogs/Assets/views/index.ts | 4 + .../components/tiles/AssetTileLink.tsx | 36 +--- .../providers/AppProviders.tsx | 49 ++--- .../src/ui/app/helpers/formatAccountName.ts | 2 +- apps/wallet/src/ui/app/helpers/index.ts | 1 - apps/wallet/src/ui/app/hooks/index.ts | 4 - .../ui/app/pages/home/nft-details/index.tsx | 128 ++++-------- .../ui/app/pages/home/nft-transfer/index.tsx | 3 +- 24 files changed, 497 insertions(+), 152 deletions(-) rename apps/{wallet/src/ui/app => core/src}/hooks/useFileExtensionType.ts (100%) rename apps/{wallet/src/ui/app => core/src}/hooks/useMediaUrl.ts (100%) rename apps/{wallet/src/ui/app => core/src}/hooks/useNFTBasicData.ts (92%) create mode 100644 apps/core/src/hooks/useNftDetails.ts rename apps/{wallet/src/ui/app => core/src}/hooks/useOwnedNFT.ts (94%) rename apps/{wallet/src/ui/app/helpers => core/src/utils}/truncateString.ts (100%) create mode 100644 apps/wallet-dashboard/components/Collapsible/Collapsible.tsx create mode 100644 apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx create mode 100644 apps/wallet-dashboard/components/Dialogs/Assets/index.ts create mode 100644 apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx create mode 100644 apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx create mode 100644 apps/wallet-dashboard/components/Dialogs/Assets/views/index.ts diff --git a/apps/core/src/hooks/index.ts b/apps/core/src/hooks/index.ts index 2ffa7f95688..686ab13dce8 100644 --- a/apps/core/src/hooks/index.ts +++ b/apps/core/src/hooks/index.ts @@ -42,5 +42,8 @@ export * from './useTransactionData'; export * from './useGetStakingValidatorDetails'; export * from './useCursorPagination'; export * from './useTheme'; +export * from './useNFTBasicData'; +export * from './useOwnedNFT'; +export * from './useNftDetails'; export * from './stake'; diff --git a/apps/wallet/src/ui/app/hooks/useFileExtensionType.ts b/apps/core/src/hooks/useFileExtensionType.ts similarity index 100% rename from apps/wallet/src/ui/app/hooks/useFileExtensionType.ts rename to apps/core/src/hooks/useFileExtensionType.ts diff --git a/apps/wallet/src/ui/app/hooks/useMediaUrl.ts b/apps/core/src/hooks/useMediaUrl.ts similarity index 100% rename from apps/wallet/src/ui/app/hooks/useMediaUrl.ts rename to apps/core/src/hooks/useMediaUrl.ts diff --git a/apps/wallet/src/ui/app/hooks/useNFTBasicData.ts b/apps/core/src/hooks/useNFTBasicData.ts similarity index 92% rename from apps/wallet/src/ui/app/hooks/useNFTBasicData.ts rename to apps/core/src/hooks/useNFTBasicData.ts index 2b9360bf94a..227a9707de1 100644 --- a/apps/wallet/src/ui/app/hooks/useNFTBasicData.ts +++ b/apps/core/src/hooks/useNFTBasicData.ts @@ -7,7 +7,7 @@ import type { IotaObjectData } from '@iota/iota-sdk/client'; import useFileExtensionType from './useFileExtensionType'; import useMediaUrl from './useMediaUrl'; -export default function useNFTBasicData(nftObj: IotaObjectData | null) { +export function useNFTBasicData(nftObj: IotaObjectData | null) { const nftObjectID = nftObj?.objectId || null; const filePath = useMediaUrl(nftObj?.content || null); let objType = null; diff --git a/apps/core/src/hooks/useNftDetails.ts b/apps/core/src/hooks/useNftDetails.ts new file mode 100644 index 00000000000..63c96d45bb4 --- /dev/null +++ b/apps/core/src/hooks/useNftDetails.ts @@ -0,0 +1,89 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +import { useGetNFTMeta, useOwnedNFT, useNFTBasicData, useGetKioskContents } from './'; +import { formatAddress } from '@iota/iota-sdk/utils'; +import { isAssetTransferable, truncateString } from '../utils'; + +type NftFields = { + metadata?: { fields?: { attributes?: { fields?: { keys: string[]; values: string[] } } } }; +}; + +export function useNftDetails(nftId: string, accountAddress: string | null) { + const { data: objectData, isPending: isNftLoading } = useOwnedNFT(nftId || '', accountAddress); + const { data } = useGetKioskContents(accountAddress); + + const isContainedInKiosk = data?.lookup.get(nftId!); + const kioskItem = data?.list.find((k) => k.data?.objectId === nftId); + + const isTransferable = isAssetTransferable(objectData); + + const { nftFields } = useNFTBasicData(objectData); + + const { data: nftMeta, isPending: isPendingMeta } = useGetNFTMeta(nftId); + + const nftName = nftMeta?.name || formatAddress(nftId); + const nftImageUrl = nftMeta?.imageUrl || ''; + + // Extract either the attributes, or use the top-level NFT fields: + const metaFields = + (nftFields as NftFields)?.metadata?.fields?.attributes?.fields || + Object.entries(nftFields ?? {}) + .filter(([key]) => key !== 'id') + .reduce( + (acc, [key, value]) => { + acc.keys.push(key); + acc.values.push(value as string); + return acc; + }, + { keys: [] as string[], values: [] as string[] }, + ); + const metaKeys: string[] = metaFields ? metaFields.keys : []; + const metaValues = metaFields ? metaFields.values : []; + + const ownerAddress = + (objectData?.owner && + typeof objectData?.owner === 'object' && + 'AddressOwner' in objectData.owner && + objectData.owner.AddressOwner) || + ''; + + function formatMetaValue(value: string | object) { + if (typeof value === 'object') { + return { + value: JSON.stringify(value), + valueLink: undefined, + }; + } else { + if (value.includes('http')) { + return { + value: value.startsWith('http') + ? truncateString(value, 20, 8) + : formatAddress(value), + valueLink: value, + }; + } + return { + value: value, + valueLink: undefined, + }; + } + } + + return { + objectData, + isNftLoading, + nftName, + nftImageUrl, + ownerAddress, + isTransferable, + metaKeys, + metaValues, + formatMetaValue, + + isContainedInKiosk, + kioskItem, + + nftMeta, + isPendingMeta, + }; +} diff --git a/apps/wallet/src/ui/app/hooks/useOwnedNFT.ts b/apps/core/src/hooks/useOwnedNFT.ts similarity index 94% rename from apps/wallet/src/ui/app/hooks/useOwnedNFT.ts rename to apps/core/src/hooks/useOwnedNFT.ts index 0e308a484ce..a7f96ac25d2 100644 --- a/apps/wallet/src/ui/app/hooks/useOwnedNFT.ts +++ b/apps/core/src/hooks/useOwnedNFT.ts @@ -2,7 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { useGetKioskContents, useGetObject } from '@iota/core'; +import { useGetKioskContents, useGetObject } from './'; import { useMemo } from 'react'; export function useOwnedNFT(nftObjectId: string | null, address: string | null) { diff --git a/apps/core/src/utils/index.ts b/apps/core/src/utils/index.ts index 8cbb31afa73..acb9f5eb473 100644 --- a/apps/core/src/utils/index.ts +++ b/apps/core/src/utils/index.ts @@ -21,6 +21,7 @@ export * from './getDelegationDataByStakeId'; export * from './api-env'; export * from './getExplorerPaths'; export * from './getExplorerLink'; +export * from './truncateString'; export * from './stake'; export * from './transaction'; diff --git a/apps/wallet/src/ui/app/helpers/truncateString.ts b/apps/core/src/utils/truncateString.ts similarity index 100% rename from apps/wallet/src/ui/app/helpers/truncateString.ts rename to apps/core/src/utils/truncateString.ts diff --git a/apps/ui-kit/src/lib/components/organisms/accordion/Accordion.tsx b/apps/ui-kit/src/lib/components/organisms/accordion/Accordion.tsx index ce4d80f97c6..1489c52c1d5 100644 --- a/apps/ui-kit/src/lib/components/organisms/accordion/Accordion.tsx +++ b/apps/ui-kit/src/lib/components/organisms/accordion/Accordion.tsx @@ -7,7 +7,7 @@ import { ArrowDown } from '@iota/ui-icons'; import { Button, ButtonType } from '@/lib'; import { ICON_STYLE } from './accordion.classes'; -interface AccordionHeaderProps { +export interface AccordionHeaderProps { /** * Flag for show/hide content */ diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index 00fe1d57e24..af060bd09d8 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -11,6 +11,7 @@ import { IotaObjectData } from '@iota/iota-sdk/client'; import { useState } from 'react'; import { AssetCategory } from '@/lib/enums'; import { AssetList } from '@/components/AssetsList'; +import { AssetsDialog } from '@/components/Dialogs/Assets/AssetsDialog'; const PAGINATION_RANGE = [20, 40, 60]; @@ -28,6 +29,7 @@ const ASSET_CATEGORIES: { label: string; value: AssetCategory }[] = [ export default function AssetsDashboardPage(): React.JSX.Element { const [selectedCategory, setSelectedCategory] = useState(AssetCategory.Visual); const [limit, setLimit] = useState(PAGINATION_RANGE[1]); + const [selectedAsset, setSelectedAsset] = useState(null); const account = useCurrentAccount(); const ownedObjectsQuery = useGetOwnedObjects(account?.address, undefined, limit); @@ -62,6 +64,14 @@ export default function AssetsDashboardPage(): React.JSX.Element { const assetList = categoryToAsset[selectedCategory]; + function handleClickAsset(asset: IotaObjectData) { + setSelectedAsset(asset); + } + + function handleCloseDialog() { + setSelectedAsset(null); + } + return ( @@ -77,7 +87,11 @@ export default function AssetsDashboardPage(): React.JSX.Element { ))} </div> - <AssetList assets={assetList} selectedCategory={selectedCategory} /> + <AssetList + assets={assetList} + selectedCategory={selectedCategory} + onClick={handleClickAsset} + /> <div className="flex flex-row items-center justify-end py-xs"> <PaginationOptions pagination={pagination} @@ -92,6 +106,11 @@ export default function AssetsDashboardPage(): React.JSX.Element { } /> </div> + <AssetsDialog + isOpen={!!selectedAsset} + handleClose={handleCloseDialog} + asset={selectedAsset} + /> </div> </Panel> ); diff --git a/apps/wallet-dashboard/components/AssetsList.tsx b/apps/wallet-dashboard/components/AssetsList.tsx index ca14660daff..0fb8926a030 100644 --- a/apps/wallet-dashboard/components/AssetsList.tsx +++ b/apps/wallet-dashboard/components/AssetsList.tsx @@ -8,6 +8,7 @@ import { AssetTileLink } from '@/components'; interface AssetListProps { assets: IotaObjectData[]; selectedCategory: AssetCategory; + onClick: (asset: IotaObjectData) => void; } const ASSET_LAYOUT: Record<AssetCategory, string> = { @@ -16,11 +17,20 @@ const ASSET_LAYOUT: Record<AssetCategory, string> = { [AssetCategory.Other]: 'flex flex-col overflow-auto py-sm', }; -export function AssetList({ assets, selectedCategory }: AssetListProps): React.JSX.Element { +export function AssetList({ + assets, + selectedCategory, + onClick, +}: AssetListProps): React.JSX.Element { return ( <div className={ASSET_LAYOUT[selectedCategory]}> {assets.map((asset) => ( - <AssetTileLink key={asset.digest} asset={asset} type={selectedCategory} /> + <AssetTileLink + key={asset.digest} + asset={asset} + type={selectedCategory} + onClick={onClick} + /> ))} </div> ); diff --git a/apps/wallet-dashboard/components/Collapsible/Collapsible.tsx b/apps/wallet-dashboard/components/Collapsible/Collapsible.tsx new file mode 100644 index 00000000000..28989df53d2 --- /dev/null +++ b/apps/wallet-dashboard/components/Collapsible/Collapsible.tsx @@ -0,0 +1,41 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +import { PropsWithChildren, useState } from 'react'; +import { + AccordionHeaderProps, + Accordion, + AccordionHeader, + AccordionContent, + TitleSize, + Title, +} from '@iota/apps-ui-kit'; + +type CollapsibleProps = { + title: string; + hideBorder?: boolean; + defaultExpanded?: boolean; + headerProps?: AccordionHeaderProps; + titleSize?: TitleSize; +}; + +export function Collapsible({ + title, + children, + defaultExpanded = false, + hideBorder, + titleSize = TitleSize.Small, +}: PropsWithChildren<CollapsibleProps>) { + const [isExpanded, setIsExpanded] = useState(defaultExpanded); + return ( + <Accordion hideBorder={hideBorder}> + <AccordionHeader + hideBorder={hideBorder} + isExpanded={isExpanded} + onToggle={() => setIsExpanded(!isExpanded)} + > + <Title size={titleSize} title={title} /> + </AccordionHeader> + <AccordionContent isExpanded={isExpanded}>{children}</AccordionContent> + </Accordion> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx new file mode 100644 index 00000000000..130a69e449b --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx @@ -0,0 +1,38 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { Dialog } from '@iota/apps-ui-kit'; +import { DetailsView } from './views'; +import { IotaObjectData } from '@iota/iota-sdk/client'; + +export enum AssetsDialogView { + Details, + Send, +} + +interface AssetsDialogProps { + isOpen: boolean; + handleClose: () => void; + asset: IotaObjectData | null; +} + +export function AssetsDialog({ isOpen, handleClose, asset }: AssetsDialogProps): JSX.Element { + const [view, setView] = React.useState<AssetsDialogView>(AssetsDialogView.Details); + + function handleDetailsSend() { + setView(AssetsDialogView.Send); + } + + return ( + <Dialog open={isOpen} onOpenChange={() => handleClose()}> + {view === AssetsDialogView.Details && asset && ( + <DetailsView + asset={asset} + handleClose={handleClose} + handleSend={handleDetailsSend} + /> + )} + </Dialog> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/index.ts b/apps/wallet-dashboard/components/Dialogs/Assets/index.ts new file mode 100644 index 00000000000..507ff4f98be --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Assets/index.ts @@ -0,0 +1,2 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx new file mode 100644 index 00000000000..299f7188be1 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx @@ -0,0 +1,190 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { ExplorerLinkType, useNftDetails } from '@iota/core'; +import { + Button, + ButtonType, + Header, + KeyValueInfo, + VisualAssetCard, + VisualAssetType, +} from '@iota/apps-ui-kit'; +import Link from 'next/link'; +import { formatAddress } from '@iota/iota-sdk/utils'; +import { Layout, LayoutBody, LayoutFooter } from '../../Staking/views/Layout'; +import { IotaObjectData } from '@iota/iota-sdk/client'; +import { Collapsible } from '@/components/Collapsible/Collapsible'; +import { ExplorerLink } from '@/components/ExplorerLink'; +import { useCurrentAccount } from '@iota/dapp-kit'; + +interface DetailsViewProps { + asset: IotaObjectData; + handleClose: () => void; + handleSend: () => void; +} + +export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps) { + const account = useCurrentAccount(); + + const senderAddress = account?.address ?? ''; + const objectId = asset.objectId; + + const { + nftName, + nftImageUrl, + nftMeta, + ownerAddress, + isTransferable, + metaKeys, + metaValues, + formatMetaValue, + isContainedInKiosk, + kioskItem, + } = useNftDetails(objectId, senderAddress); + + function handleMoreAboutKiosk() { + window.open('https://wiki.iota.org/', '_blank'); + } + + function handleMarketplace() { + window.open('https://wiki.iota.org/', '_blank'); + } + + return ( + <Layout> + <Header title="Asset" onClose={handleClose} /> + <LayoutBody> + <div className="flex w-full flex-col items-center justify-center gap-xs"> + <div className="w-[172px]"> + <VisualAssetCard + assetSrc={nftImageUrl} + assetTitle={nftName} + assetType={VisualAssetType.Image} + altText={nftName || 'NFT'} + isHoverable={false} + /> + </div> + <ExplorerLink + address={senderAddress} + linkProps={{ type: ExplorerLinkType.Object, objectID: objectId }} + > + <Button type={ButtonType.Ghost} text="View on Explorer" /> + </ExplorerLink> + <div className="flex w-full flex-col gap-md"> + <div className="flex flex-col gap-xxxs"> + <span className="text-title-lg text-neutral-10">{nftMeta?.name}</span> + {nftMeta?.description ? ( + <span className="text-body-md text-neutral-60"> + {nftMeta?.description} + </span> + ) : null} + </div> + + {(nftMeta?.projectUrl || !!nftMeta?.creator) && ( + <div className="flex flex-col gap-xs"> + {nftMeta?.projectUrl && ( + <KeyValueInfo + keyText="Website" + value={ + <Link href={nftMeta?.projectUrl}> + {nftMeta?.projectUrl} + </Link> + } + fullwidth + /> + )} + {nftMeta?.creator && ( + <KeyValueInfo + keyText="Creator" + value={nftMeta?.creator ?? '-'} + fullwidth + /> + )} + </div> + )} + + <Collapsible defaultExpanded title="Details"> + <div className="flex flex-col gap-xs px-md pb-xs pt-sm"> + {ownerAddress && ( + <KeyValueInfo + keyText="Owner" + value={ + <ExplorerLink + linkProps={{ type: ExplorerLinkType.Address }} + address={ownerAddress} + > + {formatAddress(ownerAddress)} + </ExplorerLink> + } + fullwidth + /> + )} + {objectId && ( + <KeyValueInfo + keyText="Object ID" + value={formatAddress(objectId)} + fullwidth + /> + )} + </div> + </Collapsible> + {metaKeys.length ? ( + <Collapsible defaultExpanded title="Attributes"> + <div className="flex flex-col gap-xs px-md pb-xs pt-sm"> + {metaKeys.map((aKey, idx) => { + const { value, valueLink } = formatMetaValue( + metaValues[idx], + ); + return ( + <KeyValueInfo + key={idx} + keyText={aKey} + value={ + valueLink ? ( + <Link key={aKey} href={valueLink || ''}> + {value} + </Link> + ) : ( + value + ) + } + fullwidth + /> + ); + })} + </div> + </Collapsible> + ) : null} + </div> + </div> + </LayoutBody> + <LayoutFooter> + <div className="flex flex-col"> + {isContainedInKiosk && kioskItem?.isLocked ? ( + <div className="flex flex-col gap-2"> + <Button + type={ButtonType.Secondary} + onClick={handleMoreAboutKiosk} + text="Learn more about Kiosks" + /> + <Button + type={ButtonType.Primary} + onClick={handleMarketplace} + text="Marketplace" + /> + </div> + ) : ( + <Button + disabled={!isTransferable} + onClick={handleSend} + text="Send" + fullWidth + /> + )} + </div> + </LayoutFooter> + </Layout> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx new file mode 100644 index 00000000000..31e13461195 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx @@ -0,0 +1,17 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { Layout, LayoutBody } from '../../Staking/views/Layout'; + +export function SendView() { + return ( + <Layout> + <LayoutBody> + <div className="flex w-full flex-col items-center justify-center gap-xs"> + Send view + </div> + </LayoutBody> + </Layout> + ); +} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/index.ts b/apps/wallet-dashboard/components/Dialogs/Assets/views/index.ts new file mode 100644 index 00000000000..5623d78737d --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './DetailsView'; diff --git a/apps/wallet-dashboard/components/tiles/AssetTileLink.tsx b/apps/wallet-dashboard/components/tiles/AssetTileLink.tsx index 3bf06075604..64c4d805513 100644 --- a/apps/wallet-dashboard/components/tiles/AssetTileLink.tsx +++ b/apps/wallet-dashboard/components/tiles/AssetTileLink.tsx @@ -3,50 +3,30 @@ 'use client'; -import { ASSETS_ROUTE } from '@/lib/constants/routes.constants'; import { AssetCategory } from '@/lib/enums'; import { VisibilityOff } from '@iota/ui-icons'; import { VisualAssetTile } from '.'; import { IotaObjectData } from '@iota/iota-sdk/client'; import { NonVisualAssetCard } from './NonVisualAssetTile'; -import { useExplorerLinkGetter } from '@/hooks'; -import Link from 'next/link'; -import { ExplorerLinkType } from '@iota/core'; interface AssetTileLinkProps { asset: IotaObjectData; type: AssetCategory; + onClick: (asset: IotaObjectData) => void; } -export function AssetTileLink({ asset, type }: AssetTileLinkProps): React.JSX.Element { - 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: explorerLink, - target: '_blank', - rel: 'noopener noreferrer', - }; - } +export function AssetTileLink({ asset, type, onClick }: AssetTileLinkProps): React.JSX.Element { + function handleClick() { + onClick(asset); } return ( - <Link {...linkProps}> + <> {type === AssetCategory.Visual ? ( - <VisualAssetTile asset={asset} icon={<VisibilityOff />} /> + <VisualAssetTile asset={asset} icon={<VisibilityOff />} onClick={handleClick} /> ) : ( - <NonVisualAssetCard asset={asset} /> + <NonVisualAssetCard asset={asset} onClick={handleClick} /> )} - </Link> + </> ); } diff --git a/apps/wallet-dashboard/providers/AppProviders.tsx b/apps/wallet-dashboard/providers/AppProviders.tsx index 1cdcf6da752..267427a4e34 100644 --- a/apps/wallet-dashboard/providers/AppProviders.tsx +++ b/apps/wallet-dashboard/providers/AppProviders.tsx @@ -9,6 +9,7 @@ import { IotaClientProvider, lightTheme, darkTheme, WalletProvider } from '@iota import { getAllNetworks, getDefaultNetwork } from '@iota/iota-sdk/client'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useState } from 'react'; +import { KioskClientProvider } from '@iota/core'; import { growthbook } from '@/lib/utils'; import { Popup } from '@/components/Popup'; import { Toaster } from 'react-hot-toast'; @@ -25,29 +26,31 @@ export function AppProviders({ children }: React.PropsWithChildren) { <GrowthBookProvider growthbook={growthbook}> <QueryClientProvider client={queryClient}> <IotaClientProvider networks={allNetworks} defaultNetwork={defaultNetwork}> - <WalletProvider - theme={[ - { - variables: lightTheme, - }, - { - selector: '.dark', - variables: darkTheme, - }, - ]} - > - <ThemeProvider appId="dashboard"> - <PopupProvider> - {children} - <Toaster - containerStyle={{ - zIndex: 99999, - }} - /> - <Popup /> - </PopupProvider> - </ThemeProvider> - </WalletProvider> + <KioskClientProvider> + <WalletProvider + theme={[ + { + variables: lightTheme, + }, + { + selector: '.dark', + variables: darkTheme, + }, + ]} + > + <ThemeProvider appId="dashboard"> + <PopupProvider> + {children} + <Toaster + containerStyle={{ + zIndex: 99999, + }} + /> + <Popup /> + </PopupProvider> + </ThemeProvider> + </WalletProvider> + </KioskClientProvider> </IotaClientProvider> </QueryClientProvider> </GrowthBookProvider> diff --git a/apps/wallet/src/ui/app/helpers/formatAccountName.ts b/apps/wallet/src/ui/app/helpers/formatAccountName.ts index dff1c73ba80..c512a363841 100644 --- a/apps/wallet/src/ui/app/helpers/formatAccountName.ts +++ b/apps/wallet/src/ui/app/helpers/formatAccountName.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { formatAddress } from '@iota/iota-sdk/utils'; -import { truncateString } from './truncateString'; +import { truncateString } from '@iota/core'; export function formatAccountName( nickname: string | undefined | null, diff --git a/apps/wallet/src/ui/app/helpers/index.ts b/apps/wallet/src/ui/app/helpers/index.ts index aa14d5f7511..fde8b2c4f27 100644 --- a/apps/wallet/src/ui/app/helpers/index.ts +++ b/apps/wallet/src/ui/app/helpers/index.ts @@ -7,5 +7,4 @@ export { default as notEmpty } from './notEmptyCheck'; // export { getEventsSummary } from './getEventsSummary'; export { getAmount } from './getAmount'; export { checkStakingTxn } from './checkStakingTxn'; -export { truncateString } from './truncateString'; export { formatAccountName } from './formatAccountName'; diff --git a/apps/wallet/src/ui/app/hooks/index.ts b/apps/wallet/src/ui/app/hooks/index.ts index 33244c68262..f788f5dce44 100644 --- a/apps/wallet/src/ui/app/hooks/index.ts +++ b/apps/wallet/src/ui/app/hooks/index.ts @@ -6,15 +6,11 @@ export { default as useAppDispatch } from './useAppDispatch'; export { default as useAppSelector } from './useAppSelector'; export { default as useInitializedGuard } from './useInitializedGuard'; export { default as useFullscreenGuard } from './useFullscreenGuard'; -export { default as useMediaUrl } from './useMediaUrl'; export { default as useOnClickOutside } from './useOnClickOutside'; export { default as useOnKeyboardEvent } from './useOnKeyboardEvent'; -export { default as useFileExtensionType } from './useFileExtensionType'; -export { default as useNFTBasicData } from './useNFTBasicData'; export { useTransactionDryRun } from './useTransactionDryRun'; export { useGetTxnRecipientAddress } from './useGetTxnRecipientAddress'; export { useGetTransferAmount } from './useGetTransferAmount'; -export { useOwnedNFT } from './useOwnedNFT'; export { useCopyToClipboard } from './useCopyToClipboard'; export * from './useExplorerLink'; diff --git a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx index ab9508a19f0..451184573b0 100644 --- a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx @@ -5,57 +5,36 @@ import { useActiveAddress } from '_app/hooks/useActiveAddress'; import { Collapsible } from '_app/shared/collapse'; import { ExplorerLink, ExplorerLinkType, Loading, NFTDisplayCard, PageTemplate } from '_components'; -import { useNFTBasicData, useOwnedNFT } from '_hooks'; import { useUnlockedGuard } from '_src/ui/app/hooks/useUnlockedGuard'; -import { isAssetTransferable, useGetKioskContents, useGetNFTMeta } from '@iota/core'; +import { useNFTBasicData, useNftDetails } from '@iota/core'; import { formatAddress } from '@iota/iota-sdk/utils'; import cl from 'clsx'; import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom'; import { Button, ButtonType, KeyValueInfo } from '@iota/apps-ui-kit'; -import { truncateString } from '_src/ui/app/helpers'; - -type NftFields = { - metadata?: { fields?: { attributes?: { fields?: { keys: string[]; values: string[] } } } }; -}; function NFTDetailsPage() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const nftId = searchParams.get('objectId'); const accountAddress = useActiveAddress(); - const { data: objectData, isPending: isNftLoading } = useOwnedNFT(nftId || '', accountAddress); - const isTransferable = isAssetTransferable(objectData); - const { nftFields, fileExtensionType, filePath } = useNFTBasicData(objectData); - const address = useActiveAddress(); - const { data } = useGetKioskContents(address); + const { + nftMeta, + isPendingMeta, + isNftLoading, + ownerAddress, + objectData, + isTransferable, + metaKeys, + metaValues, + formatMetaValue, + isContainedInKiosk, + kioskItem, + } = useNftDetails(nftId || '', accountAddress); - const isContainedInKiosk = data?.lookup.get(nftId!); - const kioskItem = data?.list.find((k) => k.data?.objectId === nftId); + const { fileExtensionType, filePath } = useNFTBasicData(objectData); - // Extract either the attributes, or use the top-level NFT fields: - const metaFields = - (nftFields as NftFields)?.metadata?.fields?.attributes?.fields || - Object.entries(nftFields ?? {}) - .filter(([key]) => key !== 'id') - .reduce( - (acc, [key, value]) => { - acc.keys.push(key); - acc.values.push(value as string); - return acc; - }, - { keys: [] as string[], values: [] as string[] }, - ); - const metaKeys: string[] = metaFields ? metaFields.keys : []; - const metaValues = metaFields ? metaFields.values : []; - const { data: nftDisplayData, isPending: isPendingDisplay } = useGetNFTMeta(nftId || ''); - const ownerAddress = - (objectData?.owner && - typeof objectData?.owner === 'object' && - 'AddressOwner' in objectData.owner && - objectData.owner.AddressOwner) || - ''; const isGuardLoading = useUnlockedGuard(); - const isPending = isNftLoading || isPendingDisplay || isGuardLoading; + const isPending = isNftLoading || isPendingMeta || isGuardLoading; function handleMoreAboutKiosk() { window.open('https://docs.iota.org/references/ts-sdk/kiosk/', '_blank'); @@ -70,28 +49,6 @@ function NFTDetailsPage() { navigate(`/nft-transfer/${nftId}`); } - function formatMetaValue(value: string | object) { - if (typeof value === 'object') { - return { - value: JSON.stringify(value), - valueLink: undefined, - }; - } else { - if (value.includes('http')) { - return { - value: value.startsWith('http') - ? truncateString(value, 20, 8) - : formatAddress(value), - valueLink: value, - }; - } - return { - value: value, - valueLink: undefined, - }; - } - } - return ( <PageTemplate title="Visual Asset" @@ -126,39 +83,36 @@ function NFTDetailsPage() { <div className="flex flex-col gap-md"> <div className="flex flex-col gap-xxxs"> <span className="text-title-lg text-neutral-10 dark:text-neutral-92"> - {nftDisplayData?.name} + {nftMeta?.name} </span> - {nftDisplayData?.description ? ( + {nftMeta?.description ? ( <span className="text-body-md text-neutral-60"> - {nftDisplayData?.description} + {nftMeta?.description} </span> ) : null} </div> - {nftDisplayData?.projectUrl || - (nftDisplayData?.creator && ( - <div className="flex flex-col gap-xs"> - {nftDisplayData?.projectUrl && ( - <KeyValueInfo - keyText="Website" - value={ - <Link - to={nftDisplayData?.projectUrl} - > - {nftDisplayData?.projectUrl} - </Link> - } - fullwidth - /> - )} - {nftDisplayData?.creator && ( - <KeyValueInfo - keyText="Creator" - value={nftDisplayData?.creator ?? '-'} - fullwidth - /> - )} - </div> - ))} + {(nftMeta?.projectUrl || nftMeta?.creator) && ( + <div className="flex flex-col gap-xs"> + {nftMeta?.projectUrl && ( + <KeyValueInfo + keyText="Website" + value={ + <Link to={nftMeta?.projectUrl}> + {nftMeta?.projectUrl} + </Link> + } + fullwidth + /> + )} + {nftMeta?.creator && ( + <KeyValueInfo + keyText="Creator" + value={nftMeta?.creator ?? '-'} + fullwidth + /> + )} + </div> + )} </div> <div className="flex flex-col gap-md"> <Collapsible defaultOpen title="Details"> diff --git a/apps/wallet/src/ui/app/pages/home/nft-transfer/index.tsx b/apps/wallet/src/ui/app/pages/home/nft-transfer/index.tsx index d8dcaf5af50..2675b3aa1f3 100644 --- a/apps/wallet/src/ui/app/pages/home/nft-transfer/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/nft-transfer/index.tsx @@ -4,11 +4,10 @@ import { useActiveAddress } from '_app/hooks/useActiveAddress'; import { Loading, NFTDisplayCard, Overlay } from '_components'; -import { useOwnedNFT } from '_hooks'; import { useUnlockedGuard } from '_src/ui/app/hooks/useUnlockedGuard'; import { Navigate, useNavigate, useParams } from 'react-router-dom'; import { TransferNFTForm } from './TransferNFTForm'; -import { isAssetTransferable } from '@iota/core'; +import { isAssetTransferable, useOwnedNFT } from '@iota/core'; function NftTransferPage() { const { nftId } = useParams(); From 9bb5dd09d83997e43e1b3ca099e1482550225436 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Mon, 25 Nov 2024 19:54:54 +0200 Subject: [PATCH 02/14] refactor(core): destructure metaKeys and metaValues from attributes --- apps/core/src/hooks/useNftDetails.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/core/src/hooks/useNftDetails.ts b/apps/core/src/hooks/useNftDetails.ts index 63c96d45bb4..f28d27192a9 100644 --- a/apps/core/src/hooks/useNftDetails.ts +++ b/apps/core/src/hooks/useNftDetails.ts @@ -25,7 +25,7 @@ export function useNftDetails(nftId: string, accountAddress: string | null) { const nftImageUrl = nftMeta?.imageUrl || ''; // Extract either the attributes, or use the top-level NFT fields: - const metaFields = + const { keys: metaKeys, values: metaValues } = (nftFields as NftFields)?.metadata?.fields?.attributes?.fields || Object.entries(nftFields ?? {}) .filter(([key]) => key !== 'id') @@ -37,8 +37,6 @@ export function useNftDetails(nftId: string, accountAddress: string | null) { }, { keys: [] as string[], values: [] as string[] }, ); - const metaKeys: string[] = metaFields ? metaFields.keys : []; - const metaValues = metaFields ? metaFields.values : []; const ownerAddress = (objectData?.owner && @@ -79,10 +77,8 @@ export function useNftDetails(nftId: string, accountAddress: string | null) { metaKeys, metaValues, formatMetaValue, - isContainedInKiosk, kioskItem, - nftMeta, isPendingMeta, }; From 1f2eb82d5631ae1c132f73c06a012fd00e9efcf3 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Mon, 25 Nov 2024 20:17:53 +0200 Subject: [PATCH 03/14] refactor(wallet): move Collapsible component to core. --- .../components/collapsible/Collapsible.tsx | 51 +++++++++++++++++++ apps/core/src/components/collapsible/index.ts | 4 ++ apps/core/src/components/index.ts | 2 +- .../Dialogs/Assets/AssetsDialog.tsx | 21 ++++---- .../components/Dialogs/Assets/hooks/index.ts | 4 ++ .../Dialogs/Assets/hooks/useAssetsDialog.ts | 18 +++++++ .../Dialogs/Assets/views/DetailsView.tsx | 8 +-- .../components/Dialogs/Assets/views/index.ts | 1 + .../pages/accounts/manage/AccountGroup.tsx | 3 +- .../transaction-request/GasFees.tsx | 3 +- .../TransactionDetails/Command.tsx | 2 +- .../TransactionDetails/index.tsx | 2 +- .../ui/app/pages/home/kiosk-details/index.tsx | 3 +- .../ui/app/pages/home/nft-details/index.tsx | 3 +- .../ui/app/pages/home/tokens/TokenList.tsx | 2 +- .../cards/ObjectChanges.tsx | 2 +- 16 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 apps/core/src/components/collapsible/Collapsible.tsx create mode 100644 apps/core/src/components/collapsible/index.ts create mode 100644 apps/wallet-dashboard/components/Dialogs/Assets/hooks/index.ts create mode 100644 apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts diff --git a/apps/core/src/components/collapsible/Collapsible.tsx b/apps/core/src/components/collapsible/Collapsible.tsx new file mode 100644 index 00000000000..c44607bc686 --- /dev/null +++ b/apps/core/src/components/collapsible/Collapsible.tsx @@ -0,0 +1,51 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Accordion, AccordionContent, AccordionHeader, Title, TitleSize } from '@iota/apps-ui-kit'; +import { useState, type ReactNode } from 'react'; + +interface CollapsibleProps { + title?: string; + defaultOpen?: boolean; + children: ReactNode | ReactNode[]; + isOpen?: boolean; + onOpenChange?: (isOpen: boolean) => void; + titleSize?: TitleSize; + render?: ({ isOpen }: { isOpen: boolean }) => ReactNode; + hideArrow?: boolean; + hideBorder?: boolean; +} + +export function Collapsible({ + title = '', + children, + defaultOpen, + isOpen, + onOpenChange, + titleSize = TitleSize.Small, + render, + hideArrow, + hideBorder, +}: CollapsibleProps) { + const [open, setOpen] = useState(isOpen ?? defaultOpen ?? false); + + function handleOpenChange(isOpen: boolean) { + setOpen(isOpen); + onOpenChange?.(isOpen); + } + + return ( + <Accordion hideBorder={hideBorder}> + <AccordionHeader + hideBorder={hideBorder} + hideArrow={hideArrow} + isExpanded={isOpen ?? open} + onToggle={() => handleOpenChange(!open)} + > + {render ? render({ isOpen: open }) : <Title size={titleSize} title={title} />} + </AccordionHeader> + <AccordionContent isExpanded={isOpen ?? open}>{children}</AccordionContent> + </Accordion> + ); +} diff --git a/apps/core/src/components/collapsible/index.ts b/apps/core/src/components/collapsible/index.ts new file mode 100644 index 00000000000..dbcea5060fa --- /dev/null +++ b/apps/core/src/components/collapsible/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './Collapsible'; diff --git a/apps/core/src/components/index.ts b/apps/core/src/components/index.ts index a2aa837dfc6..9f005714aa5 100644 --- a/apps/core/src/components/index.ts +++ b/apps/core/src/components/index.ts @@ -5,5 +5,5 @@ export * from './coin'; export * from './icon'; export * from './Inputs'; export * from './QR'; - +export * from './collapsible'; export * from './providers'; diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx index 130a69e449b..5978d6fe0c4 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx @@ -3,23 +3,25 @@ import React from 'react'; import { Dialog } from '@iota/apps-ui-kit'; -import { DetailsView } from './views'; +import { DetailsView, SendView } from './views'; import { IotaObjectData } from '@iota/iota-sdk/client'; - -export enum AssetsDialogView { - Details, - Send, -} +import { AssetsDialogView } from './hooks'; interface AssetsDialogProps { isOpen: boolean; handleClose: () => void; asset: IotaObjectData | null; + view: AssetsDialogView; + setView: (view: AssetsDialogView) => void; } -export function AssetsDialog({ isOpen, handleClose, asset }: AssetsDialogProps): JSX.Element { - const [view, setView] = React.useState<AssetsDialogView>(AssetsDialogView.Details); - +export function AssetsDialog({ + isOpen, + handleClose, + asset, + setView, + view, +}: AssetsDialogProps): JSX.Element { function handleDetailsSend() { setView(AssetsDialogView.Send); } @@ -33,6 +35,7 @@ export function AssetsDialog({ isOpen, handleClose, asset }: AssetsDialogProps): handleSend={handleDetailsSend} /> )} + {view === AssetsDialogView.Send && <SendView />} </Dialog> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/hooks/index.ts b/apps/wallet-dashboard/components/Dialogs/Assets/hooks/index.ts new file mode 100644 index 00000000000..fc9c6483fb9 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Assets/hooks/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './useAssetsDialog'; diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts b/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts new file mode 100644 index 00000000000..d2792097363 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts @@ -0,0 +1,18 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; + +export enum AssetsDialogView { + Details = 'Details', + Send = 'Send', +} + +export function useAssetsDialog() { + const [view, setView] = React.useState<AssetsDialogView>(AssetsDialogView.Details); + + return { + view, + setView, + }; +} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx index 299f7188be1..2f79b4f2d91 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import React from 'react'; -import { ExplorerLinkType, useNftDetails } from '@iota/core'; +import { ExplorerLinkType, useNftDetails, Collapsible } from '@iota/core'; import { Button, ButtonType, @@ -15,7 +15,7 @@ import Link from 'next/link'; import { formatAddress } from '@iota/iota-sdk/utils'; import { Layout, LayoutBody, LayoutFooter } from '../../Staking/views/Layout'; import { IotaObjectData } from '@iota/iota-sdk/client'; -import { Collapsible } from '@/components/Collapsible/Collapsible'; +// import { Collapsible } from '@/components/Collapsible/Collapsible'; import { ExplorerLink } from '@/components/ExplorerLink'; import { useCurrentAccount } from '@iota/dapp-kit'; @@ -105,7 +105,7 @@ export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps </div> )} - <Collapsible defaultExpanded title="Details"> + <Collapsible defaultOpen title="Details"> <div className="flex flex-col gap-xs px-md pb-xs pt-sm"> {ownerAddress && ( <KeyValueInfo @@ -131,7 +131,7 @@ export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps </div> </Collapsible> {metaKeys.length ? ( - <Collapsible defaultExpanded title="Attributes"> + <Collapsible defaultOpen title="Attributes"> <div className="flex flex-col gap-xs px-md pb-xs pt-sm"> {metaKeys.map((aKey, idx) => { const { value, valueLink } = formatMetaValue( diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/index.ts b/apps/wallet-dashboard/components/Dialogs/Assets/views/index.ts index 5623d78737d..fcc3856cac3 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/index.ts @@ -2,3 +2,4 @@ // SPDX-License-Identifier: Apache-2.0 export * from './DetailsView'; +export * from './SendView'; diff --git a/apps/wallet/src/ui/app/pages/accounts/manage/AccountGroup.tsx b/apps/wallet/src/ui/app/pages/accounts/manage/AccountGroup.tsx index b6cd64ae3b6..c1a63ae97cd 100644 --- a/apps/wallet/src/ui/app/pages/accounts/manage/AccountGroup.tsx +++ b/apps/wallet/src/ui/app/pages/accounts/manage/AccountGroup.tsx @@ -14,9 +14,8 @@ import { Button, ButtonSize, ButtonType, Dropdown, ListItem } from '@iota/apps-u import { Add, MoreHoriz, TriangleDown } from '@iota/ui-icons'; import { OutsideClickHandler } from '_components/OutsideClickHandler'; import { AccountGroupItem } from '_pages/accounts/manage/AccountGroupItem'; -import { Collapsible } from '_app/shared/collapse'; import { useFeature } from '@growthbook/growthbook-react'; -import { Feature } from '@iota/core'; +import { Feature, Collapsible } from '@iota/core'; import { useActiveAccount } from '_app/hooks/useActiveAccount'; const ACCOUNT_TYPE_TO_LABEL: Record<AccountType, string> = { diff --git a/apps/wallet/src/ui/app/pages/approval-request/transaction-request/GasFees.tsx b/apps/wallet/src/ui/app/pages/approval-request/transaction-request/GasFees.tsx index 2460016d0fb..31351c29c2e 100644 --- a/apps/wallet/src/ui/app/pages/approval-request/transaction-request/GasFees.tsx +++ b/apps/wallet/src/ui/app/pages/approval-request/transaction-request/GasFees.tsx @@ -3,9 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 import { TitleSize, Badge, BadgeType, Title, Panel } from '@iota/apps-ui-kit'; -import { Collapsible } from '_src/ui/app/shared/collapse'; import { GasSummary } from '_src/ui/app/shared/transaction-summary/cards/GasSummary'; -import { type GasSummaryType } from '@iota/core'; +import { type GasSummaryType, Collapsible } from '@iota/core'; interface GasFeesProps { sender?: string; diff --git a/apps/wallet/src/ui/app/pages/approval-request/transaction-request/TransactionDetails/Command.tsx b/apps/wallet/src/ui/app/pages/approval-request/transaction-request/TransactionDetails/Command.tsx index 72075262870..f3106a47831 100644 --- a/apps/wallet/src/ui/app/pages/approval-request/transaction-request/TransactionDetails/Command.tsx +++ b/apps/wallet/src/ui/app/pages/approval-request/transaction-request/TransactionDetails/Command.tsx @@ -5,7 +5,7 @@ import { TypeTagSerializer, type TypeTag } from '@iota/iota-sdk/bcs'; import { type TransactionArgument, type Commands } from '@iota/iota-sdk/transactions/'; import { formatAddress, normalizeIotaAddress, toB64 } from '@iota/iota-sdk/utils'; -import { Collapsible } from '_src/ui/app/shared/collapse'; +import { Collapsible } from '@iota/core'; import { TitleSize } from '@iota/apps-ui-kit'; type TransactionType = ReturnType<(typeof Commands)[keyof typeof Commands]>; diff --git a/apps/wallet/src/ui/app/pages/approval-request/transaction-request/TransactionDetails/index.tsx b/apps/wallet/src/ui/app/pages/approval-request/transaction-request/TransactionDetails/index.tsx index 368174dc241..518a0929061 100644 --- a/apps/wallet/src/ui/app/pages/approval-request/transaction-request/TransactionDetails/index.tsx +++ b/apps/wallet/src/ui/app/pages/approval-request/transaction-request/TransactionDetails/index.tsx @@ -6,7 +6,7 @@ import { useTransactionData } from '_src/ui/app/hooks'; import { type Transaction } from '@iota/iota-sdk/transactions'; import { Command } from './Command'; import { Input } from './Input'; -import { Collapsible } from '_src/ui/app/shared/collapse'; +import { Collapsible } from '@iota/core'; import { ButtonSegment, ButtonSegmentType, diff --git a/apps/wallet/src/ui/app/pages/home/kiosk-details/index.tsx b/apps/wallet/src/ui/app/pages/home/kiosk-details/index.tsx index 1d3d10dc9fe..826dc5e2317 100644 --- a/apps/wallet/src/ui/app/pages/home/kiosk-details/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/kiosk-details/index.tsx @@ -12,8 +12,7 @@ import { PageTemplate, } from '_components'; import { useUnlockedGuard } from '_src/ui/app/hooks/useUnlockedGuard'; -import { Collapsible } from '_src/ui/app/shared/collapse'; -import { useGetKioskContents } from '@iota/core'; +import { useGetKioskContents, Collapsible } from '@iota/core'; import { formatAddress } from '@iota/iota-sdk/utils'; import { Link, useSearchParams, useNavigate } from 'react-router-dom'; import cl from 'clsx'; diff --git a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx index 451184573b0..fd2b201f94a 100644 --- a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx @@ -3,10 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 import { useActiveAddress } from '_app/hooks/useActiveAddress'; -import { Collapsible } from '_app/shared/collapse'; import { ExplorerLink, ExplorerLinkType, Loading, NFTDisplayCard, PageTemplate } from '_components'; import { useUnlockedGuard } from '_src/ui/app/hooks/useUnlockedGuard'; -import { useNFTBasicData, useNftDetails } from '@iota/core'; +import { useNFTBasicData, useNftDetails, Collapsible } from '@iota/core'; import { formatAddress } from '@iota/iota-sdk/utils'; import cl from 'clsx'; import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom'; diff --git a/apps/wallet/src/ui/app/pages/home/tokens/TokenList.tsx b/apps/wallet/src/ui/app/pages/home/tokens/TokenList.tsx index 7952e1c8db2..d2b1a8dba3b 100644 --- a/apps/wallet/src/ui/app/pages/home/tokens/TokenList.tsx +++ b/apps/wallet/src/ui/app/pages/home/tokens/TokenList.tsx @@ -2,7 +2,7 @@ // Modifications Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { Collapsible } from '_src/ui/app/shared/collapse'; +import { Collapsible } from '@iota/core'; import { type ReactNode } from 'react'; type TokenListProps = { diff --git a/apps/wallet/src/ui/app/shared/transaction-summary/cards/ObjectChanges.tsx b/apps/wallet/src/ui/app/shared/transaction-summary/cards/ObjectChanges.tsx index 86febdbe570..0f1e92eddfc 100644 --- a/apps/wallet/src/ui/app/shared/transaction-summary/cards/ObjectChanges.tsx +++ b/apps/wallet/src/ui/app/shared/transaction-summary/cards/ObjectChanges.tsx @@ -8,12 +8,12 @@ import { type ObjectChangeSummary, type IotaObjectChangeTypes, type IotaObjectChangeWithDisplay, + Collapsible, } from '@iota/core'; import { formatAddress } from '@iota/iota-sdk/utils'; import cx from 'clsx'; import { ExpandableList } from '../../ExpandableList'; import { ObjectChangeDisplay } from './objectSummary/ObjectChangeDisplay'; -import { Collapsible } from '../../collapse'; import { Badge, BadgeType, From f9530b10893e4d538a2a50483efdb4306ddc4278 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Mon, 25 Nov 2024 20:58:04 +0200 Subject: [PATCH 04/14] feat(dashboard): integrate useAssetsDialog for asset details view --- .../app/(protected)/assets/page.tsx | 6 ++- .../components/Collapsible/Collapsible.tsx | 41 --------------- .../components/Dialogs/Assets/index.ts | 3 ++ .../Dialogs/Assets/views/DetailsView.tsx | 8 +-- .../components/ExplorerLink.tsx | 1 + .../src/ui/app/shared/collapse/index.tsx | 51 ------------------- 6 files changed, 11 insertions(+), 99 deletions(-) delete mode 100644 apps/wallet-dashboard/components/Collapsible/Collapsible.tsx delete mode 100644 apps/wallet/src/ui/app/shared/collapse/index.tsx diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index af060bd09d8..881977dc02e 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -11,7 +11,7 @@ import { IotaObjectData } from '@iota/iota-sdk/client'; import { useState } from 'react'; import { AssetCategory } from '@/lib/enums'; import { AssetList } from '@/components/AssetsList'; -import { AssetsDialog } from '@/components/Dialogs/Assets/AssetsDialog'; +import { AssetsDialog, AssetsDialogView, useAssetsDialog } from '@/components/Dialogs/Assets'; const PAGINATION_RANGE = [20, 40, 60]; @@ -27,6 +27,7 @@ const ASSET_CATEGORIES: { label: string; value: AssetCategory }[] = [ ]; export default function AssetsDashboardPage(): React.JSX.Element { + const { view, setView } = useAssetsDialog(); const [selectedCategory, setSelectedCategory] = useState<AssetCategory>(AssetCategory.Visual); const [limit, setLimit] = useState<number>(PAGINATION_RANGE[1]); const [selectedAsset, setSelectedAsset] = useState<IotaObjectData | null>(null); @@ -66,6 +67,7 @@ export default function AssetsDashboardPage(): React.JSX.Element { function handleClickAsset(asset: IotaObjectData) { setSelectedAsset(asset); + setView(AssetsDialogView.Details); } function handleCloseDialog() { @@ -107,6 +109,8 @@ export default function AssetsDashboardPage(): React.JSX.Element { /> </div> <AssetsDialog + view={view} + setView={setView} isOpen={!!selectedAsset} handleClose={handleCloseDialog} asset={selectedAsset} diff --git a/apps/wallet-dashboard/components/Collapsible/Collapsible.tsx b/apps/wallet-dashboard/components/Collapsible/Collapsible.tsx deleted file mode 100644 index 28989df53d2..00000000000 --- a/apps/wallet-dashboard/components/Collapsible/Collapsible.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 -import { PropsWithChildren, useState } from 'react'; -import { - AccordionHeaderProps, - Accordion, - AccordionHeader, - AccordionContent, - TitleSize, - Title, -} from '@iota/apps-ui-kit'; - -type CollapsibleProps = { - title: string; - hideBorder?: boolean; - defaultExpanded?: boolean; - headerProps?: AccordionHeaderProps; - titleSize?: TitleSize; -}; - -export function Collapsible({ - title, - children, - defaultExpanded = false, - hideBorder, - titleSize = TitleSize.Small, -}: PropsWithChildren<CollapsibleProps>) { - const [isExpanded, setIsExpanded] = useState(defaultExpanded); - return ( - <Accordion hideBorder={hideBorder}> - <AccordionHeader - hideBorder={hideBorder} - isExpanded={isExpanded} - onToggle={() => setIsExpanded(!isExpanded)} - > - <Title size={titleSize} title={title} /> - </AccordionHeader> - <AccordionContent isExpanded={isExpanded}>{children}</AccordionContent> - </Accordion> - ); -} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/index.ts b/apps/wallet-dashboard/components/Dialogs/Assets/index.ts index 507ff4f98be..8f1485fd6b6 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/Assets/index.ts @@ -1,2 +1,5 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 + +export * from './AssetsDialog'; +export * from './hooks'; diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx index 2f79b4f2d91..1f9899997f4 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx @@ -15,7 +15,6 @@ import Link from 'next/link'; import { formatAddress } from '@iota/iota-sdk/utils'; import { Layout, LayoutBody, LayoutFooter } from '../../Staking/views/Layout'; import { IotaObjectData } from '@iota/iota-sdk/client'; -// import { Collapsible } from '@/components/Collapsible/Collapsible'; import { ExplorerLink } from '@/components/ExplorerLink'; import { useCurrentAccount } from '@iota/dapp-kit'; @@ -66,10 +65,7 @@ export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps isHoverable={false} /> </div> - <ExplorerLink - address={senderAddress} - linkProps={{ type: ExplorerLinkType.Object, objectID: objectId }} - > + <ExplorerLink type={ExplorerLinkType.Object} objectID={objectId}> <Button type={ButtonType.Ghost} text="View on Explorer" /> </ExplorerLink> <div className="flex w-full flex-col gap-md"> @@ -112,7 +108,7 @@ export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps keyText="Owner" value={ <ExplorerLink - linkProps={{ type: ExplorerLinkType.Address }} + type={ExplorerLinkType.Address} address={ownerAddress} > {formatAddress(ownerAddress)} diff --git a/apps/wallet-dashboard/components/ExplorerLink.tsx b/apps/wallet-dashboard/components/ExplorerLink.tsx index 2b671cf8cc5..75211dceddd 100644 --- a/apps/wallet-dashboard/components/ExplorerLink.tsx +++ b/apps/wallet-dashboard/components/ExplorerLink.tsx @@ -16,6 +16,7 @@ export function ExplorerLink({ isExternal, ...getLinkProps }: React.PropsWithChildren<ExplorerLinkProps>): React.JSX.Element { + console.log('--- getLinkProps', getLinkProps); const getExplorerLink = useExplorerLinkGetter(); const href = getExplorerLink(getLinkProps) ?? '#'; diff --git a/apps/wallet/src/ui/app/shared/collapse/index.tsx b/apps/wallet/src/ui/app/shared/collapse/index.tsx deleted file mode 100644 index c44607bc686..00000000000 --- a/apps/wallet/src/ui/app/shared/collapse/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { Accordion, AccordionContent, AccordionHeader, Title, TitleSize } from '@iota/apps-ui-kit'; -import { useState, type ReactNode } from 'react'; - -interface CollapsibleProps { - title?: string; - defaultOpen?: boolean; - children: ReactNode | ReactNode[]; - isOpen?: boolean; - onOpenChange?: (isOpen: boolean) => void; - titleSize?: TitleSize; - render?: ({ isOpen }: { isOpen: boolean }) => ReactNode; - hideArrow?: boolean; - hideBorder?: boolean; -} - -export function Collapsible({ - title = '', - children, - defaultOpen, - isOpen, - onOpenChange, - titleSize = TitleSize.Small, - render, - hideArrow, - hideBorder, -}: CollapsibleProps) { - const [open, setOpen] = useState(isOpen ?? defaultOpen ?? false); - - function handleOpenChange(isOpen: boolean) { - setOpen(isOpen); - onOpenChange?.(isOpen); - } - - return ( - <Accordion hideBorder={hideBorder}> - <AccordionHeader - hideBorder={hideBorder} - hideArrow={hideArrow} - isExpanded={isOpen ?? open} - onToggle={() => handleOpenChange(!open)} - > - {render ? render({ isOpen: open }) : <Title size={titleSize} title={title} />} - </AccordionHeader> - <AccordionContent isExpanded={isOpen ?? open}>{children}</AccordionContent> - </Accordion> - ); -} From d055db0f627abe44d11b1baf629a87ea1897103d Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Tue, 26 Nov 2024 13:52:47 +0200 Subject: [PATCH 05/14] fix(assets): update import path and enhance text styling in DetailsView --- .../app/(protected)/assets/page.tsx | 16 ++-- .../Dialogs/Assets/AssetsDialog.tsx | 84 +++++++++++++++++-- .../Dialogs/Assets/hooks/useAssetsDialog.ts | 2 +- .../Dialogs/Assets/views/DetailsView.tsx | 6 +- .../Dialogs/Assets/views/SendView.tsx | 58 ++++++++++++- .../components/ExplorerLink.tsx | 1 - 6 files changed, 144 insertions(+), 23 deletions(-) diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index 881977dc02e..cc24a7909d8 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -108,13 +108,15 @@ export default function AssetsDashboardPage(): React.JSX.Element { } /> </div> - <AssetsDialog - view={view} - setView={setView} - isOpen={!!selectedAsset} - handleClose={handleCloseDialog} - asset={selectedAsset} - /> + {view && ( + <AssetsDialog + view={view} + setView={setView} + isOpen={!!selectedAsset} + handleClose={handleCloseDialog} + asset={selectedAsset} + /> + )} </div> </Panel> ); diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx index 5978d6fe0c4..b631a4963b2 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx @@ -3,18 +3,33 @@ import React from 'react'; import { Dialog } from '@iota/apps-ui-kit'; +import { FormikProvider, useFormik } from 'formik'; +import { useCurrentAccount } from '@iota/dapp-kit'; +import { createNftSendValidationSchema } from '@iota/core'; import { DetailsView, SendView } from './views'; import { IotaObjectData } from '@iota/iota-sdk/client'; import { AssetsDialogView } from './hooks'; +import { useCreateSendAssetTransaction, useNotifications } from '@/hooks'; +import { NotificationType } from '@/stores/notificationStore'; +import { ASSETS_ROUTE } from '@/lib/constants/routes.constants'; +import { useRouter } from 'next/navigation'; interface AssetsDialogProps { isOpen: boolean; handleClose: () => void; asset: IotaObjectData | null; view: AssetsDialogView; - setView: (view: AssetsDialogView) => void; + setView: (view: AssetsDialogView | undefined) => void; } +export interface FormValues { + to: string; +} + +const INITIAL_VALUES: FormValues = { + to: '', +}; + export function AssetsDialog({ isOpen, handleClose, @@ -22,20 +37,71 @@ export function AssetsDialog({ setView, view, }: AssetsDialogProps): JSX.Element { + const account = useCurrentAccount(); + const activeAddress = account?.address ?? ''; + const objectId = asset?.objectId ?? ''; + const router = useRouter(); + const { addNotification } = useNotifications(); + const validationSchema = createNftSendValidationSchema(activeAddress, objectId); + function handleDetailsSend() { setView(AssetsDialogView.Send); } + const { mutation: sendAsset } = useCreateSendAssetTransaction( + objectId, + onSendAssetSuccess, + onSendAssetError, + ); + + const formik = useFormik<FormValues>({ + initialValues: INITIAL_VALUES, + validationSchema: validationSchema, + onSubmit: onSubmit, + validateOnChange: true, + }); + + function onSendAssetSuccess() { + addNotification('Transfer transaction successful', NotificationType.Success); + router.push(ASSETS_ROUTE.path + '/assets'); + } + + function onSendAssetError() { + addNotification('Transfer transaction failed', NotificationType.Error); + } + + async function onSubmit(values: FormValues) { + try { + await sendAsset.mutateAsync(values.to); + } catch (error) { + addNotification('Transfer transaction failed', NotificationType.Error); + } + } + + function onSendClose() { + setView(undefined); + } + + function onSendBack() { + setView(AssetsDialogView.Details); + } + return ( <Dialog open={isOpen} onOpenChange={() => handleClose()}> - {view === AssetsDialogView.Details && asset && ( - <DetailsView - asset={asset} - handleClose={handleClose} - handleSend={handleDetailsSend} - /> - )} - {view === AssetsDialogView.Send && <SendView />} + <FormikProvider value={formik}> + <> + {view === AssetsDialogView.Details && asset && ( + <DetailsView + asset={asset} + handleClose={handleClose} + handleSend={handleDetailsSend} + /> + )} + {view === AssetsDialogView.Send && asset && ( + <SendView asset={asset} onClose={onSendClose} onBack={onSendBack} /> + )} + </> + </FormikProvider> </Dialog> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts b/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts index d2792097363..4879d392633 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts +++ b/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts @@ -9,7 +9,7 @@ export enum AssetsDialogView { } export function useAssetsDialog() { - const [view, setView] = React.useState<AssetsDialogView>(AssetsDialogView.Details); + const [view, setView] = React.useState<AssetsDialogView | undefined>(AssetsDialogView.Details); return { view, diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx index 1f9899997f4..57372b32e4a 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx @@ -53,7 +53,7 @@ export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps return ( <Layout> - <Header title="Asset" onClose={handleClose} /> + <Header title="Asset" onClose={handleClose} titleCentered /> <LayoutBody> <div className="flex w-full flex-col items-center justify-center gap-xs"> <div className="w-[172px]"> @@ -70,7 +70,9 @@ export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps </ExplorerLink> <div className="flex w-full flex-col gap-md"> <div className="flex flex-col gap-xxxs"> - <span className="text-title-lg text-neutral-10">{nftMeta?.name}</span> + <span className="text-title-lg text-neutral-10 dark:text-neutral-92"> + {nftMeta?.name} + </span> {nftMeta?.description ? ( <span className="text-body-md text-neutral-60"> {nftMeta?.description} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx index 31e13461195..cd9428c6611 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx @@ -2,16 +2,68 @@ // SPDX-License-Identifier: Apache-2.0 import React from 'react'; -import { Layout, LayoutBody } from '../../Staking/views/Layout'; +import { AddressInput, useNftDetails } from '@iota/core'; +import { useFormikContext } from 'formik'; +import { Layout, LayoutBody, LayoutFooter } from '../../Staking/views/Layout'; +import { + Button, + ButtonHtmlType, + Header, + VisualAssetCard, + VisualAssetType, +} from '@iota/apps-ui-kit'; +import { Loader } from '@iota/ui-icons'; +import { useCurrentAccount } from '@iota/dapp-kit'; +import { IotaObjectData } from '@iota/iota-sdk/client'; -export function SendView() { +interface SendViewProps { + asset: IotaObjectData; + onClose: () => void; + onBack: () => void; +} + +export function SendView({ asset, onClose, onBack }: SendViewProps) { + const { isValid, dirty, isSubmitting, submitForm } = useFormikContext(); + + const account = useCurrentAccount(); + + const senderAddress = account?.address ?? ''; + const objectId = asset?.objectId || ''; + + const { nftName, nftImageUrl } = useNftDetails(objectId, senderAddress); return ( <Layout> + <Header title="Send asset" onClose={onClose} titleCentered onBack={onBack} /> <LayoutBody> <div className="flex w-full flex-col items-center justify-center gap-xs"> - Send view + <div className="w-[172px]"> + <VisualAssetCard + assetSrc={nftImageUrl} + assetTitle={nftName} + assetType={VisualAssetType.Image} + altText={nftName || 'NFT'} + isHoverable={false} + /> + </div> + <div className="flex w-full flex-col gap-md"> + <div className="flex flex-col items-center gap-xxxs"> + <span className="text-title-lg text-neutral-10">{nftName}</span> + </div> + <AddressInput name="to" placeholder="Enter Address" /> + </div> </div> </LayoutBody> + <LayoutFooter> + <Button + fullWidth + htmlType={ButtonHtmlType.Submit} + disabled={!(isValid && dirty) || isSubmitting} + text="Send" + icon={isSubmitting ? <Loader className="animate-spin" /> : undefined} + iconAfterText + onClick={submitForm} + /> + </LayoutFooter> </Layout> ); } diff --git a/apps/wallet-dashboard/components/ExplorerLink.tsx b/apps/wallet-dashboard/components/ExplorerLink.tsx index 75211dceddd..2b671cf8cc5 100644 --- a/apps/wallet-dashboard/components/ExplorerLink.tsx +++ b/apps/wallet-dashboard/components/ExplorerLink.tsx @@ -16,7 +16,6 @@ export function ExplorerLink({ isExternal, ...getLinkProps }: React.PropsWithChildren<ExplorerLinkProps>): React.JSX.Element { - console.log('--- getLinkProps', getLinkProps); const getExplorerLink = useExplorerLinkGetter(); const href = getExplorerLink(getLinkProps) ?? '#'; From fc404d8e3be57ea697b455329050f0b68015a0fc Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Thu, 28 Nov 2024 12:55:12 +0200 Subject: [PATCH 06/14] refactor(wallet-dashboard): move state to page --- .../app/(protected)/assets/page.tsx | 4 ++-- .../components/Dialogs/Assets/AssetsDialog.tsx | 2 +- .../Assets/constants/AssetsDialogView.ts | 7 +++++++ .../Assets/{hooks => constants}/index.ts | 2 +- .../Dialogs/Assets/hooks/useAssetsDialog.ts | 18 ------------------ .../components/Dialogs/Assets/index.ts | 2 +- 6 files changed, 12 insertions(+), 23 deletions(-) create mode 100644 apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts rename apps/wallet-dashboard/components/Dialogs/Assets/{hooks => constants}/index.ts (67%) delete mode 100644 apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index de371ce6d65..0b496533e07 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -10,7 +10,7 @@ import { IotaObjectData } from '@iota/iota-sdk/client'; import { useState } from 'react'; import { AssetCategory } from '@/lib/enums'; import { AssetList } from '@/components/AssetsList'; -import { AssetsDialog, AssetsDialogView, useAssetsDialog } from '@/components/Dialogs/Assets'; +import { AssetsDialog, AssetsDialogView } from '@/components/Dialogs/Assets'; const OBJECTS_PER_REQ = 50; @@ -26,7 +26,7 @@ const ASSET_CATEGORIES: { label: string; value: AssetCategory }[] = [ ]; export default function AssetsDashboardPage(): React.JSX.Element { - const { view, setView } = useAssetsDialog(); + const [view, setView] = useState<AssetsDialogView | undefined>(AssetsDialogView.Details); const [selectedCategory, setSelectedCategory] = useState<AssetCategory>(AssetCategory.Visual); const [selectedAsset, setSelectedAsset] = useState<IotaObjectData | null>(null); const account = useCurrentAccount(); diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx index b631a4963b2..58ff913f2b0 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx @@ -8,7 +8,7 @@ import { useCurrentAccount } from '@iota/dapp-kit'; import { createNftSendValidationSchema } from '@iota/core'; import { DetailsView, SendView } from './views'; import { IotaObjectData } from '@iota/iota-sdk/client'; -import { AssetsDialogView } from './hooks'; +import { AssetsDialogView } from './constants'; import { useCreateSendAssetTransaction, useNotifications } from '@/hooks'; import { NotificationType } from '@/stores/notificationStore'; import { ASSETS_ROUTE } from '@/lib/constants/routes.constants'; diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts b/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts new file mode 100644 index 00000000000..88cb34c15b1 --- /dev/null +++ b/apps/wallet-dashboard/components/Dialogs/Assets/constants/AssetsDialogView.ts @@ -0,0 +1,7 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export enum AssetsDialogView { + Details = 'Details', + Send = 'Send', +} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/hooks/index.ts b/apps/wallet-dashboard/components/Dialogs/Assets/constants/index.ts similarity index 67% rename from apps/wallet-dashboard/components/Dialogs/Assets/hooks/index.ts rename to apps/wallet-dashboard/components/Dialogs/Assets/constants/index.ts index fc9c6483fb9..76b42558333 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/hooks/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/Assets/constants/index.ts @@ -1,4 +1,4 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export * from './useAssetsDialog'; +export * from './AssetsDialogView'; diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts b/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts deleted file mode 100644 index 4879d392633..00000000000 --- a/apps/wallet-dashboard/components/Dialogs/Assets/hooks/useAssetsDialog.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import React from 'react'; - -export enum AssetsDialogView { - Details = 'Details', - Send = 'Send', -} - -export function useAssetsDialog() { - const [view, setView] = React.useState<AssetsDialogView | undefined>(AssetsDialogView.Details); - - return { - view, - setView, - }; -} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/index.ts b/apps/wallet-dashboard/components/Dialogs/Assets/index.ts index 8f1485fd6b6..6f9c35ceac4 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/Assets/index.ts @@ -2,4 +2,4 @@ // SPDX-License-Identifier: Apache-2.0 export * from './AssetsDialog'; -export * from './hooks'; +export * from './constants'; From 61a13f8d06b27358197d17852661e1f46bdd11db Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Thu, 28 Nov 2024 14:50:53 +0200 Subject: [PATCH 07/14] refactor(wallet-dashboard): rename handler functions for consistency and clarity --- .../app/(protected)/assets/page.tsx | 11 ++++++----- .../components/Dialogs/Assets/AssetsDialog.tsx | 14 +++++--------- .../Dialogs/Assets/views/DetailsView.tsx | 15 +++++---------- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index 0b496533e07..2a221f44b72 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -27,8 +27,8 @@ const ASSET_CATEGORIES: { label: string; value: AssetCategory }[] = [ export default function AssetsDashboardPage(): React.JSX.Element { const [view, setView] = useState<AssetsDialogView | undefined>(AssetsDialogView.Details); - const [selectedCategory, setSelectedCategory] = useState<AssetCategory>(AssetCategory.Visual); const [selectedAsset, setSelectedAsset] = useState<IotaObjectData | null>(null); + const [selectedCategory, setSelectedCategory] = useState<AssetCategory>(AssetCategory.Visual); const account = useCurrentAccount(); const { data, isFetching, fetchNextPage, hasNextPage } = useGetOwnedObjects( account?.address, @@ -52,13 +52,14 @@ export default function AssetsDashboardPage(): React.JSX.Element { } } - function handleClickAsset(asset: IotaObjectData) { + function onClickAsset(asset: IotaObjectData) { setSelectedAsset(asset); setView(AssetsDialogView.Details); } - function handleCloseDialog() { + function onCloseDialog() { setSelectedAsset(null); + setView(undefined); } return ( @@ -79,7 +80,7 @@ export default function AssetsDashboardPage(): React.JSX.Element { <AssetList assets={assets} selectedCategory={selectedCategory} - onClick={handleClickAsset} + onClick={onClickAsset} hasNextPage={hasNextPage} isFetchingNextPage={isFetching} fetchNextPage={fetchNextPage} @@ -89,7 +90,7 @@ export default function AssetsDashboardPage(): React.JSX.Element { view={view} setView={setView} isOpen={!!selectedAsset} - handleClose={handleCloseDialog} + onClose={onCloseDialog} asset={selectedAsset} /> )} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx index 58ff913f2b0..eaebe555497 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx @@ -16,7 +16,7 @@ import { useRouter } from 'next/navigation'; interface AssetsDialogProps { isOpen: boolean; - handleClose: () => void; + onClose: () => void; asset: IotaObjectData | null; view: AssetsDialogView; setView: (view: AssetsDialogView | undefined) => void; @@ -32,7 +32,7 @@ const INITIAL_VALUES: FormValues = { export function AssetsDialog({ isOpen, - handleClose, + onClose, asset, setView, view, @@ -44,7 +44,7 @@ export function AssetsDialog({ const { addNotification } = useNotifications(); const validationSchema = createNftSendValidationSchema(activeAddress, objectId); - function handleDetailsSend() { + function onDetailsSend() { setView(AssetsDialogView.Send); } @@ -87,15 +87,11 @@ export function AssetsDialog({ } return ( - <Dialog open={isOpen} onOpenChange={() => handleClose()}> + <Dialog open={isOpen} onOpenChange={() => onClose()}> <FormikProvider value={formik}> <> {view === AssetsDialogView.Details && asset && ( - <DetailsView - asset={asset} - handleClose={handleClose} - handleSend={handleDetailsSend} - /> + <DetailsView asset={asset} onClose={onClose} onSend={onDetailsSend} /> )} {view === AssetsDialogView.Send && asset && ( <SendView asset={asset} onClose={onSendClose} onBack={onSendBack} /> diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx index 57372b32e4a..ceed115228f 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx @@ -20,11 +20,11 @@ import { useCurrentAccount } from '@iota/dapp-kit'; interface DetailsViewProps { asset: IotaObjectData; - handleClose: () => void; - handleSend: () => void; + onClose: () => void; + onSend: () => void; } -export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps) { +export function DetailsView({ onClose, asset, onSend }: DetailsViewProps) { const account = useCurrentAccount(); const senderAddress = account?.address ?? ''; @@ -53,7 +53,7 @@ export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps return ( <Layout> - <Header title="Asset" onClose={handleClose} titleCentered /> + <Header title="Asset" onClose={onClose} titleCentered /> <LayoutBody> <div className="flex w-full flex-col items-center justify-center gap-xs"> <div className="w-[172px]"> @@ -174,12 +174,7 @@ export function DetailsView({ handleClose, asset, handleSend }: DetailsViewProps /> </div> ) : ( - <Button - disabled={!isTransferable} - onClick={handleSend} - text="Send" - fullWidth - /> + <Button disabled={!isTransferable} onClick={onSend} text="Send" fullWidth /> )} </div> </LayoutFooter> From 4ce390b598f953f009f2af8efefc82ee52623ecf Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Fri, 29 Nov 2024 13:48:09 +0200 Subject: [PATCH 08/14] refactor(dashboard): update state for asset view, improve code --- apps/core/src/hooks/useNftDetails.ts | 20 +++++--- .../app/(protected)/assets/page.tsx | 19 ++------ .../{AssetsDialog.tsx => AssetDialog.tsx} | 47 +++++++------------ .../components/Dialogs/Assets/index.ts | 2 +- .../Dialogs/Assets/views/SendView.tsx | 3 +- 5 files changed, 38 insertions(+), 53 deletions(-) rename apps/wallet-dashboard/components/Dialogs/Assets/{AssetsDialog.tsx => AssetDialog.tsx} (71%) diff --git a/apps/core/src/hooks/useNftDetails.ts b/apps/core/src/hooks/useNftDetails.ts index f28d27192a9..6e1941e4579 100644 --- a/apps/core/src/hooks/useNftDetails.ts +++ b/apps/core/src/hooks/useNftDetails.ts @@ -1,11 +1,19 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { useGetNFTMeta, useOwnedNFT, useNFTBasicData, useGetKioskContents } from './'; +import { + useGetNFTMeta, + useOwnedNFT, + useNFTBasicData, + useGetKioskContents, + useIsAssetTransferable, +} from './'; import { formatAddress } from '@iota/iota-sdk/utils'; -import { isAssetTransferable, truncateString } from '../utils'; +import { truncateString } from '../utils'; + +type NftField = { keys: string[]; values: string[] }; type NftFields = { - metadata?: { fields?: { attributes?: { fields?: { keys: string[]; values: string[] } } } }; + metadata?: { fields?: { attributes?: { fields?: NftField } } }; }; export function useNftDetails(nftId: string, accountAddress: string | null) { @@ -15,7 +23,7 @@ export function useNftDetails(nftId: string, accountAddress: string | null) { const isContainedInKiosk = data?.lookup.get(nftId!); const kioskItem = data?.list.find((k) => k.data?.objectId === nftId); - const isTransferable = isAssetTransferable(objectData); + const { data: isTransferable } = useIsAssetTransferable(objectData); const { nftFields } = useNFTBasicData(objectData); @@ -29,13 +37,13 @@ export function useNftDetails(nftId: string, accountAddress: string | null) { (nftFields as NftFields)?.metadata?.fields?.attributes?.fields || Object.entries(nftFields ?? {}) .filter(([key]) => key !== 'id') - .reduce( + .reduce<NftField>( (acc, [key, value]) => { acc.keys.push(key); acc.values.push(value as string); return acc; }, - { keys: [] as string[], values: [] as string[] }, + { keys: [], values: [] }, ); const ownerAddress = diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index 2a221f44b72..0e76fadc1bf 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -10,7 +10,7 @@ import { IotaObjectData } from '@iota/iota-sdk/client'; import { useState } from 'react'; import { AssetCategory } from '@/lib/enums'; import { AssetList } from '@/components/AssetsList'; -import { AssetsDialog, AssetsDialogView } from '@/components/Dialogs/Assets'; +import { AssetDialog } from '@/components/Dialogs/Assets'; const OBJECTS_PER_REQ = 50; @@ -26,7 +26,6 @@ const ASSET_CATEGORIES: { label: string; value: AssetCategory }[] = [ ]; export default function AssetsDashboardPage(): React.JSX.Element { - const [view, setView] = useState<AssetsDialogView | undefined>(AssetsDialogView.Details); const [selectedAsset, setSelectedAsset] = useState<IotaObjectData | null>(null); const [selectedCategory, setSelectedCategory] = useState<AssetCategory>(AssetCategory.Visual); const account = useCurrentAccount(); @@ -54,12 +53,6 @@ export default function AssetsDashboardPage(): React.JSX.Element { function onClickAsset(asset: IotaObjectData) { setSelectedAsset(asset); - setView(AssetsDialogView.Details); - } - - function onCloseDialog() { - setSelectedAsset(null); - setView(undefined); } return ( @@ -85,14 +78,8 @@ export default function AssetsDashboardPage(): React.JSX.Element { isFetchingNextPage={isFetching} fetchNextPage={fetchNextPage} /> - {view && ( - <AssetsDialog - view={view} - setView={setView} - isOpen={!!selectedAsset} - onClose={onCloseDialog} - asset={selectedAsset} - /> + {selectedAsset && ( + <AssetDialog onClose={() => setSelectedAsset(null)} asset={selectedAsset} /> )} </div> </Panel> diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx similarity index 71% rename from apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx rename to apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx index eaebe555497..e71d84c6d07 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetsDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import React from 'react'; +import React, { useState } from 'react'; import { Dialog } from '@iota/apps-ui-kit'; import { FormikProvider, useFormik } from 'formik'; import { useCurrentAccount } from '@iota/dapp-kit'; @@ -11,18 +11,13 @@ import { IotaObjectData } from '@iota/iota-sdk/client'; import { AssetsDialogView } from './constants'; import { useCreateSendAssetTransaction, useNotifications } from '@/hooks'; import { NotificationType } from '@/stores/notificationStore'; -import { ASSETS_ROUTE } from '@/lib/constants/routes.constants'; -import { useRouter } from 'next/navigation'; interface AssetsDialogProps { - isOpen: boolean; onClose: () => void; - asset: IotaObjectData | null; - view: AssetsDialogView; - setView: (view: AssetsDialogView | undefined) => void; + asset: IotaObjectData; } -export interface FormValues { +interface FormValues { to: string; } @@ -30,24 +25,14 @@ const INITIAL_VALUES: FormValues = { to: '', }; -export function AssetsDialog({ - isOpen, - onClose, - asset, - setView, - view, -}: AssetsDialogProps): JSX.Element { +export function AssetDialog({ onClose: onCloseCb, asset }: AssetsDialogProps): JSX.Element { + const [view, setView] = useState<AssetsDialogView>(AssetsDialogView.Details); const account = useCurrentAccount(); const activeAddress = account?.address ?? ''; const objectId = asset?.objectId ?? ''; - const router = useRouter(); const { addNotification } = useNotifications(); const validationSchema = createNftSendValidationSchema(activeAddress, objectId); - function onDetailsSend() { - setView(AssetsDialogView.Send); - } - const { mutation: sendAsset } = useCreateSendAssetTransaction( objectId, onSendAssetSuccess, @@ -62,8 +47,9 @@ export function AssetsDialog({ }); function onSendAssetSuccess() { + setView(AssetsDialogView.Details); + onCloseCb(); addNotification('Transfer transaction successful', NotificationType.Success); - router.push(ASSETS_ROUTE.path + '/assets'); } function onSendAssetError() { @@ -78,23 +64,26 @@ export function AssetsDialog({ } } - function onSendClose() { - setView(undefined); + function onDetailsSend() { + setView(AssetsDialogView.Send); } - function onSendBack() { + function onSendViewBack() { setView(AssetsDialogView.Details); } - + function onClose() { + setView(AssetsDialogView.Details); + onCloseCb(); + } return ( - <Dialog open={isOpen} onOpenChange={() => onClose()}> + <Dialog open onOpenChange={onClose}> <FormikProvider value={formik}> <> - {view === AssetsDialogView.Details && asset && ( + {view === AssetsDialogView.Details && ( <DetailsView asset={asset} onClose={onClose} onSend={onDetailsSend} /> )} - {view === AssetsDialogView.Send && asset && ( - <SendView asset={asset} onClose={onSendClose} onBack={onSendBack} /> + {view === AssetsDialogView.Send && ( + <SendView asset={asset} onClose={onClose} onBack={onSendViewBack} /> )} </> </FormikProvider> diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/index.ts b/apps/wallet-dashboard/components/Dialogs/Assets/index.ts index 6f9c35ceac4..3a6df1b0b55 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/index.ts +++ b/apps/wallet-dashboard/components/Dialogs/Assets/index.ts @@ -1,5 +1,5 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export * from './AssetsDialog'; +export * from './AssetDialog'; export * from './constants'; diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx index cd9428c6611..973d0407fd9 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/SendView.tsx @@ -11,6 +11,7 @@ import { Header, VisualAssetCard, VisualAssetType, + Title, } from '@iota/apps-ui-kit'; import { Loader } from '@iota/ui-icons'; import { useCurrentAccount } from '@iota/dapp-kit'; @@ -47,7 +48,7 @@ export function SendView({ asset, onClose, onBack }: SendViewProps) { </div> <div className="flex w-full flex-col gap-md"> <div className="flex flex-col items-center gap-xxxs"> - <span className="text-title-lg text-neutral-10">{nftName}</span> + <Title title={nftName} /> </div> <AddressInput name="to" placeholder="Enter Address" /> </div> From 84b2dba1eb82f6efefa901c4062f07aa6d933506 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Mon, 2 Dec 2024 16:40:24 +0200 Subject: [PATCH 09/14] fix(wallet-dashboard): unify asset transfer success and error handling in AssetDialog --- .../components/Dialogs/Assets/AssetDialog.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx index e71d84c6d07..ef8e68072b6 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx @@ -35,8 +35,8 @@ export function AssetDialog({ onClose: onCloseCb, asset }: AssetsDialogProps): J const { mutation: sendAsset } = useCreateSendAssetTransaction( objectId, - onSendAssetSuccess, - onSendAssetError, + onSendAsset, + onSendAsset, ); const formik = useFormik<FormValues>({ @@ -46,20 +46,17 @@ export function AssetDialog({ onClose: onCloseCb, asset }: AssetsDialogProps): J validateOnChange: true, }); - function onSendAssetSuccess() { + function onSendAsset() { setView(AssetsDialogView.Details); onCloseCb(); - addNotification('Transfer transaction successful', NotificationType.Success); - } - - function onSendAssetError() { - addNotification('Transfer transaction failed', NotificationType.Error); } async function onSubmit(values: FormValues) { try { await sendAsset.mutateAsync(values.to); + addNotification('Transfer transaction successful', NotificationType.Success); } catch (error) { + console.log(error); addNotification('Transfer transaction failed', NotificationType.Error); } } From f6992bc99811aa52c5e2a7d7b9b30db29297a3b6 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Tue, 3 Dec 2024 20:52:29 +0200 Subject: [PATCH 10/14] refactor(dashboard): adjust z-index for dialog and notifications; --- .../src/lib/components/organisms/dialog/Dialog.tsx | 2 +- .../components/Dialogs/Assets/AssetDialog.tsx | 14 +++----------- .../components/Notifications/Notifications.tsx | 2 +- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/apps/ui-kit/src/lib/components/organisms/dialog/Dialog.tsx b/apps/ui-kit/src/lib/components/organisms/dialog/Dialog.tsx index 58575ba3dcb..5bd43a1b7ba 100644 --- a/apps/ui-kit/src/lib/components/organisms/dialog/Dialog.tsx +++ b/apps/ui-kit/src/lib/components/organisms/dialog/Dialog.tsx @@ -74,7 +74,7 @@ const DialogContent = React.forwardRef< <RadixDialog.Content ref={ref} className={cx( - 'fixed z-[99999] flex flex-col justify-center overflow-hidden bg-primary-100 dark:bg-neutral-6 md:w-96', + 'fixed z-[99998] flex flex-col justify-center overflow-hidden bg-primary-100 dark:bg-neutral-6 md:w-96', positionClass, )} {...props} diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx index ef8e68072b6..94854fd0e05 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx @@ -33,11 +33,7 @@ export function AssetDialog({ onClose: onCloseCb, asset }: AssetsDialogProps): J const { addNotification } = useNotifications(); const validationSchema = createNftSendValidationSchema(activeAddress, objectId); - const { mutation: sendAsset } = useCreateSendAssetTransaction( - objectId, - onSendAsset, - onSendAsset, - ); + const { mutation: sendAsset } = useCreateSendAssetTransaction(objectId); const formik = useFormik<FormValues>({ initialValues: INITIAL_VALUES, @@ -46,17 +42,13 @@ export function AssetDialog({ onClose: onCloseCb, asset }: AssetsDialogProps): J validateOnChange: true, }); - function onSendAsset() { - setView(AssetsDialogView.Details); - onCloseCb(); - } - async function onSubmit(values: FormValues) { try { await sendAsset.mutateAsync(values.to); + setView(AssetsDialogView.Details); + onCloseCb(); addNotification('Transfer transaction successful', NotificationType.Success); } catch (error) { - console.log(error); addNotification('Transfer transaction failed', NotificationType.Error); } } diff --git a/apps/wallet-dashboard/components/Notifications/Notifications.tsx b/apps/wallet-dashboard/components/Notifications/Notifications.tsx index 3e0b0aad6d5..77493b164d9 100644 --- a/apps/wallet-dashboard/components/Notifications/Notifications.tsx +++ b/apps/wallet-dashboard/components/Notifications/Notifications.tsx @@ -55,7 +55,7 @@ function Notification(props: { notification: NotificationData }): JSX.Element { export default function Notifications(): JSX.Element { const notifications = useNotificationStore((state) => state.notifications); return ( - <div className="absolute right-2 top-1 z-50"> + <div className="fixed right-2 top-1 z-[99999]"> {notifications.map((notification) => ( <Notification key={`${notification.index}`} notification={notification} /> ))} From 127064a6f74c31e1d67b7288f61362618ba53192 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Tue, 3 Dec 2024 21:52:43 +0200 Subject: [PATCH 11/14] feat(dashboard): add refetch functionality after asset transfered --- .../app/(protected)/assets/page.tsx | 8 ++++-- .../components/Dialogs/Assets/AssetDialog.tsx | 11 +++++--- .../hooks/useCreateSendAssetTransaction.ts | 25 +++++++------------ 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index 0e76fadc1bf..84fbcca9bfd 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -29,7 +29,7 @@ export default function AssetsDashboardPage(): React.JSX.Element { const [selectedAsset, setSelectedAsset] = useState<IotaObjectData | null>(null); const [selectedCategory, setSelectedCategory] = useState<AssetCategory>(AssetCategory.Visual); const account = useCurrentAccount(); - const { data, isFetching, fetchNextPage, hasNextPage } = useGetOwnedObjects( + const { data, isFetching, fetchNextPage, hasNextPage, refetch } = useGetOwnedObjects( account?.address, undefined, OBJECTS_PER_REQ, @@ -79,7 +79,11 @@ export default function AssetsDashboardPage(): React.JSX.Element { fetchNextPage={fetchNextPage} /> {selectedAsset && ( - <AssetDialog onClose={() => setSelectedAsset(null)} asset={selectedAsset} /> + <AssetDialog + onClose={() => setSelectedAsset(null)} + onSent={() => refetch()} + asset={selectedAsset} + /> )} </div> </Panel> diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx index 94854fd0e05..cb3262e08a1 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx @@ -14,6 +14,7 @@ import { NotificationType } from '@/stores/notificationStore'; interface AssetsDialogProps { onClose: () => void; + onSent?: () => void; asset: IotaObjectData; } @@ -25,7 +26,7 @@ const INITIAL_VALUES: FormValues = { to: '', }; -export function AssetDialog({ onClose: onCloseCb, asset }: AssetsDialogProps): JSX.Element { +export function AssetDialog({ onClose: onCloseCb, onSent, asset }: AssetsDialogProps): JSX.Element { const [view, setView] = useState<AssetsDialogView>(AssetsDialogView.Details); const account = useCurrentAccount(); const activeAddress = account?.address ?? ''; @@ -45,10 +46,12 @@ export function AssetDialog({ onClose: onCloseCb, asset }: AssetsDialogProps): J async function onSubmit(values: FormValues) { try { await sendAsset.mutateAsync(values.to); - setView(AssetsDialogView.Details); - onCloseCb(); addNotification('Transfer transaction successful', NotificationType.Success); - } catch (error) { + onCloseCb(); + setView(AssetsDialogView.Details); + await new Promise((resolve) => setTimeout(resolve, 1000)); + onSent?.(); + } catch { addNotification('Transfer transaction failed', NotificationType.Error); } } diff --git a/apps/wallet-dashboard/hooks/useCreateSendAssetTransaction.ts b/apps/wallet-dashboard/hooks/useCreateSendAssetTransaction.ts index 82dd2b5fd6d..aa50c4fac7c 100644 --- a/apps/wallet-dashboard/hooks/useCreateSendAssetTransaction.ts +++ b/apps/wallet-dashboard/hooks/useCreateSendAssetTransaction.ts @@ -1,28 +1,16 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -import { useIotaClient, useSignAndExecuteTransaction } from '@iota/dapp-kit'; +import { useSignAndExecuteTransaction } from '@iota/dapp-kit'; import { Transaction } from '@iota/iota-sdk/transactions'; import { useMutation } from '@tanstack/react-query'; export function useCreateSendAssetTransaction( objectId: string, - onSuccess: () => void, - onError: (error: unknown) => void, + onSuccess?: () => void, + onError?: (error: unknown) => void, ) { - const iotaClient = useIotaClient(); - const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction({ - execute: async ({ bytes, signature }) => - await iotaClient.executeTransactionBlock({ - transactionBlock: bytes, - signature, - options: { - showEffects: true, - showEvents: true, - showInput: true, - }, - }), - }); + const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction(); const mutation = useMutation({ mutationFn: async (to: string) => { if (!to) { @@ -34,6 +22,11 @@ export function useCreateSendAssetTransaction( return signAndExecuteTransaction({ transaction: tx, + options: { + showEffects: true, + showEvents: true, + showInput: true, + }, }); }, onSuccess, From 1848cc5ecb7bb3bfa6e38fd2a753907ee154d770 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Wed, 4 Dec 2024 11:13:13 +0200 Subject: [PATCH 12/14] refactor(dashboard): rename callbacks for clarity in AssetDialog --- .../components/Dialogs/Assets/AssetDialog.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx index cb3262e08a1..502ff5a41ba 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx @@ -26,7 +26,7 @@ const INITIAL_VALUES: FormValues = { to: '', }; -export function AssetDialog({ onClose: onCloseCb, onSent, asset }: AssetsDialogProps): JSX.Element { +export function AssetDialog({ onClose, onSent, asset }: AssetsDialogProps): JSX.Element { const [view, setView] = useState<AssetsDialogView>(AssetsDialogView.Details); const account = useCurrentAccount(); const activeAddress = account?.address ?? ''; @@ -47,7 +47,7 @@ export function AssetDialog({ onClose: onCloseCb, onSent, asset }: AssetsDialogP try { await sendAsset.mutateAsync(values.to); addNotification('Transfer transaction successful', NotificationType.Success); - onCloseCb(); + onClose(); setView(AssetsDialogView.Details); await new Promise((resolve) => setTimeout(resolve, 1000)); onSent?.(); @@ -63,19 +63,19 @@ export function AssetDialog({ onClose: onCloseCb, onSent, asset }: AssetsDialogP function onSendViewBack() { setView(AssetsDialogView.Details); } - function onClose() { + function onOpenChange() { setView(AssetsDialogView.Details); - onCloseCb(); + onClose(); } return ( - <Dialog open onOpenChange={onClose}> + <Dialog open onOpenChange={onOpenChange}> <FormikProvider value={formik}> <> {view === AssetsDialogView.Details && ( - <DetailsView asset={asset} onClose={onClose} onSend={onDetailsSend} /> + <DetailsView asset={asset} onClose={onOpenChange} onSend={onDetailsSend} /> )} {view === AssetsDialogView.Send && ( - <SendView asset={asset} onClose={onClose} onBack={onSendViewBack} /> + <SendView asset={asset} onClose={onOpenChange} onBack={onSendViewBack} /> )} </> </FormikProvider> From 1327770065117c114a9bde45e57031dca2572704 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Wed, 4 Dec 2024 13:48:24 +0200 Subject: [PATCH 13/14] refactor(dashboard, cove): rename hooks, remove duplication. --- apps/core/src/hooks/index.ts | 2 +- apps/core/src/hooks/useGetNFTMeta.ts | 2 +- apps/core/src/hooks/useNftDetails.ts | 21 +++++++----- .../app/(protected)/assets/page.tsx | 12 +++---- .../components/Dialogs/Assets/AssetDialog.tsx | 5 +-- .../Dialogs/Assets/views/DetailsView.tsx | 34 +++++++++++-------- .../components/tiles/VisualAssetTile.tsx | 4 +-- .../ui/app/components/nft-display/index.tsx | 4 +-- .../ui/app/pages/home/nft-details/index.tsx | 32 ++++++++--------- .../ui/app/pages/home/nfts/HiddenAsset.tsx | 4 +-- 10 files changed, 61 insertions(+), 59 deletions(-) diff --git a/apps/core/src/hooks/index.ts b/apps/core/src/hooks/index.ts index 39389bf9b1a..693563cc0df 100644 --- a/apps/core/src/hooks/index.ts +++ b/apps/core/src/hooks/index.ts @@ -32,7 +32,7 @@ export * from './useQueryTransactionsByAddress'; export * from './useGetTransaction'; export * from './useExtendedTransactionSummary'; export * from './useSortedCoinsByCategories'; -export * from './useGetNFTMeta'; +export * from './useGetNFTDisplay'; export * from './useIotaAddressValidation'; export * from './useUnlockTimelockedObjectsTransaction'; export * from './useGetAllOwnedObjects'; diff --git a/apps/core/src/hooks/useGetNFTMeta.ts b/apps/core/src/hooks/useGetNFTMeta.ts index b63df42ce5c..1242caac535 100644 --- a/apps/core/src/hooks/useGetNFTMeta.ts +++ b/apps/core/src/hooks/useGetNFTMeta.ts @@ -11,7 +11,7 @@ export type NFTMetadata = { url: string; }; -export function useGetNFTMeta(objectID: string) { +export function useGetNFTDisplay(objectID: string) { const resp = useGetObject(objectID); const nftMeta = useMemo(() => { if (!resp.data) return null; diff --git a/apps/core/src/hooks/useNftDetails.ts b/apps/core/src/hooks/useNftDetails.ts index 6e1941e4579..f76001b0273 100644 --- a/apps/core/src/hooks/useNftDetails.ts +++ b/apps/core/src/hooks/useNftDetails.ts @@ -1,7 +1,7 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 import { - useGetNFTMeta, + useGetNFTDisplay, useOwnedNFT, useNFTBasicData, useGetKioskContents, @@ -23,14 +23,15 @@ export function useNftDetails(nftId: string, accountAddress: string | null) { const isContainedInKiosk = data?.lookup.get(nftId!); const kioskItem = data?.list.find((k) => k.data?.objectId === nftId); - const { data: isTransferable } = useIsAssetTransferable(objectData); + const { data: isAssetTransferable, isLoading: isCheckingAssetTransferability } = + useIsAssetTransferable(objectData); const { nftFields } = useNFTBasicData(objectData); - const { data: nftMeta, isPending: isPendingMeta } = useGetNFTMeta(nftId); + const { data: nftDisplayData, isPending: isPendingNftDislpay } = useGetNFTDisplay(nftId); - const nftName = nftMeta?.name || formatAddress(nftId); - const nftImageUrl = nftMeta?.imageUrl || ''; + const nftName = nftDisplayData?.name || formatAddress(nftId); + const nftImageUrl = nftDisplayData?.imageUrl || ''; // Extract either the attributes, or use the top-level NFT fields: const { keys: metaKeys, values: metaValues } = @@ -75,19 +76,23 @@ export function useNftDetails(nftId: string, accountAddress: string | null) { } } + const isLoading = isNftLoading || isCheckingAssetTransferability || isPendingNftDislpay; + return { + isLoading, objectData, isNftLoading, nftName, nftImageUrl, ownerAddress, - isTransferable, + isCheckingAssetTransferability, + isAssetTransferable, metaKeys, metaValues, formatMetaValue, isContainedInKiosk, kioskItem, - nftMeta, - isPendingMeta, + nftDisplayData, + isPendingNftDislpay, }; } diff --git a/apps/wallet-dashboard/app/(protected)/assets/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/page.tsx index 84fbcca9bfd..ed96fe0a519 100644 --- a/apps/wallet-dashboard/app/(protected)/assets/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/assets/page.tsx @@ -29,7 +29,7 @@ export default function AssetsDashboardPage(): React.JSX.Element { const [selectedAsset, setSelectedAsset] = useState<IotaObjectData | null>(null); const [selectedCategory, setSelectedCategory] = useState<AssetCategory>(AssetCategory.Visual); const account = useCurrentAccount(); - const { data, isFetching, fetchNextPage, hasNextPage, refetch } = useGetOwnedObjects( + const { data, isFetching, fetchNextPage, hasNextPage } = useGetOwnedObjects( account?.address, undefined, OBJECTS_PER_REQ, @@ -51,7 +51,7 @@ export default function AssetsDashboardPage(): React.JSX.Element { } } - function onClickAsset(asset: IotaObjectData) { + function onAssetClick(asset: IotaObjectData) { setSelectedAsset(asset); } @@ -73,17 +73,13 @@ export default function AssetsDashboardPage(): React.JSX.Element { <AssetList assets={assets} selectedCategory={selectedCategory} - onClick={onClickAsset} + onClick={onAssetClick} hasNextPage={hasNextPage} isFetchingNextPage={isFetching} fetchNextPage={fetchNextPage} /> {selectedAsset && ( - <AssetDialog - onClose={() => setSelectedAsset(null)} - onSent={() => refetch()} - asset={selectedAsset} - /> + <AssetDialog onClose={() => setSelectedAsset(null)} asset={selectedAsset} /> )} </div> </Panel> diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx index 502ff5a41ba..ec3d8d38d11 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx @@ -14,7 +14,6 @@ import { NotificationType } from '@/stores/notificationStore'; interface AssetsDialogProps { onClose: () => void; - onSent?: () => void; asset: IotaObjectData; } @@ -26,7 +25,7 @@ const INITIAL_VALUES: FormValues = { to: '', }; -export function AssetDialog({ onClose, onSent, asset }: AssetsDialogProps): JSX.Element { +export function AssetDialog({ onClose, asset }: AssetsDialogProps): JSX.Element { const [view, setView] = useState<AssetsDialogView>(AssetsDialogView.Details); const account = useCurrentAccount(); const activeAddress = account?.address ?? ''; @@ -49,8 +48,6 @@ export function AssetDialog({ onClose, onSent, asset }: AssetsDialogProps): JSX. addNotification('Transfer transaction successful', NotificationType.Success); onClose(); setView(AssetsDialogView.Details); - await new Promise((resolve) => setTimeout(resolve, 1000)); - onSent?.(); } catch { addNotification('Transfer transaction failed', NotificationType.Error); } diff --git a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx index ceed115228f..6ca3452ffa6 100644 --- a/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/Assets/views/DetailsView.tsx @@ -33,9 +33,9 @@ export function DetailsView({ onClose, asset, onSend }: DetailsViewProps) { const { nftName, nftImageUrl, - nftMeta, + nftDisplayData, ownerAddress, - isTransferable, + isAssetTransferable, metaKeys, metaValues, formatMetaValue, @@ -44,11 +44,12 @@ export function DetailsView({ onClose, asset, onSend }: DetailsViewProps) { } = useNftDetails(objectId, senderAddress); function handleMoreAboutKiosk() { - window.open('https://wiki.iota.org/', '_blank'); + window.open('https://docs.iota.org/references/ts-sdk/kiosk/', '_blank'); } function handleMarketplace() { - window.open('https://wiki.iota.org/', '_blank'); + // TODO: https://github.com/iotaledger/iota/issues/4024 + window.open('https://docs.iota.org/references/ts-sdk/kiosk/', '_blank'); } return ( @@ -71,32 +72,32 @@ export function DetailsView({ onClose, asset, onSend }: DetailsViewProps) { <div className="flex w-full flex-col gap-md"> <div className="flex flex-col gap-xxxs"> <span className="text-title-lg text-neutral-10 dark:text-neutral-92"> - {nftMeta?.name} + {nftDisplayData?.name} </span> - {nftMeta?.description ? ( + {nftDisplayData?.description ? ( <span className="text-body-md text-neutral-60"> - {nftMeta?.description} + {nftDisplayData?.description} </span> ) : null} </div> - {(nftMeta?.projectUrl || !!nftMeta?.creator) && ( + {(nftDisplayData?.projectUrl || !!nftDisplayData?.creator) && ( <div className="flex flex-col gap-xs"> - {nftMeta?.projectUrl && ( + {nftDisplayData?.projectUrl && ( <KeyValueInfo keyText="Website" value={ - <Link href={nftMeta?.projectUrl}> - {nftMeta?.projectUrl} + <Link href={nftDisplayData?.projectUrl}> + {nftDisplayData?.projectUrl} </Link> } fullwidth /> )} - {nftMeta?.creator && ( + {nftDisplayData?.creator && ( <KeyValueInfo keyText="Creator" - value={nftMeta?.creator ?? '-'} + value={nftDisplayData?.creator ?? '-'} fullwidth /> )} @@ -174,7 +175,12 @@ export function DetailsView({ onClose, asset, onSend }: DetailsViewProps) { /> </div> ) : ( - <Button disabled={!isTransferable} onClick={onSend} text="Send" fullWidth /> + <Button + disabled={!isAssetTransferable} + onClick={onSend} + text="Send" + fullWidth + /> )} </div> </LayoutFooter> diff --git a/apps/wallet-dashboard/components/tiles/VisualAssetTile.tsx b/apps/wallet-dashboard/components/tiles/VisualAssetTile.tsx index 6fbc03801c4..642d06daff0 100644 --- a/apps/wallet-dashboard/components/tiles/VisualAssetTile.tsx +++ b/apps/wallet-dashboard/components/tiles/VisualAssetTile.tsx @@ -5,7 +5,7 @@ import { IotaObjectData } from '@iota/iota-sdk/client'; import React from 'react'; -import { useGetNFTMeta } from '@iota/core'; +import { useGetNFTDisplay } from '@iota/core'; import { FlexDirection } from '@/lib/ui/enums'; import { VisualAssetCard, VisualAssetType, type VisualAssetCardProps } from '@iota/apps-ui-kit'; @@ -20,7 +20,7 @@ export function VisualAssetTile({ onIconClick, icon, }: AssetCardProps): React.JSX.Element | null { - const { data: nftMeta } = useGetNFTMeta(asset.objectId); + const { data: nftMeta } = useGetNFTDisplay(asset.objectId); if (!asset.display || !nftMeta || !nftMeta.imageUrl) { return null; diff --git a/apps/wallet/src/ui/app/components/nft-display/index.tsx b/apps/wallet/src/ui/app/components/nft-display/index.tsx index dec1953fc94..779afa87e49 100644 --- a/apps/wallet/src/ui/app/components/nft-display/index.tsx +++ b/apps/wallet/src/ui/app/components/nft-display/index.tsx @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Loading, NftImage } from '_components'; -import { isKioskOwnerToken, useGetNFTMeta, useGetObject, useKioskClient } from '@iota/core'; +import { isKioskOwnerToken, useGetNFTDisplay, useGetObject, useKioskClient } from '@iota/core'; import { formatAddress } from '@iota/iota-sdk/utils'; import { cva } from 'class-variance-authority'; import type { VariantProps } from 'class-variance-authority'; @@ -42,7 +42,7 @@ export function NFTDisplayCard({ onIconClick, }: NFTDisplayCardProps) { const { data: objectData } = useGetObject(objectId); - const { data: nftMeta, isPending } = useGetNFTMeta(objectId); + const { data: nftMeta, isPending } = useGetNFTDisplay(objectId); const nftName = nftMeta?.name || formatAddress(objectId); const nftImageUrl = nftMeta?.imageUrl || ''; const video = useResolveVideo(objectData); diff --git a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx index 5302a358912..062577a10b1 100644 --- a/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx +++ b/apps/wallet/src/ui/app/pages/home/nft-details/index.tsx @@ -5,7 +5,7 @@ import { useActiveAddress } from '_app/hooks/useActiveAddress'; import { ExplorerLink, ExplorerLinkType, Loading, NFTDisplayCard, PageTemplate } from '_components'; import { useUnlockedGuard } from '_src/ui/app/hooks/useUnlockedGuard'; -import { useNFTBasicData, useNftDetails, Collapsible, useIsAssetTransferable } from '@iota/core'; +import { useNFTBasicData, useNftDetails, Collapsible } from '@iota/core'; import { formatAddress } from '@iota/iota-sdk/utils'; import cl from 'clsx'; import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom'; @@ -17,9 +17,8 @@ function NFTDetailsPage() { const nftId = searchParams.get('objectId'); const accountAddress = useActiveAddress(); const { - nftMeta, - isPendingMeta, - isNftLoading, + nftDisplayData, + isLoading, ownerAddress, objectData, metaKeys, @@ -27,14 +26,12 @@ function NFTDetailsPage() { formatMetaValue, isContainedInKiosk, kioskItem, + isAssetTransferable, } = useNftDetails(nftId || '', accountAddress); - const { data: isAssetTransferable, isLoading: isCheckingAssetTransferability } = - useIsAssetTransferable(objectData); const { fileExtensionType, filePath } = useNFTBasicData(objectData); const isGuardLoading = useUnlockedGuard(); - const isPending = - isNftLoading || isPendingMeta || isGuardLoading || isCheckingAssetTransferability; + const isPending = isLoading || isGuardLoading; function handleMoreAboutKiosk() { window.open('https://docs.iota.org/references/ts-sdk/kiosk/', '_blank'); @@ -83,31 +80,32 @@ function NFTDetailsPage() { <div className="flex flex-col gap-md"> <div className="flex flex-col gap-xxxs"> <span className="text-title-lg text-neutral-10 dark:text-neutral-92"> - {nftMeta?.name} + {nftDisplayData?.name} </span> - {nftMeta?.description ? ( + {nftDisplayData?.description ? ( <span className="text-body-md text-neutral-60"> - {nftMeta?.description} + {nftDisplayData?.description} </span> ) : null} </div> - {(nftMeta?.projectUrl || nftMeta?.creator) && ( + {(nftDisplayData?.projectUrl || + nftDisplayData?.creator) && ( <div className="flex flex-col gap-xs"> - {nftMeta?.projectUrl && ( + {nftDisplayData?.projectUrl && ( <KeyValueInfo keyText="Website" value={ - <Link to={nftMeta?.projectUrl}> - {nftMeta?.projectUrl} + <Link to={nftDisplayData?.projectUrl}> + {nftDisplayData?.projectUrl} </Link> } fullwidth /> )} - {nftMeta?.creator && ( + {nftDisplayData?.creator && ( <KeyValueInfo keyText="Creator" - value={nftMeta?.creator ?? '-'} + value={nftDisplayData?.creator ?? '-'} fullwidth /> )} diff --git a/apps/wallet/src/ui/app/pages/home/nfts/HiddenAsset.tsx b/apps/wallet/src/ui/app/pages/home/nfts/HiddenAsset.tsx index 4651fc56e7a..92ac60bc862 100644 --- a/apps/wallet/src/ui/app/pages/home/nfts/HiddenAsset.tsx +++ b/apps/wallet/src/ui/app/pages/home/nfts/HiddenAsset.tsx @@ -10,7 +10,7 @@ import { useHiddenAssets } from '../assets/HiddenAssetsProvider'; import { getKioskIdFromOwnerCap, isKioskOwnerToken, - useGetNFTMeta, + useGetNFTDisplay, useGetObject, useKioskClient, } from '@iota/core'; @@ -44,7 +44,7 @@ export default function HiddenAsset(item: HiddenAssetProps) { const navigate = useNavigate(); const { objectId, type } = item.data!; const { data: objectData } = useGetObject(objectId); - const { data: nftMeta } = useGetNFTMeta(objectId); + const { data: nftMeta } = useGetNFTDisplay(objectId); const nftName = nftMeta?.name || formatAddress(objectId); const nftImageUrl = nftMeta?.imageUrl || ''; From 18789db1cc36a3c75e9c1e0c73588ff0a68989b9 Mon Sep 17 00:00:00 2001 From: Panteleymonchuk <panteleymonchuk@gmail.com> Date: Wed, 4 Dec 2024 14:11:01 +0200 Subject: [PATCH 14/14] refactor(dashboard): remove unused asset details page --- .../(protected)/assets/[objectId]/page.tsx | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 apps/wallet-dashboard/app/(protected)/assets/[objectId]/page.tsx diff --git a/apps/wallet-dashboard/app/(protected)/assets/[objectId]/page.tsx b/apps/wallet-dashboard/app/(protected)/assets/[objectId]/page.tsx deleted file mode 100644 index 563d7a9e9c0..00000000000 --- a/apps/wallet-dashboard/app/(protected)/assets/[objectId]/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -'use client'; - -import React, { useCallback } from 'react'; -import { useParams } from 'next/navigation'; -import { Button, RouteLink, SendAssetPopup, VisualAssetDetailsCard } from '@/components'; -import { useIsAssetTransferable, useGetObject } from '@iota/core'; -import { usePopups } from '@/hooks'; -import { useCurrentAccount } from '@iota/dapp-kit'; -import { ASSETS_ROUTE } from '@/lib/constants/routes.constants'; - -const VisualAssetDetailPage = () => { - const params = useParams(); - const objectId = params.objectId as string; - const { data: asset } = useGetObject(objectId); - const { data: isAssetTransferable } = useIsAssetTransferable(asset?.data); - const activeAccount = useCurrentAccount(); - - const { openPopup, closePopup } = usePopups(); - - const showSendAssetPopup = useCallback(() => { - if (asset?.data) { - openPopup(<SendAssetPopup asset={asset?.data} onClose={closePopup} />); - } - }, [asset, openPopup, closePopup]); - - return ( - <div className="flex h-full w-full flex-col space-y-4 px-40"> - <RouteLink path={ASSETS_ROUTE.path} title="Back" /> - {asset?.data ? ( - <VisualAssetDetailsCard key={asset.data.objectId} asset={asset.data} /> - ) : ( - <div className="flex justify-center p-20">Asset not found</div> - )} - {isAssetTransferable && activeAccount ? ( - <Button onClick={showSendAssetPopup}>Send Asset</Button> - ) : null} - </div> - ); -}; - -export default VisualAssetDetailPage;