11import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
22
3+ import { TokenValidationError } from '@common/errors' ;
34import { parseReamPathItemsByPath } from '@common/utils/parse-utils' ;
5+ import { isGRC20TokenModel } from '@common/validation' ;
46import AdditionalToken from '@components/pages/additional-token/additional-token' ;
57import { AddingType } from '@components/pages/additional-token/additional-token-type-selector' ;
68import { ManageTokenLayout } from '@components/pages/manage-token-layout' ;
79import useAppNavigate from '@hooks/use-app-navigate' ;
10+ import { useDebounce } from '@hooks/use-debounce' ;
811import { useGRC20Token } from '@hooks/use-grc20-token' ;
912import { useGRC20Tokens } from '@hooks/use-grc20-tokens' ;
1013import { useNetwork } from '@hooks/use-network' ;
@@ -23,50 +26,63 @@ const ManageTokenAddedContainer: React.FC = () => {
2326 const [ addingType , setAddingType ] = useState ( AddingType . SEARCH ) ;
2427 const [ manualTokenPath , setManualTokenPath ] = useState ( '' ) ;
2528
26- useEffect ( ( ) => {
27- document . body . addEventListener ( 'click' , closeSelectBox ) ;
28- return ( ) => document . body . removeEventListener ( 'click' , closeSelectBox ) ;
29- } , [ document . body ] ) ;
30-
31- useEffect ( ( ) => {
32- if ( finished ) {
33- goBack ( ) ;
34- }
35- } , [ finished ] ) ;
36-
3729 const { data : grc20Tokens } = useGRC20Tokens ( ) ;
3830
31+ const {
32+ debouncedValue : debouncedManualTokenPath ,
33+ setDebouncedValue : setDebouncedManualTokenPath ,
34+ isLoading : isLoadingDebounce ,
35+ } = useDebounce ( manualTokenPath , 500 ) ;
3936 const { data : manualGRC20Token , isFetching : isFetchingManualGRC20Token } =
40- useGRC20Token ( manualTokenPath ) ;
37+ useGRC20Token ( debouncedManualTokenPath ) ;
38+
39+ const isValidManualGRC20Token = useMemo ( ( ) => {
40+ if ( manualTokenPath === '' ) {
41+ return true ;
42+ }
4143
42- const isValidManualGRC20TokenPath = useMemo ( ( ) => {
4344 try {
4445 parseReamPathItemsByPath ( manualTokenPath ) ;
46+ return true ;
4547 } catch {
4648 return false ;
4749 }
48- return true ;
4950 } , [ manualTokenPath ] ) ;
5051
51- const isLoadingManualGRC20Token = useMemo ( ( ) => {
52- if ( ! isValidManualGRC20TokenPath ) {
53- return false ;
52+ const errorManualGRC20Token = useMemo ( ( ) => {
53+ if ( manualTokenPath === '' ) {
54+ return null ;
5455 }
5556
56- return isFetchingManualGRC20Token ;
57- } , [ isValidManualGRC20TokenPath , isFetchingManualGRC20Token ] ) ;
57+ if ( ! isValidManualGRC20Token || ! manualGRC20Token ) {
58+ return new TokenValidationError ( 'INVALID_REALM_PATH' ) ;
59+ }
60+
61+ const isRegistered = tokenMetainfos . some ( ( tokenMetaInfo ) => {
62+ if ( tokenMetaInfo . tokenId === manualTokenPath ) {
63+ return true ;
64+ }
65+
66+ if ( isGRC20TokenModel ( tokenMetaInfo ) ) {
67+ return tokenMetaInfo . pkgPath === manualTokenPath ;
68+ }
5869
59- const isErrorManualGRC20Token = useMemo ( ( ) => {
60- if ( manualTokenPath === '' ) {
6170 return false ;
71+ } ) ;
72+ if ( isRegistered ) {
73+ return new TokenValidationError ( 'ALREADY_ADDED' ) ;
6274 }
6375
64- if ( isLoadingManualGRC20Token ) {
76+ return null ;
77+ } , [ tokenMetainfos , isLoadingDebounce , manualGRC20Token , manualTokenPath ] ) ;
78+
79+ const isLoadingManualGRC20Token = useMemo ( ( ) => {
80+ if ( ! isValidManualGRC20Token ) {
6581 return false ;
6682 }
6783
68- return manualGRC20Token === null ;
69- } , [ isLoadingManualGRC20Token , manualTokenPath , manualGRC20Token ] ) ;
84+ return isLoadingDebounce || isFetchingManualGRC20Token ;
85+ } , [ isValidManualGRC20Token , isLoadingDebounce , isFetchingManualGRC20Token ] ) ;
7086
7187 const tokenInfos : TokenInfo [ ] = useMemo ( ( ) => {
7288 if ( ! grc20Tokens ) {
@@ -120,6 +136,7 @@ const ManageTokenAddedContainer: React.FC = () => {
120136 setAddingType ( addingType ) ;
121137 setKeyword ( '' ) ;
122138 setManualTokenPath ( '' ) ;
139+ setDebouncedManualTokenPath ( '' ) ;
123140 setSelectedTokenInfo ( null ) ;
124141 setOpened ( false ) ;
125142 setSelected ( false ) ;
@@ -144,6 +161,10 @@ const ManageTokenAddedContainer: React.FC = () => {
144161 } , [ ] ) ;
145162
146163 const onClickAdd = useCallback ( async ( ) => {
164+ if ( errorManualGRC20Token ) {
165+ return ;
166+ }
167+
147168 if ( ! selected || ! selectedTokenInfo || finished ) {
148169 return ;
149170 }
@@ -152,6 +173,17 @@ const ManageTokenAddedContainer: React.FC = () => {
152173 setFinished ( true ) ;
153174 } , [ selected , selectedTokenInfo , finished ] ) ;
154175
176+ useEffect ( ( ) => {
177+ document . body . addEventListener ( 'click' , closeSelectBox ) ;
178+ return ( ) => document . body . removeEventListener ( 'click' , closeSelectBox ) ;
179+ } , [ document . body ] ) ;
180+
181+ useEffect ( ( ) => {
182+ if ( finished ) {
183+ goBack ( ) ;
184+ }
185+ } , [ finished ] ) ;
186+
155187 useEffect ( ( ) => {
156188 if ( addingType === AddingType . SEARCH ) {
157189 return ;
@@ -189,7 +221,7 @@ const ManageTokenAddedContainer: React.FC = () => {
189221 tokenInfos = { tokenInfos ?? [ ] }
190222 manualTokenPath = { manualTokenPath }
191223 isLoadingManualGRC20Token = { isLoadingManualGRC20Token }
192- isErrorManualGRC20Token = { isErrorManualGRC20Token }
224+ errorManualGRC20Token = { errorManualGRC20Token }
193225 selectedTokenInfo = { selectedTokenInfo }
194226 selectAddingType = { selectAddingType }
195227 onChangeKeyword = { onChangeKeyword }
0 commit comments