Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wallet-dashboard): style selected visual Assets #4085

Merged
merged 29 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4074f2b
feat(wallet-dashboard): style selected visual Assets.
panteleymonchuk Nov 15, 2024
9bb5dd0
refactor(core): destructure metaKeys and metaValues from attributes
panteleymonchuk Nov 25, 2024
1f2eb82
refactor(wallet): move Collapsible component to core.
panteleymonchuk Nov 25, 2024
f9530b1
feat(dashboard): integrate useAssetsDialog for asset details view
panteleymonchuk Nov 25, 2024
934cd96
Merge remote-tracking branch 'origin/develop' into tooling-dashboard/…
panteleymonchuk Nov 26, 2024
d055db0
fix(assets): update import path and enhance text styling in DetailsView
panteleymonchuk Nov 26, 2024
2a2daeb
Merge remote-tracking branch 'origin/develop' into tooling-dashboard/…
panteleymonchuk Nov 27, 2024
6304492
Merge remote-tracking branch 'origin/develop' into tooling-dashboard/…
panteleymonchuk Nov 27, 2024
c22a756
Merge remote-tracking branch 'origin/develop' into tooling-dashboard/…
panteleymonchuk Nov 27, 2024
124ddec
Merge branch 'develop' into tooling-dashboard/style-selected-asset
panteleymonchuk Nov 28, 2024
fc404d8
refactor(wallet-dashboard): move state to page
panteleymonchuk Nov 28, 2024
7bd0c26
Merge branch 'develop' into tooling-dashboard/style-selected-asset
panteleymonchuk Nov 28, 2024
61a13f8
refactor(wallet-dashboard): rename handler functions for consistency …
panteleymonchuk Nov 28, 2024
ed3e381
Merge remote-tracking branch 'origin/develop' into tooling-dashboard/…
panteleymonchuk Nov 29, 2024
336468e
fix(dashboard): PR conflicts
panteleymonchuk Nov 29, 2024
4ce390b
refactor(dashboard): update state for asset view, improve code
panteleymonchuk Nov 29, 2024
16110bb
Merge branch 'develop' into tooling-dashboard/style-selected-asset
brancoder Nov 29, 2024
0bd5c50
Merge remote-tracking branch 'origin/develop' into tooling-dashboard/…
panteleymonchuk Dec 2, 2024
84b2dba
fix(wallet-dashboard): unify asset transfer success and error handlin…
panteleymonchuk Dec 2, 2024
21f6471
Merge branch 'develop' into tooling-dashboard/style-selected-asset
panteleymonchuk Dec 3, 2024
f6992bc
refactor(dashboard): adjust z-index for dialog and notifications;
panteleymonchuk Dec 3, 2024
e8e4e9d
Merge branch 'develop' into tooling-dashboard/style-selected-asset
panteleymonchuk Dec 3, 2024
127064a
feat(dashboard): add refetch functionality after asset transfered
panteleymonchuk Dec 3, 2024
1848cc5
refactor(dashboard): rename callbacks for clarity in AssetDialog
panteleymonchuk Dec 4, 2024
78517ee
Merge branch 'develop' into tooling-dashboard/style-selected-asset
panteleymonchuk Dec 4, 2024
1327770
refactor(dashboard, cove): rename hooks, remove duplication.
panteleymonchuk Dec 4, 2024
4d2d4c5
Merge branch 'develop' into tooling-dashboard/style-selected-asset
panteleymonchuk Dec 4, 2024
18789db
refactor(dashboard): remove unused asset details page
panteleymonchuk Dec 4, 2024
e1ad666
Merge branch 'develop' into tooling-dashboard/style-selected-asset
marc2332 Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/core/src/components/collapsible/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export * from './Collapsible';
2 changes: 1 addition & 1 deletion apps/core/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ export * from './coin';
export * from './icon';
export * from './Inputs';
export * from './QR';

export * from './collapsible';
export * from './providers';
5 changes: 4 additions & 1 deletion apps/core/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -43,6 +43,9 @@ export * from './useTransactionData';
export * from './useGetStakingValidatorDetails';
export * from './useCursorPagination';
export * from './useTheme';
export * from './useNFTBasicData';
export * from './useOwnedNFT';
export * from './useNftDetails';
export * from './useCountdownByTimestamp';

export * from './stake';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Modifications Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { useGetObject } from './';
import { useGetObject } from '.';
import { useMemo } from 'react';

export type NFTMetadata = {
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
98 changes: 98 additions & 0 deletions apps/core/src/hooks/useNftDetails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0
import {
useGetNFTDisplay,
useOwnedNFT,
useNFTBasicData,
useGetKioskContents,
useIsAssetTransferable,
} from './';
import { formatAddress } from '@iota/iota-sdk/utils';
import { truncateString } from '../utils';

type NftField = { keys: string[]; values: string[] };

type NftFields = {
metadata?: { fields?: { attributes?: { fields?: NftField } } };
};

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 { data: isAssetTransferable, isLoading: isCheckingAssetTransferability } =
useIsAssetTransferable(objectData);

const { nftFields } = useNFTBasicData(objectData);

const { data: nftDisplayData, isPending: isPendingNftDislpay } = useGetNFTDisplay(nftId);

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 } =
(nftFields as NftFields)?.metadata?.fields?.attributes?.fields ||
Object.entries(nftFields ?? {})
.filter(([key]) => key !== 'id')
.reduce<NftField>(
(acc, [key, value]) => {
acc.keys.push(key);
acc.values.push(value as string);
return acc;
},
{ keys: [], 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,
};
}
}

