-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into dev-tools/de-deprecate-clap
- Loading branch information
Showing
199 changed files
with
1,868 additions
and
1,269 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
Oops, something went wrong.