-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(wallet-dashboard): Improve assets filter (#4512)
* refactor(wallet): move notification to separate component. * feat(wallet): enhance hidden asset functionality with undo option and notifications * feat(wallet): add undo functionality for showing hidden assets with notifications * feat(core): move HiddenAssetsProvider. * feat(core): move useGetNFTs. * feat(core): update useGetNFTs to accept filter and improve asset fetching * refactor(dashboard): remove 'Hidden' asset category and related logic * refactor(wallet-dashboard): remove 'Hidden' asset category from layout * refactor(dashboard): remove HiddenAssets context, update default select asset logic. * feat(core): add refetch capability to useGetNFTs hook * refactor(core): improve hide/show logic * refactor(core): remove undo functionality and streamline asset visibility management * refactor(core): simplify asset visibility management and improve error handling * refactor(wallet): rename MoveAssetNotification to MovedAssetNotification * refactor(dashboard): adapt logic to useGetNFTs hook. * fix(wallet-dashboard): improve asset loading logic and conditional rendering * feat(wallet-dashboard): move logic for page to the hook * fix(dashboard): load more if we have intersection * feat(ui-kit, dashboard): add disabled state to Chip component and update assets logic * refactor(core): reorganize usePageAssets hook into ui directory * refactor(core): move filter from param to hook direct.
- Loading branch information
1 parent
4f02c12
commit 55b6b80
Showing
25 changed files
with
512 additions
and
454 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// Copyright (c) Mysten Labs, Inc. | ||
// Modifications Copyright (c) 2024 IOTA Stiftung | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import { get, set } from 'idb-keyval'; | ||
import { | ||
PropsWithChildren, | ||
createContext, | ||
useCallback, | ||
useContext, | ||
useEffect, | ||
useState, | ||
useRef, | ||
} from 'react'; | ||
|
||
const HIDDEN_ASSET_IDS = 'hidden-asset-ids'; | ||
|
||
export type HiddenAssets = | ||
| { | ||
type: 'loading'; | ||
} | ||
| { | ||
type: 'loaded'; | ||
assetIds: string[]; | ||
}; | ||
|
||
interface HiddenAssetContext { | ||
hiddenAssets: HiddenAssets; | ||
hideAsset: (assetId: string) => string | void; | ||
showAsset: (assetId: string) => string | void; | ||
} | ||
|
||
export const HiddenAssetsContext = createContext<HiddenAssetContext>({ | ||
hiddenAssets: { | ||
type: 'loading', | ||
}, | ||
hideAsset: () => {}, | ||
showAsset: () => {}, | ||
}); | ||
|
||
export const HiddenAssetsProvider = ({ children }: PropsWithChildren) => { | ||
const [hiddenAssets, setHiddenAssets] = useState<HiddenAssets>({ | ||
type: 'loading', | ||
}); | ||
const hiddenAssetIdsRef = useRef<string[]>([]); | ||
|
||
useEffect(() => { | ||
(async () => { | ||
try { | ||
const hiddenAssetsFromStorage = (await get<string[]>(HIDDEN_ASSET_IDS)) ?? []; | ||
hiddenAssetIdsRef.current = hiddenAssetsFromStorage; | ||
setHiddenAssetIds(hiddenAssetsFromStorage); | ||
} catch (error) { | ||
console.error('Failed to load hidden assets from storage:', error); | ||
setHiddenAssetIds([]); | ||
} | ||
})(); | ||
}, []); | ||
|
||
function setHiddenAssetIds(hiddenAssetIds: string[]) { | ||
hiddenAssetIdsRef.current = hiddenAssetIds; | ||
setHiddenAssets({ | ||
type: 'loaded', | ||
assetIds: hiddenAssetIds, | ||
}); | ||
} | ||
|
||
const syncIdb = useCallback(async (nextState: string[], prevState: string[]) => { | ||
try { | ||
await set(HIDDEN_ASSET_IDS, nextState); | ||
} catch (error) { | ||
console.error('Error syncing with IndexedDB:', error); | ||
// Revert to the previous state on failure | ||
setHiddenAssetIds(prevState); | ||
} | ||
}, []); | ||
|
||
const hideAsset = useCallback((assetId: string) => { | ||
const prevIds = [...hiddenAssetIdsRef.current]; | ||
const newHiddenAssetIds = Array.from(new Set([...hiddenAssetIdsRef.current, assetId])); | ||
setHiddenAssetIds(newHiddenAssetIds); | ||
syncIdb(newHiddenAssetIds, prevIds); | ||
return assetId; | ||
}, []); | ||
|
||
const showAsset = useCallback((assetId: string) => { | ||
// Ensure the asset exists in the hidden list | ||
if (!hiddenAssetIdsRef.current.includes(assetId)) return; | ||
|
||
const prevIds = [...hiddenAssetIdsRef.current]; | ||
// Compute the new list of hidden assets | ||
const updatedHiddenAssetIds = hiddenAssetIdsRef.current.filter((id) => id !== assetId); | ||
setHiddenAssetIds(updatedHiddenAssetIds); | ||
syncIdb(updatedHiddenAssetIds, prevIds); | ||
}, []); | ||
|
||
return ( | ||
<HiddenAssetsContext.Provider | ||
value={{ | ||
hiddenAssets, | ||
hideAsset, | ||
showAsset, | ||
}} | ||
> | ||
{children} | ||
</HiddenAssetsContext.Provider> | ||
); | ||
}; | ||
|
||
export const useHiddenAssets = () => { | ||
return useContext(HiddenAssetsContext); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ | |
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
export * from './ThemeContext'; | ||
export * from './HiddenAssetsProvider'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 './usePageAssets'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
// Copyright (c) 2024 IOTA Stiftung | ||
// SPDX-License-Identifier: Apache-2.0 | ||
import { useState, useMemo, useRef, useEffect } from 'react'; | ||
import { useGetNFTs, HiddenAssets, useOnScreen } from '../..'; | ||
|
||
export enum AssetCategory { | ||
Visual = 'Visual', | ||
Other = 'Other', | ||
Hidden = 'Hidden', | ||
} | ||
|
||
export function usePageAssets(address: string | null, hiddenAssets?: HiddenAssets) { | ||
const [selectedAssetCategory, setSelectedAssetCategory] = useState<AssetCategory | null>(null); | ||
const observerElem = useRef<HTMLDivElement | null>(null); | ||
const { isIntersecting } = useOnScreen(observerElem); | ||
const { | ||
data: ownedAssets, | ||
hasNextPage, | ||
isFetchingNextPage, | ||
fetchNextPage, | ||
error, | ||
isPending, | ||
isError, | ||
isFetching, | ||
refetch, | ||
} = useGetNFTs(address, hiddenAssets); | ||
|
||
const isAssetsLoaded = !!ownedAssets; | ||
const isSpinnerVisible = isFetchingNextPage && hasNextPage; | ||
|
||
const filteredAssets = (() => { | ||
if (!ownedAssets) return []; | ||
switch (selectedAssetCategory) { | ||
case AssetCategory.Visual: | ||
return ownedAssets.visual; | ||
case AssetCategory.Other: | ||
return ownedAssets.other; | ||
default: | ||
return []; | ||
} | ||
})(); | ||
|
||
const filteredHiddenAssets = useMemo(() => { | ||
return ( | ||
ownedAssets?.hidden | ||
.flatMap((data) => { | ||
return { | ||
data: data, | ||
display: data?.display?.data, | ||
}; | ||
}) | ||
.sort((nftA, nftB) => { | ||
const nameA = nftA.display?.name || ''; | ||
const nameB = nftB.display?.name || ''; | ||
|
||
if (nameA < nameB) { | ||
return -1; | ||
} else if (nameA > nameB) { | ||
return 1; | ||
} | ||
return 0; | ||
}) ?? [] | ||
); | ||
}, [ownedAssets]); | ||
|
||
// Fetch the next page if the user scrolls to the bottom of the page | ||
useEffect(() => { | ||
if (isIntersecting && hasNextPage && !isFetchingNextPage) { | ||
fetchNextPage(); | ||
} | ||
}, [isIntersecting, fetchNextPage, hasNextPage, isFetchingNextPage]); | ||
|
||
// select the default category if no category is selected and assets are loaded | ||
useEffect(() => { | ||
let computeSelectedCategory = false; | ||
if ( | ||
(selectedAssetCategory === AssetCategory.Visual && ownedAssets?.visual.length === 0) || | ||
(selectedAssetCategory === AssetCategory.Other && ownedAssets?.other.length === 0) || | ||
(selectedAssetCategory === AssetCategory.Hidden && ownedAssets?.hidden.length === 0) || | ||
!selectedAssetCategory | ||
) { | ||
computeSelectedCategory = true; | ||
} | ||
if (computeSelectedCategory && ownedAssets) { | ||
const defaultCategory = | ||
ownedAssets.visual.length > 0 | ||
? AssetCategory.Visual | ||
: ownedAssets.other.length > 0 | ||
? AssetCategory.Other | ||
: ownedAssets.hidden.length > 0 | ||
? AssetCategory.Hidden | ||
: null; | ||
setSelectedAssetCategory(defaultCategory); | ||
} | ||
}, [ownedAssets]); | ||
|
||
// Fetch the next page if there are no visual assets, other + hidden assets are present in multiples of 50, and there are more pages to fetch | ||
useEffect(() => { | ||
if ( | ||
hasNextPage && | ||
ownedAssets?.visual.length === 0 && | ||
ownedAssets?.other.length + ownedAssets?.hidden.length > 0 && | ||
(ownedAssets.other.length + ownedAssets.hidden.length) % 50 === 0 && | ||
!isFetchingNextPage | ||
) { | ||
fetchNextPage(); | ||
setSelectedAssetCategory(null); | ||
} | ||
}, [hasNextPage, ownedAssets, isFetchingNextPage]); | ||
|
||
return { | ||
// reexport from useGetNFTs | ||
ownedAssets, | ||
hasNextPage, | ||
isFetchingNextPage, | ||
fetchNextPage, | ||
error, | ||
isPending, | ||
isError, | ||
isFetching, | ||
refetch, | ||
|
||
isAssetsLoaded, | ||
filteredAssets, | ||
filteredHiddenAssets, | ||
selectedAssetCategory, | ||
setSelectedAssetCategory, | ||
observerElem, | ||
isSpinnerVisible, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.