const isLoading = isNftLoading || isCheckingAssetTransferability || isPendingNftDislpay;

return {
isLoading,
objectData,
isNftLoading,
nftName,
nftImageUrl,
ownerAddress,
isCheckingAssetTransferability,
isAssetTransferable,
metaKeys,
metaValues,
formatMetaValue,
isContainedInKiosk,
kioskItem,
nftDisplayData,
isPendingNftDislpay,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions apps/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from './getDelegationDataByStakeId';
export * from './api-env';
export * from './getExplorerPaths';
export * from './getExplorerLink';
export * from './truncateString';

export * from './stake';
export * from './transaction';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down

This file was deleted.

10 changes: 10 additions & 0 deletions apps/wallet-dashboard/app/(protected)/assets/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { IotaObjectData } from '@iota/iota-sdk/client';
import { useState } from 'react';
import { AssetCategory } from '@/lib/enums';
import { AssetList } from '@/components/AssetsList';
import { AssetDialog } from '@/components/Dialogs/Assets';

const OBJECTS_PER_REQ = 50;

Expand All @@ -25,6 +26,7 @@ const ASSET_CATEGORIES: { label: string; value: AssetCategory }[] = [
];

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(
Expand All @@ -49,6 +51,10 @@ export default function AssetsDashboardPage(): React.JSX.Element {
}
}

function onAssetClick(asset: IotaObjectData) {
setSelectedAsset(asset);
}

return (
<Panel>
<Title title="Assets" size={TitleSize.Medium} />
Expand All @@ -67,10 +73,14 @@ export default function AssetsDashboardPage(): React.JSX.Element {
<AssetList
assets={assets}
selectedCategory={selectedCategory}
onClick={onAssetClick}
hasNextPage={hasNextPage}
isFetchingNextPage={isFetching}
fetchNextPage={fetchNextPage}
/>
{selectedAsset && (
<AssetDialog onClose={() => setSelectedAsset(null)} asset={selectedAsset} />
)}
</div>
</Panel>
);
Expand Down
9 changes: 8 additions & 1 deletion apps/wallet-dashboard/components/AssetsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface AssetListProps {
hasNextPage: boolean;
isFetchingNextPage: boolean;
fetchNextPage: () => void;
onClick: (asset: IotaObjectData) => void;
}

const ASSET_LAYOUT: Record<AssetCategory, string> = {
Expand All @@ -29,6 +30,7 @@ export function AssetList({
hasNextPage,
isFetchingNextPage,
fetchNextPage,
onClick,
}: AssetListProps): React.JSX.Element {
const observerElem = useRef<HTMLDivElement | null>(null);
const { isIntersecting } = useOnScreen(observerElem);
Expand All @@ -43,7 +45,12 @@ export function AssetList({
return (
<div className={cl('max-h-[600px]', 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 ref={observerElem}>
{isSpinnerVisible ? (
Expand Down
81 changes: 81 additions & 0 deletions apps/wallet-dashboard/components/Dialogs/Assets/AssetDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import React, { useState } 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 './constants';
import { useCreateSendAssetTransaction, useNotifications } from '@/hooks';
import { NotificationType } from '@/stores/notificationStore';

interface AssetsDialogProps {
onClose: () => void;
asset: IotaObjectData;
}

interface FormValues {
to: string;
}

const INITIAL_VALUES: FormValues = {
to: '',
};

export function AssetDialog({ onClose, asset }: AssetsDialogProps): JSX.Element {
const [view, setView] = useState<AssetsDialogView>(AssetsDialogView.Details);
const account = useCurrentAccount();
const activeAddress = account?.address ?? '';
const objectId = asset?.objectId ?? '';
const { addNotification } = useNotifications();
const validationSchema = createNftSendValidationSchema(activeAddress, objectId);

const { mutation: sendAsset } = useCreateSendAssetTransaction(objectId);

const formik = useFormik<FormValues>({
initialValues: INITIAL_VALUES,
validationSchema: validationSchema,
onSubmit: onSubmit,
validateOnChange: true,
});

async function onSubmit(values: FormValues) {
try {
await sendAsset.mutateAsync(values.to);
addNotification('Transfer transaction successful', NotificationType.Success);
onClose();
setView(AssetsDialogView.Details);
} catch {
addNotification('Transfer transaction failed', NotificationType.Error);
}
}

function onDetailsSend() {
setView(AssetsDialogView.Send);
}

function onSendViewBack() {
setView(AssetsDialogView.Details);
}
function onOpenChange() {
setView(AssetsDialogView.Details);
onClose();
}
return (
<Dialog open onOpenChange={onOpenChange}>
<FormikProvider value={formik}>
<>
{view === AssetsDialogView.Details && (
<DetailsView asset={asset} onClose={onOpenChange} onSend={onDetailsSend} />
)}
{view === AssetsDialogView.Send && (
<SendView asset={asset} onClose={onOpenChange} onBack={onSendViewBack} />
)}
</>
</FormikProvider>
</Dialog>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

export enum AssetsDialogView {
Details = 'Details',
Send = 'Send',
}
Loading
Loading