11import { Trans } from '@lingui/react/macro'
2- import { CollectionList , UserAssetsProvider } from '@masknet/shared'
2+ import { AddCollectiblesModal , CollectionList , UserAssetsProvider } from '@masknet/shared'
33import { NetworkPluginID } from '@masknet/shared-base'
44import { makeStyles } from '@masknet/theme'
55import type { Web3Helper } from '@masknet/web3-helpers'
6- import { useChainContext } from '@masknet/web3-hooks-base'
6+ import { useChainContext , useNetworkContext , useWeb3Connection , useWeb3Hub } from '@masknet/web3-hooks-base'
77import { isSameAddress } from '@masknet/web3-shared-base'
88import type { ChainId } from '@masknet/web3-shared-evm'
99import { alpha , Box , Button , DialogActions } from '@mui/material'
10- import { useCallback , useMemo , useState } from 'react'
10+ import { useCallback , useEffect , useMemo , useState } from 'react'
1111import { useNavigate } from 'react-router-dom'
1212import { NFT_DEFAULT_CHAINS , NFT_RED_PACKET_MAX_SHARES } from '../../constants.js'
1313import { useRedPacket } from '../contexts/RedPacketContext.js'
14+ import { emitter } from '../emitter.js'
15+ import { compact , uniqBy } from 'lodash-es'
1416
1517const useStyles = makeStyles ( ) ( ( theme ) => ( {
1618 container : {
@@ -40,8 +42,12 @@ const gridProps = {
4042}
4143export function SelectCollectibles ( ) {
4244 const { classes } = useStyles ( )
43- const { account, setChainId } = useChainContext < NetworkPluginID . PLUGIN_EVM > ( )
45+ const { account, chainId, setChainId } = useChainContext < NetworkPluginID . PLUGIN_EVM > ( )
46+ const [ assetChainId , setAssetChainId ] = useState < ChainId > ( )
4447 const navigate = useNavigate ( )
48+ const { pluginID } = useNetworkContext ( )
49+ const Web3 = useWeb3Connection ( pluginID )
50+ const Hub = useWeb3Hub ( pluginID )
4551 const { selectedNfts, setSelectedNfts, setCollection } = useRedPacket ( )
4652 const [ pendingNfts , setPendingNfts ] = useState < Web3Helper . NonFungibleAssetAll [ ] > ( selectedNfts )
4753 const handleItemClick = useCallback ( ( nft : Web3Helper . NonFungibleAssetAll ) => {
@@ -61,6 +67,60 @@ export function SelectCollectibles() {
6167 const selectedSet = new Set ( selectedNfts . map ( ( x ) => [ x . chainId , x . address , x . tokenId ] . join ( ':' ) . toLowerCase ( ) ) )
6268 return pendingSet . difference ( selectedSet ) . size === 0
6369 } , [ pendingNfts , selectedNfts ] )
70+
71+ const [ pendingTokenCount , setPendingTokenCount ] = useState ( 0 )
72+ const [ tokens , setTokens ] = useState < Web3Helper . NonFungibleAssetAll [ ] > ( [ ] )
73+ const handleAddCollectibles = useCallback ( async ( ) => {
74+ const results = await AddCollectiblesModal . openAndWaitForClose ( {
75+ pluginID,
76+ chainId : assetChainId || chainId ,
77+ account,
78+ } )
79+ if ( ! results ) return
80+ const [ contract , tokenIds ] = results
81+ const selectedChainId = contract . chainId || assetChainId || chainId
82+ const address = contract . address
83+ setPendingTokenCount ( ( count ) => count + tokenIds . length )
84+ const allSettled = await Promise . allSettled (
85+ tokenIds . map ( async ( tokenId ) => {
86+ const [ asset , token , isOwner ] = await Promise . all ( [
87+ Hub . getNonFungibleAsset ( address , tokenId , {
88+ chainId : selectedChainId ,
89+ account,
90+ } ) ,
91+ Web3 . getNonFungibleToken ( address , tokenId , undefined , {
92+ chainId : selectedChainId ,
93+ } ) ,
94+ Web3 . getNonFungibleTokenOwnership ( address , tokenId , account , undefined , {
95+ chainId : selectedChainId ,
96+ } ) ,
97+ ] )
98+
99+ if ( ! asset ?. contract ?. chainId || ! token . chainId || token . contract ?. chainId !== assetChainId ) return
100+ if ( ! isOwner ) return
101+ return { ...token , ...asset } as Web3Helper . NonFungibleAssetAll
102+ } ) ,
103+ )
104+
105+ setPendingTokenCount ( ( count ) => Math . max ( count - tokenIds . length , 0 ) )
106+ const tokens = compact ( allSettled . map ( ( x ) => ( x . status === 'fulfilled' ? x . value : null ) ) )
107+ if ( ! tokens . length ) return
108+ setTokens ( ( originalTokens ) => {
109+ return uniqBy ( [ ...originalTokens , ...tokens ] , ( x ) => `${ x . contract ?. address } .${ x . tokenId } ` )
110+ } )
111+ } , [ pluginID , assetChainId , chainId , account ] )
112+
113+ const handleSelect = useCallback ( ( assets : Web3Helper . NonFungibleAssetAll [ ] ) => {
114+ setPendingNfts ( assets . length > NFT_RED_PACKET_MAX_SHARES ? assets . slice ( 0 , NFT_RED_PACKET_MAX_SHARES ) : assets )
115+ } , [ ] )
116+
117+ useEffect ( ( ) => {
118+ const unsubscribe = emitter . on ( 'add' , handleAddCollectibles )
119+ return ( ) => {
120+ unsubscribe ( )
121+ }
122+ } , [ handleAddCollectibles ] )
123+
64124 return (
65125 < Box className = { classes . container } >
66126 < UserAssetsProvider
@@ -73,8 +133,18 @@ export function SelectCollectibles() {
73133 maxSelectionDescription = {
74134 < Trans > The maximum number of NFTs to be sold in one collection lucky drop contract is 255.</ Trans >
75135 }
76- selectedAssets = { pendingNfts } >
77- < CollectionList height = { 564 } gridProps = { gridProps } disableWindowScroll onItemClick = { handleItemClick } />
136+ selectedAssets = { pendingNfts }
137+ disableReport >
138+ < CollectionList
139+ height = { 564 }
140+ gridProps = { gridProps }
141+ disableWindowScroll
142+ additionalAssets = { tokens }
143+ pendingAdditionalAssetCount = { pendingTokenCount }
144+ onItemClick = { handleItemClick }
145+ onChainChange = { setAssetChainId as ( chainId ?: Web3Helper . ChainIdAll ) => void }
146+ onSelect = { handleSelect }
147+ />
78148 </ UserAssetsProvider >
79149 < DialogActions className = { classes . dialogActions } >
80150 < Button className = { classes . cancel } fullWidth variant = "outlined" onClick = { ( ) => navigate ( - 1 ) } >
0 commit comments