@rango-dev/widget-embedded 0.40.2-next.8 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CustomTokenModal/CustomTokenModal.constants.d.ts +2 -0
- package/dist/components/CustomTokenModal/CustomTokenModal.constants.d.ts.map +1 -0
- package/dist/components/CustomTokenModal/CustomTokenModal.d.ts.map +1 -1
- package/dist/components/CustomTokenModal/CustomTokenModal.types.d.ts +1 -0
- package/dist/components/CustomTokenModal/CustomTokenModal.types.d.ts.map +1 -1
- package/dist/components/ImportCustomToken/ImportCustomToken.d.ts +4 -0
- package/dist/components/ImportCustomToken/ImportCustomToken.d.ts.map +1 -0
- package/dist/components/ImportCustomToken/ImportCustomToken.types.d.ts +15 -0
- package/dist/components/ImportCustomToken/ImportCustomToken.types.d.ts.map +1 -0
- package/dist/components/ImportCustomToken/index.d.ts +2 -0
- package/dist/components/ImportCustomToken/index.d.ts.map +1 -0
- package/dist/components/Quote/Quote.styles.d.ts +2 -2
- package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
- package/dist/components/SearchInput/SearchInput.types.d.ts +4 -2
- package/dist/components/SearchInput/SearchInput.types.d.ts.map +1 -1
- package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts +2 -2
- package/dist/components/SwapDetailsModal/SwapDetailsModal.types.d.ts.map +1 -1
- package/dist/components/TokenList/TokenList.d.ts.map +1 -1
- package/dist/components/TokenList/TokenList.styles.d.ts +323 -1
- package/dist/components/TokenList/TokenList.styles.d.ts.map +1 -1
- package/dist/components/TokenList/TokenList.types.d.ts +6 -1
- package/dist/components/TokenList/TokenList.types.d.ts.map +1 -1
- package/dist/constants/customTokens.d.ts +1 -0
- package/dist/constants/customTokens.d.ts.map +1 -1
- package/dist/containers/Inputs/Inputs.d.ts.map +1 -1
- package/dist/hooks/useFetchCustomToken.d.ts +8 -4
- package/dist/hooks/useFetchCustomToken.d.ts.map +1 -1
- package/dist/hooks/usePrepareBlockchainList/usePrepareBlockchainList.helpers.d.ts +2 -2
- package/dist/hooks/useSearchCustomTokens/index.d.ts +2 -0
- package/dist/hooks/useSearchCustomTokens/index.d.ts.map +1 -0
- package/dist/hooks/useSearchCustomTokens/types.d.ts +9 -0
- package/dist/hooks/useSearchCustomTokens/types.d.ts.map +1 -0
- package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.constants.d.ts +2 -0
- package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.constants.d.ts.map +1 -0
- package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.d.ts +3 -0
- package/dist/hooks/useSearchCustomTokens/useSearchCustomTokens.d.ts.map +1 -0
- package/dist/hooks/useSyncStoresWithConfig.d.ts.map +1 -1
- package/dist/hooks/useSyncUrlAndStore/useSyncUrlAndStore.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +4 -4
- package/dist/pages/AddCustomTokenPage.d.ts.map +1 -1
- package/dist/pages/CustomTokensPage.d.ts.map +1 -1
- package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.constants.d.ts +3 -0
- package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.constants.d.ts.map +1 -0
- package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.helpers.d.ts +4 -0
- package/dist/pages/SelectSwapItemPage/SelectSwapItemPage.helpers.d.ts.map +1 -0
- package/dist/pages/SelectSwapItemPage/SelectSwapItemsPage.d.ts.map +1 -0
- package/dist/pages/SelectSwapItemPage/index.d.ts +2 -0
- package/dist/pages/SelectSwapItemPage/index.d.ts.map +1 -0
- package/dist/store/app.d.ts +1 -1
- package/dist/store/app.d.ts.map +1 -1
- package/dist/store/quote.d.ts +9 -6
- package/dist/store/quote.d.ts.map +1 -1
- package/dist/store/slices/settings.d.ts +4 -2
- package/dist/store/slices/settings.d.ts.map +1 -1
- package/dist/store/slices/wallets.d.ts +5 -1
- package/dist/store/slices/wallets.d.ts.map +1 -1
- package/dist/store/utils/wallets.d.ts +2 -1
- package/dist/store/utils/wallets.d.ts.map +1 -1
- package/dist/utils/quote.d.ts +1 -1
- package/dist/utils/quote.d.ts.map +1 -1
- package/dist/utils/settings.d.ts +1 -1
- package/dist/widget-embedded.build.json +1 -1
- package/package.json +12 -12
- package/src/components/AppRoutes.tsx +1 -1
- package/src/components/CustomTokenModal/CustomTokenModal.constants.ts +2 -0
- package/src/components/CustomTokenModal/CustomTokenModal.tsx +26 -5
- package/src/components/CustomTokenModal/CustomTokenModal.types.ts +1 -0
- package/src/components/ImportCustomToken/ImportCustomToken.tsx +121 -0
- package/src/components/ImportCustomToken/ImportCustomToken.types.ts +18 -0
- package/src/components/ImportCustomToken/index.ts +1 -0
- package/src/components/NotificationContent/NotificationContent.tsx +2 -2
- package/src/components/Quote/QuoteTrigger.tsx +5 -5
- package/src/components/QuoteSkeleton/QuoteSummarySkeleton.tsx +2 -2
- package/src/components/QuoteSkeleton/StepSkeleton.tsx +2 -2
- package/src/components/SearchInput/SearchInput.tsx +13 -10
- package/src/components/SearchInput/SearchInput.types.ts +4 -2
- package/src/components/SwapDetailsModal/SwapDetailsModal.types.ts +2 -2
- package/src/components/TokenList/TokenList.styles.ts +40 -0
- package/src/components/TokenList/TokenList.tsx +134 -22
- package/src/components/TokenList/TokenList.types.ts +5 -1
- package/src/constants/customTokens.ts +3 -0
- package/src/containers/Inputs/Inputs.tsx +6 -4
- package/src/hooks/useFetchCustomToken.ts +39 -16
- package/src/hooks/useSearchCustomTokens/index.ts +1 -0
- package/src/hooks/useSearchCustomTokens/types.ts +9 -0
- package/src/hooks/useSearchCustomTokens/useSearchCustomTokens.constants.ts +1 -0
- package/src/hooks/useSearchCustomTokens/useSearchCustomTokens.ts +81 -0
- package/src/hooks/useSyncStoresWithConfig.ts +2 -3
- package/src/hooks/useSyncUrlAndStore/useSyncUrlAndStore.ts +1 -3
- package/src/pages/AddCustomTokenPage.tsx +24 -94
- package/src/pages/CustomTokensPage.tsx +1 -0
- package/src/pages/SelectSwapItemPage/SelectSwapItemPage.constants.ts +2 -0
- package/src/pages/SelectSwapItemPage/SelectSwapItemPage.helpers.ts +51 -0
- package/src/pages/{SelectSwapItemsPage.tsx → SelectSwapItemPage/SelectSwapItemsPage.tsx} +54 -13
- package/src/pages/SelectSwapItemPage/index.ts +1 -0
- package/src/store/app.ts +12 -1
- package/src/store/quote.ts +6 -3
- package/src/store/slices/settings.ts +11 -4
- package/src/store/slices/wallets.ts +105 -2
- package/src/store/utils/wallets.ts +31 -2
- package/src/utils/quote.ts +1 -1
- package/dist/pages/SelectSwapItemsPage.d.ts.map +0 -1
- /package/dist/pages/{SelectSwapItemsPage.d.ts → SelectSwapItemPage/SelectSwapItemsPage.d.ts} +0 -0
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-magic-numbers */
|
|
2
|
-
import type { PropTypes, RenderDescProps } from './TokenList.types';
|
|
3
|
-
import type { BlockchainMeta, Token } from 'rango-sdk';
|
|
2
|
+
import type { PropTypes, RenderDescProps, TokenData } from './TokenList.types';
|
|
4
3
|
|
|
5
4
|
import { i18n } from '@lingui/core';
|
|
6
5
|
import {
|
|
6
|
+
Button,
|
|
7
|
+
CustomTokenWarning,
|
|
7
8
|
darkTheme,
|
|
8
9
|
Divider,
|
|
9
10
|
ExternalLinkIcon,
|
|
10
11
|
Image,
|
|
11
|
-
|
|
12
|
+
ListItem,
|
|
12
13
|
NotFound,
|
|
13
14
|
PinIcon,
|
|
14
15
|
Skeleton,
|
|
15
16
|
Typography,
|
|
16
17
|
VirtualizedList,
|
|
17
18
|
} from '@rango-dev/ui';
|
|
18
|
-
import React from 'react';
|
|
19
|
+
import React, { useState } from 'react';
|
|
20
|
+
import { useTranslation } from 'react-i18next';
|
|
19
21
|
|
|
22
|
+
import { DEFAULT_TOKEN_IMAGE_SRC } from '../../constants/customTokens';
|
|
23
|
+
import { useNavigateBack } from '../../hooks/useNavigateBack';
|
|
20
24
|
import { useAppStore } from '../../store/AppStore';
|
|
25
|
+
import { useQuoteStore } from '../../store/quote';
|
|
21
26
|
import { createTintsAndShades } from '../../utils/colors';
|
|
27
|
+
import { getContainer } from '../../utils/common';
|
|
28
|
+
import { findBlockchain } from '../../utils/meta';
|
|
22
29
|
import { formatBalance } from '../../utils/wallets';
|
|
30
|
+
import { ImportCustomToken } from '../ImportCustomToken';
|
|
23
31
|
|
|
24
32
|
import { LoadingTokenList } from './LoadingTokenList';
|
|
25
33
|
import {
|
|
@@ -29,8 +37,10 @@ import {
|
|
|
29
37
|
End,
|
|
30
38
|
ImageSection,
|
|
31
39
|
List,
|
|
40
|
+
ListItemContainer,
|
|
32
41
|
Pin,
|
|
33
42
|
StyledLink,
|
|
43
|
+
StyledListItemButton,
|
|
34
44
|
Tag,
|
|
35
45
|
TagTitle,
|
|
36
46
|
Title,
|
|
@@ -97,14 +107,57 @@ export function TokenList(props: PropTypes) {
|
|
|
97
107
|
selectedBlockchain,
|
|
98
108
|
showTitle = true,
|
|
99
109
|
action,
|
|
110
|
+
type,
|
|
111
|
+
showWarning = true,
|
|
100
112
|
} = props;
|
|
101
113
|
|
|
102
114
|
const fetchStatus = useAppStore().fetchStatus;
|
|
103
115
|
const blockchains = useAppStore().blockchains();
|
|
104
116
|
const { getBalanceFor, fetchingWallets: loadingWallet } = useAppStore();
|
|
105
117
|
const { isTokenPinned } = useAppStore();
|
|
118
|
+
const { setFromToken, setToToken } = useQuoteStore();
|
|
119
|
+
const { t } = useTranslation();
|
|
120
|
+
const navigateBack = useNavigateBack();
|
|
106
121
|
|
|
107
|
-
const
|
|
122
|
+
const [customToken, setCustomToken] = useState<TokenData | null>(null);
|
|
123
|
+
|
|
124
|
+
const updateToken = () => {
|
|
125
|
+
if (type === 'source') {
|
|
126
|
+
setFromToken({ token: customToken, meta: { blockchains } });
|
|
127
|
+
} else {
|
|
128
|
+
setToToken({ token: customToken, meta: { blockchains } });
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const handleImportToken = () => {
|
|
133
|
+
updateToken();
|
|
134
|
+
navigateBack();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const endRenderer = (token: TokenData) => {
|
|
138
|
+
if (token.customToken) {
|
|
139
|
+
const { customToken, ...otherProps } = token;
|
|
140
|
+
const handleClick: React.MouseEventHandler<HTMLButtonElement> = (
|
|
141
|
+
event
|
|
142
|
+
) => {
|
|
143
|
+
event.stopPropagation();
|
|
144
|
+
setCustomToken({ ...otherProps, warning: true });
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
// eslint-disable-next-line jsx-id-attribute-enforcement/missing-ids
|
|
149
|
+
<Button
|
|
150
|
+
variant="contained"
|
|
151
|
+
type="primary"
|
|
152
|
+
size="small"
|
|
153
|
+
className="widget-token-list-item-import-btn"
|
|
154
|
+
onClick={handleClick}>
|
|
155
|
+
<Typography variant="body" size="xsmall" color="background">
|
|
156
|
+
{t('import')}
|
|
157
|
+
</Typography>
|
|
158
|
+
</Button>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
108
161
|
const tokenBalance = formatBalance(getBalanceFor(token));
|
|
109
162
|
|
|
110
163
|
if (action) {
|
|
@@ -144,10 +197,42 @@ export function TokenList(props: PropTypes) {
|
|
|
144
197
|
<VirtualizedList
|
|
145
198
|
itemContent={(index) => {
|
|
146
199
|
const token = tokens[index];
|
|
200
|
+
if (token === 'skeleton') {
|
|
201
|
+
return (
|
|
202
|
+
<ListItem
|
|
203
|
+
hasDivider
|
|
204
|
+
start={<Skeleton variant="circular" width={35} height={35} />}
|
|
205
|
+
end={
|
|
206
|
+
<End>
|
|
207
|
+
<Skeleton variant="text" size="large" width={70} />
|
|
208
|
+
<Divider size={4} />
|
|
209
|
+
<Skeleton variant="text" size="medium" width={50} />
|
|
210
|
+
</End>
|
|
211
|
+
}
|
|
212
|
+
title={
|
|
213
|
+
<div>
|
|
214
|
+
<Skeleton variant="text" size="large" width={90} />
|
|
215
|
+
<Divider size={4} />
|
|
216
|
+
<Skeleton variant="text" size="medium" width={90} />
|
|
217
|
+
</div>
|
|
218
|
+
}
|
|
219
|
+
/>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
147
222
|
const address = token.address || '';
|
|
148
223
|
const blockchain = blockchains.find(
|
|
149
224
|
(blockchain) => blockchain.name === token.blockchain
|
|
150
|
-
)
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* This block is added to satisfy TypeScript without using assertions and to prevent any errors that could break the app.
|
|
229
|
+
* Be cautious, as Virtuoso warns us if we return empty elements.
|
|
230
|
+
* If you need to exclude any items, do so before passing them to the virtual list.
|
|
231
|
+
*/
|
|
232
|
+
if (!blockchain) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
151
236
|
const colors = createTintsAndShades(blockchain.color, 'main');
|
|
152
237
|
const customCssForTag = {
|
|
153
238
|
$$color: colors.main150,
|
|
@@ -165,26 +250,32 @@ export function TokenList(props: PropTypes) {
|
|
|
165
250
|
color: '$$color',
|
|
166
251
|
};
|
|
167
252
|
|
|
253
|
+
const handleClick = () => {
|
|
254
|
+
if (typeof token !== 'string' && !token.customToken) {
|
|
255
|
+
onChange?.(token);
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
168
259
|
return (
|
|
169
|
-
<
|
|
170
|
-
|
|
171
|
-
paddingRight: 5,
|
|
172
|
-
}}>
|
|
173
|
-
<ListItemButton
|
|
174
|
-
style={{
|
|
175
|
-
width: '100%',
|
|
176
|
-
overflow: 'hidden',
|
|
177
|
-
height: '60px',
|
|
178
|
-
}}
|
|
260
|
+
<ListItemContainer>
|
|
261
|
+
<StyledListItemButton
|
|
179
262
|
tab-index={index}
|
|
180
263
|
key={`${token.symbol}${token.address}`}
|
|
181
264
|
id={`${token.symbol}${token.address}`}
|
|
182
|
-
className=
|
|
265
|
+
className="widget-token-list-item-btn"
|
|
183
266
|
hasDivider
|
|
184
|
-
|
|
267
|
+
customToken={token.customToken}
|
|
268
|
+
onClick={handleClick}
|
|
185
269
|
start={
|
|
186
270
|
<ImageSection>
|
|
187
|
-
<Image
|
|
271
|
+
<Image
|
|
272
|
+
src={
|
|
273
|
+
token.image === ''
|
|
274
|
+
? DEFAULT_TOKEN_IMAGE_SRC
|
|
275
|
+
: token.image
|
|
276
|
+
}
|
|
277
|
+
size={30}
|
|
278
|
+
/>
|
|
188
279
|
{props.type !== 'custom-token' &&
|
|
189
280
|
isTokenPinned(token, props.type) && (
|
|
190
281
|
<Pin>
|
|
@@ -210,16 +301,23 @@ export function TokenList(props: PropTypes) {
|
|
|
210
301
|
{token.blockchain}
|
|
211
302
|
</TagTitle>
|
|
212
303
|
</Tag>
|
|
304
|
+
{showWarning && token.warning && (
|
|
305
|
+
<>
|
|
306
|
+
<Divider direction="horizontal" size={4} />
|
|
307
|
+
<CustomTokenWarning container={getContainer()} />
|
|
308
|
+
</>
|
|
309
|
+
)}
|
|
213
310
|
</Title>
|
|
214
311
|
) : undefined
|
|
215
312
|
}
|
|
216
313
|
description={
|
|
314
|
+
typeof token !== 'string' &&
|
|
217
315
|
!!blockchain?.info &&
|
|
218
316
|
!!address &&
|
|
219
317
|
blockchain.type !== 'COSMOS'
|
|
220
318
|
? renderDesc({
|
|
221
319
|
address,
|
|
222
|
-
token
|
|
320
|
+
token,
|
|
223
321
|
customCssForTag,
|
|
224
322
|
customCssForTagTitle,
|
|
225
323
|
name: token.name,
|
|
@@ -231,7 +329,7 @@ export function TokenList(props: PropTypes) {
|
|
|
231
329
|
}
|
|
232
330
|
end={endRenderer(token)}
|
|
233
331
|
/>
|
|
234
|
-
</
|
|
332
|
+
</ListItemContainer>
|
|
235
333
|
);
|
|
236
334
|
}}
|
|
237
335
|
totalCount={tokens.length}
|
|
@@ -240,6 +338,10 @@ export function TokenList(props: PropTypes) {
|
|
|
240
338
|
);
|
|
241
339
|
};
|
|
242
340
|
|
|
341
|
+
const customTokenBlockchain = customToken
|
|
342
|
+
? findBlockchain(customToken?.blockchain, blockchains)
|
|
343
|
+
: null;
|
|
344
|
+
|
|
243
345
|
return (
|
|
244
346
|
<>
|
|
245
347
|
{showTitle && (
|
|
@@ -256,7 +358,17 @@ export function TokenList(props: PropTypes) {
|
|
|
256
358
|
{fetchStatus === 'loading' && <LoadingTokenList size={PAGE_SIZE} />}
|
|
257
359
|
{fetchStatus === 'success' &&
|
|
258
360
|
(tokens.length ? (
|
|
259
|
-
|
|
361
|
+
<>
|
|
362
|
+
<ImportCustomToken
|
|
363
|
+
token={customToken}
|
|
364
|
+
address={customToken?.address ?? ''}
|
|
365
|
+
blockchain={customTokenBlockchain ?? undefined}
|
|
366
|
+
onImport={handleImportToken}
|
|
367
|
+
onExitErrorModal={() => setCustomToken(null)}
|
|
368
|
+
onExitImportModal={() => setCustomToken(null)}
|
|
369
|
+
/>
|
|
370
|
+
<List as="ul">{renderList()}</List>
|
|
371
|
+
</>
|
|
260
372
|
) : (
|
|
261
373
|
!!searchedFor && (
|
|
262
374
|
<NotFound
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import type { Token } from 'rango-sdk';
|
|
2
2
|
import type { ReactElement } from 'react';
|
|
3
3
|
|
|
4
|
+
export type TokenData = Token &
|
|
5
|
+
(Token & { customToken?: boolean; warning?: boolean });
|
|
6
|
+
|
|
4
7
|
export interface PropTypes {
|
|
5
|
-
list:
|
|
8
|
+
list: (TokenData | 'skeleton')[];
|
|
6
9
|
searchedFor?: string;
|
|
7
10
|
onChange?: (token: Token) => void;
|
|
8
11
|
selectedBlockchain?: string;
|
|
9
12
|
type: 'source' | 'destination' | 'custom-token';
|
|
10
13
|
action?: (token: Token) => ReactElement;
|
|
11
14
|
showTitle?: boolean;
|
|
15
|
+
showWarning?: boolean;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
export interface LoadingTokenListProps {
|
|
@@ -82,11 +82,12 @@ export function Inputs(props: PropTypes) {
|
|
|
82
82
|
balance={fromTokenFormattedBalance}
|
|
83
83
|
chain={{
|
|
84
84
|
displayName: fromBlockchain?.displayName || '',
|
|
85
|
-
image: fromBlockchain?.logo
|
|
85
|
+
image: fromBlockchain?.logo,
|
|
86
86
|
}}
|
|
87
87
|
token={{
|
|
88
88
|
displayName: fromToken?.symbol || '',
|
|
89
|
-
image: fromToken?.image
|
|
89
|
+
image: fromToken?.image,
|
|
90
|
+
securityWarning: !!fromToken?.warning,
|
|
90
91
|
}}
|
|
91
92
|
onClickToken={() => onClickToken('from')}
|
|
92
93
|
price={{
|
|
@@ -133,11 +134,12 @@ export function Inputs(props: PropTypes) {
|
|
|
133
134
|
fetchingQuote={fetchingQuote}
|
|
134
135
|
chain={{
|
|
135
136
|
displayName: toBlockchain?.displayName || '',
|
|
136
|
-
image: toBlockchain?.logo
|
|
137
|
+
image: toBlockchain?.logo,
|
|
137
138
|
}}
|
|
138
139
|
token={{
|
|
139
140
|
displayName: toToken?.symbol || '',
|
|
140
|
-
image: toToken?.image
|
|
141
|
+
image: toToken?.image,
|
|
142
|
+
securityWarning: !!toToken?.warning,
|
|
141
143
|
}}
|
|
142
144
|
percentageChange={numberToString(
|
|
143
145
|
getPriceImpact(inputUsdValue, outputUsdValue),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TokenData } from '../components/TokenList/TokenList.types';
|
|
1
2
|
import type { Token } from 'rango-sdk';
|
|
2
3
|
|
|
3
4
|
import { i18n } from '@lingui/core';
|
|
@@ -6,36 +7,49 @@ import { useState } from 'react';
|
|
|
6
7
|
import { httpService } from '../services/httpService';
|
|
7
8
|
import { useAppStore } from '../store/AppStore';
|
|
8
9
|
|
|
9
|
-
type ErrorType =
|
|
10
|
+
type ErrorType = 'duplicated' | 'not-found' | 'token-exist' | 'network-error';
|
|
11
|
+
|
|
12
|
+
export type CustomTokenError = {
|
|
13
|
+
type: ErrorType;
|
|
10
14
|
title: string;
|
|
11
15
|
message: string;
|
|
12
16
|
};
|
|
13
17
|
|
|
14
|
-
interface UseFetchCustomToken {
|
|
18
|
+
export interface UseFetchCustomToken {
|
|
15
19
|
fetchCustomToken: ({
|
|
16
20
|
blockchain,
|
|
17
21
|
tokenAddress,
|
|
18
22
|
}: {
|
|
19
23
|
blockchain: string;
|
|
20
24
|
tokenAddress: string;
|
|
21
|
-
}) => Promise<
|
|
25
|
+
}) => Promise<void>;
|
|
26
|
+
token: Token | null;
|
|
22
27
|
loading: boolean;
|
|
23
|
-
error
|
|
28
|
+
error: CustomTokenError | null;
|
|
29
|
+
resetState: () => void;
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
export function useFetchCustomToken(): UseFetchCustomToken {
|
|
27
33
|
const [loading, setLoading] = useState(false);
|
|
28
|
-
const [error, setError] = useState<
|
|
34
|
+
const [error, setError] = useState<CustomTokenError | null>(null);
|
|
35
|
+
const [token, setToken] = useState<TokenData | null>(null);
|
|
29
36
|
const { findToken } = useAppStore();
|
|
30
37
|
const customTokens = useAppStore().customTokens();
|
|
31
38
|
|
|
39
|
+
const resetState = () => {
|
|
40
|
+
setToken(null);
|
|
41
|
+
setLoading(false);
|
|
42
|
+
setError(null);
|
|
43
|
+
};
|
|
44
|
+
|
|
32
45
|
function produceErrorMessage(
|
|
33
|
-
type:
|
|
46
|
+
type: ErrorType,
|
|
34
47
|
blockchain?: string
|
|
35
|
-
) {
|
|
48
|
+
): CustomTokenError {
|
|
36
49
|
switch (type) {
|
|
37
50
|
case 'duplicated':
|
|
38
51
|
return {
|
|
52
|
+
type,
|
|
39
53
|
title: i18n.t('Duplicate Token'),
|
|
40
54
|
message: i18n.t(
|
|
41
55
|
'The address you entered is duplicate, please enter a new address.'
|
|
@@ -43,6 +57,7 @@ export function useFetchCustomToken(): UseFetchCustomToken {
|
|
|
43
57
|
};
|
|
44
58
|
case 'token-exist':
|
|
45
59
|
return {
|
|
60
|
+
type,
|
|
46
61
|
title: i18n.t('Token Already Exists'),
|
|
47
62
|
message: i18n.t(
|
|
48
63
|
`There's no need to add this token again because it already exists and is supported by us.`
|
|
@@ -50,6 +65,7 @@ export function useFetchCustomToken(): UseFetchCustomToken {
|
|
|
50
65
|
};
|
|
51
66
|
case 'not-found':
|
|
52
67
|
return {
|
|
68
|
+
type,
|
|
53
69
|
title: i18n.t('Token Not Found'),
|
|
54
70
|
message: i18n.t({
|
|
55
71
|
id: 'Sorry, no token was found on {blockchain} chain with the provided address. please make sure you have entered the right token address.',
|
|
@@ -58,6 +74,12 @@ export function useFetchCustomToken(): UseFetchCustomToken {
|
|
|
58
74
|
},
|
|
59
75
|
}),
|
|
60
76
|
};
|
|
77
|
+
case 'network-error':
|
|
78
|
+
return {
|
|
79
|
+
type,
|
|
80
|
+
title: i18n.t('Network error'),
|
|
81
|
+
message: i18n.t('An error occurred while retrieving token data.'),
|
|
82
|
+
};
|
|
61
83
|
}
|
|
62
84
|
}
|
|
63
85
|
|
|
@@ -74,7 +96,7 @@ export function useFetchCustomToken(): UseFetchCustomToken {
|
|
|
74
96
|
if (isDuplicate) {
|
|
75
97
|
const errorMessage = produceErrorMessage('duplicated');
|
|
76
98
|
setError(errorMessage);
|
|
77
|
-
return
|
|
99
|
+
return;
|
|
78
100
|
}
|
|
79
101
|
|
|
80
102
|
const response = await httpService().getCustomToken({
|
|
@@ -85,7 +107,7 @@ export function useFetchCustomToken(): UseFetchCustomToken {
|
|
|
85
107
|
if (!response || !response.token || response.error) {
|
|
86
108
|
const errorMessage = produceErrorMessage('not-found', blockchain);
|
|
87
109
|
setError(errorMessage);
|
|
88
|
-
return
|
|
110
|
+
return;
|
|
89
111
|
}
|
|
90
112
|
|
|
91
113
|
// Check if token is already in the system
|
|
@@ -98,21 +120,22 @@ export function useFetchCustomToken(): UseFetchCustomToken {
|
|
|
98
120
|
if (isTokenFound) {
|
|
99
121
|
const errorMessage = produceErrorMessage('token-exist');
|
|
100
122
|
setError(errorMessage);
|
|
101
|
-
return
|
|
123
|
+
return;
|
|
102
124
|
}
|
|
103
125
|
|
|
104
|
-
return token;
|
|
126
|
+
return setToken({ ...token, warning: true });
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
128
|
} catch (error: any) {
|
|
106
|
-
if (error
|
|
129
|
+
if (error.code === 'ERR_BAD_REQUEST') {
|
|
107
130
|
const errorMessage = produceErrorMessage('not-found', blockchain);
|
|
108
131
|
setError(errorMessage);
|
|
109
|
-
return
|
|
132
|
+
return;
|
|
110
133
|
}
|
|
111
|
-
setError(
|
|
112
|
-
|
|
134
|
+
setError(produceErrorMessage('network-error'));
|
|
135
|
+
return;
|
|
113
136
|
} finally {
|
|
114
137
|
setLoading(false);
|
|
115
138
|
}
|
|
116
139
|
};
|
|
117
|
-
return { fetchCustomToken, loading, error };
|
|
140
|
+
return { fetchCustomToken, token, loading, error, resetState };
|
|
118
141
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useSearchCustomTokens } from './useSearchCustomTokens';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEBOUNCE_DELAY = 600;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { UseSearchCustomTokens } from './types';
|
|
2
|
+
import type { Token } from 'rango-sdk';
|
|
3
|
+
|
|
4
|
+
import { useCallback, useRef, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
import { httpService } from '../../services/httpService';
|
|
7
|
+
import { useAppStore } from '../../store/AppStore';
|
|
8
|
+
import { createAssetKey } from '../../store/utils/wallets';
|
|
9
|
+
import { debounce } from '../../utils/common';
|
|
10
|
+
|
|
11
|
+
import { DEBOUNCE_DELAY } from './useSearchCustomTokens.constants';
|
|
12
|
+
|
|
13
|
+
export function useSearchCustomTokens(): UseSearchCustomTokens {
|
|
14
|
+
const blockchains = useAppStore().blockchains();
|
|
15
|
+
const abortControllerRef = useRef<AbortController | null>(null);
|
|
16
|
+
const { customTokens } = useAppStore();
|
|
17
|
+
const [loading, setLoading] = useState(false);
|
|
18
|
+
const [tokens, setTokens] = useState<Token[]>([]);
|
|
19
|
+
const [error, setError] = useState<string | null>(null);
|
|
20
|
+
|
|
21
|
+
const fetch = async (query: string, blockchain?: string) => {
|
|
22
|
+
setError(null);
|
|
23
|
+
setLoading(true);
|
|
24
|
+
setTokens([]);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const response = await httpService().searchCustomTokens(
|
|
28
|
+
{ query, blockchain },
|
|
29
|
+
{ signal: abortControllerRef.current?.signal }
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const customTokensSet = new Set(
|
|
33
|
+
customTokens().map((token) => createAssetKey(token))
|
|
34
|
+
);
|
|
35
|
+
const blockchainsSet = new Set(
|
|
36
|
+
blockchains.map((blockchain) => blockchain.name)
|
|
37
|
+
);
|
|
38
|
+
const filteredTokens = response.tokens.filter(
|
|
39
|
+
(token) =>
|
|
40
|
+
blockchainsSet.has(token.blockchain) &&
|
|
41
|
+
!customTokensSet.has(createAssetKey(token))
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
setTokens(filteredTokens);
|
|
45
|
+
setLoading(false);
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
} catch (error: any) {
|
|
48
|
+
setError(error instanceof Error ? error.message : 'something went wrong');
|
|
49
|
+
setTokens([]);
|
|
50
|
+
if (error?.name !== 'CanceledError') {
|
|
51
|
+
setLoading(false);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const debouncedFetch = useCallback(
|
|
57
|
+
debounce((query: string, blockchain?: string) => {
|
|
58
|
+
if (abortControllerRef.current?.signal.aborted) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
void fetch(query, blockchain);
|
|
62
|
+
}, DEBOUNCE_DELAY),
|
|
63
|
+
[blockchains.length]
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const cancel = () => {
|
|
67
|
+
abortControllerRef.current?.abort();
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
fetch: (query: string, blockchain?: string) => {
|
|
71
|
+
abortControllerRef.current = new AbortController();
|
|
72
|
+
setTokens([]);
|
|
73
|
+
setLoading(true);
|
|
74
|
+
debouncedFetch(query, blockchain);
|
|
75
|
+
},
|
|
76
|
+
cancel,
|
|
77
|
+
loading,
|
|
78
|
+
tokens,
|
|
79
|
+
error,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -26,7 +26,6 @@ export function useSyncStoresWithConfig() {
|
|
|
26
26
|
const config = useAppStore().config;
|
|
27
27
|
const fetchMetaStatus = useAppStore().fetchStatus;
|
|
28
28
|
const blockchains = useAppStore().blockchains();
|
|
29
|
-
const tokens = useAppStore().tokens();
|
|
30
29
|
const { findToken } = useAppStore();
|
|
31
30
|
|
|
32
31
|
const { setAffiliateRef, setAffiliatePercent, setAffiliateWallets } =
|
|
@@ -61,7 +60,7 @@ export function useSyncStoresWithConfig() {
|
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
if (token) {
|
|
64
|
-
setFromToken({ token, meta: { blockchains
|
|
63
|
+
setFromToken({ token, meta: { blockchains } });
|
|
65
64
|
} else if (!token && prevConfigFromToken.current) {
|
|
66
65
|
setFromToken({ token: null });
|
|
67
66
|
}
|
|
@@ -111,7 +110,7 @@ export function useSyncStoresWithConfig() {
|
|
|
111
110
|
}
|
|
112
111
|
|
|
113
112
|
if (token) {
|
|
114
|
-
setToToken({ token, meta: { blockchains
|
|
113
|
+
setToToken({ token, meta: { blockchains } });
|
|
115
114
|
} else if (!token && prevConfigToToken.current) {
|
|
116
115
|
setToToken({ token: null });
|
|
117
116
|
}
|
|
@@ -31,7 +31,6 @@ export function useSyncUrlAndStore() {
|
|
|
31
31
|
} = useQuoteStore();
|
|
32
32
|
const fetchMetaStatus = useAppStore().fetchStatus;
|
|
33
33
|
const blockchains = useAppStore().blockchains();
|
|
34
|
-
const tokens = useAppStore().tokens();
|
|
35
34
|
const isInRouterContext = useInRouterContext();
|
|
36
35
|
const { updateIframe, updateCampaignMode } = useAppStore();
|
|
37
36
|
const campaignMode = useAppStore().isInCampaignMode();
|
|
@@ -157,7 +156,6 @@ export function useSyncUrlAndStore() {
|
|
|
157
156
|
token: fromToken,
|
|
158
157
|
meta: {
|
|
159
158
|
blockchains: blockchains,
|
|
160
|
-
tokens: tokens,
|
|
161
159
|
},
|
|
162
160
|
});
|
|
163
161
|
}
|
|
@@ -168,7 +166,7 @@ export function useSyncUrlAndStore() {
|
|
|
168
166
|
if (!!toToken) {
|
|
169
167
|
setToToken({
|
|
170
168
|
token: toToken,
|
|
171
|
-
meta: { blockchains: blockchains
|
|
169
|
+
meta: { blockchains: blockchains },
|
|
172
170
|
});
|
|
173
171
|
}
|
|
174
172
|
}
|