1
1
import { Trans } from '@lingui/react/macro'
2
- import { CollectionList , UserAssetsProvider } from '@masknet/shared'
2
+ import { AddCollectiblesModal , CollectionList , UserAssetsProvider } from '@masknet/shared'
3
3
import { NetworkPluginID } from '@masknet/shared-base'
4
4
import { makeStyles } from '@masknet/theme'
5
5
import 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'
7
7
import { isSameAddress } from '@masknet/web3-shared-base'
8
8
import type { ChainId } from '@masknet/web3-shared-evm'
9
9
import { alpha , Box , Button , DialogActions } from '@mui/material'
10
- import { useCallback , useMemo , useState } from 'react'
10
+ import { useCallback , useEffect , useMemo , useState } from 'react'
11
11
import { useNavigate } from 'react-router-dom'
12
12
import { NFT_DEFAULT_CHAINS , NFT_RED_PACKET_MAX_SHARES } from '../../constants.js'
13
13
import { useRedPacket } from '../contexts/RedPacketContext.js'
14
+ import { emitter } from '../emitter.js'
15
+ import { compact , uniqBy } from 'lodash-es'
14
16
15
17
const useStyles = makeStyles ( ) ( ( theme ) => ( {
16
18
container : {
@@ -40,8 +42,12 @@ const gridProps = {
40
42
}
41
43
export function SelectCollectibles ( ) {
42
44
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 > ( )
44
47
const navigate = useNavigate ( )
48
+ const { pluginID } = useNetworkContext ( )
49
+ const Web3 = useWeb3Connection ( pluginID )
50
+ const Hub = useWeb3Hub ( pluginID )
45
51
const { selectedNfts, setSelectedNfts, setCollection } = useRedPacket ( )
46
52
const [ pendingNfts , setPendingNfts ] = useState < Web3Helper . NonFungibleAssetAll [ ] > ( selectedNfts )
47
53
const handleItemClick = useCallback ( ( nft : Web3Helper . NonFungibleAssetAll ) => {
@@ -61,6 +67,60 @@ export function SelectCollectibles() {
61
67
const selectedSet = new Set ( selectedNfts . map ( ( x ) => [ x . chainId , x . address , x . tokenId ] . join ( ':' ) . toLowerCase ( ) ) )
62
68
return pendingSet . difference ( selectedSet ) . size === 0
63
69
} , [ 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
+
64
124
return (
65
125
< Box className = { classes . container } >
66
126
< UserAssetsProvider
@@ -73,8 +133,18 @@ export function SelectCollectibles() {
73
133
maxSelectionDescription = {
74
134
< Trans > The maximum number of NFTs to be sold in one collection lucky drop contract is 255.</ Trans >
75
135
}
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
+ />
78
148
</ UserAssetsProvider >
79
149
< DialogActions className = { classes . dialogActions } >
80
150
< Button className = { classes . cancel } fullWidth variant = "outlined" onClick = { ( ) => navigate ( - 1 ) } >
0 commit comments