@dodoex/wallet-web3-react 0.2.0 → 0.3.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.
Files changed (40) hide show
  1. package/babel.config.js +9 -9
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.js +1 -1
  4. package/lingui.config.ts +13 -13
  5. package/package.json +91 -76
  6. package/rollup.config.mjs +100 -100
  7. package/src/ClientProvider.tsx +17 -15
  8. package/src/LangProvider.tsx +36 -34
  9. package/src/WalletConnect/AccountPage.tsx +496 -494
  10. package/src/WalletConnect/ActivityList.tsx +606 -604
  11. package/src/WalletConnect/ConnectAlchemy/index.tsx +248 -246
  12. package/src/WalletConnect/ConnectAlchemy/useConnectAlchemy.ts +105 -105
  13. package/src/WalletConnect/ConnectDialog.tsx +35 -33
  14. package/src/WalletConnect/ConnectLedger/ErrorDialog.tsx +61 -61
  15. package/src/WalletConnect/ConnectLedger/LockedDialog.tsx +54 -54
  16. package/src/WalletConnect/ConnectLedger/helper.ts +14 -14
  17. package/src/WalletConnect/ConnectLedger/index.tsx +2 -0
  18. package/src/WalletConnect/ConnectPage.tsx +508 -506
  19. package/src/WalletConnect/HasBalanceTokenList.tsx +202 -200
  20. package/src/WalletConnect/ReceiveTokenPage.tsx +145 -143
  21. package/src/WalletConnect/SendTokenPage.tsx +251 -249
  22. package/src/WalletConnect/WalletDialog.tsx +80 -78
  23. package/src/WalletConnectProvider.tsx +57 -55
  24. package/src/components/AddressWithLinkAndCopy.tsx +202 -200
  25. package/src/components/Dialog.tsx +158 -156
  26. package/src/components/TokenLogo.tsx +167 -165
  27. package/src/components/WalletTag.tsx +117 -115
  28. package/src/constants/localstorage.ts +24 -22
  29. package/src/hooks/useConnectWallet.ts +150 -146
  30. package/src/hooks/useFetchFiatPrice.ts +53 -51
  31. package/src/hooks/useFetchTokensBalance.ts +53 -51
  32. package/src/hooks/useHasBalanceTokenList.ts +95 -93
  33. package/src/hooks/useTransactionList.ts +89 -87
  34. package/src/index.tsx +7 -7
  35. package/src/locales/en.po +51 -51
  36. package/src/locales/zh.po +51 -51
  37. package/src/utils/formatter.ts +102 -102
  38. package/src/utils/time.ts +21 -21
  39. package/src/utils/utils.ts +8 -8
  40. package/tsconfig.json +23 -23
@@ -1,604 +1,606 @@
1
- import { Box, EmptyDataIcon, RotatingIcon, Skeleton } from '@dodoex/components';
2
- import {
3
- ArrowRight,
4
- DoneFilled as PassedIcon,
5
- ErrorFilled as RejectedIcon,
6
- InvalidFilled as InvalidIcon,
7
- ArrowBack,
8
- } from '@dodoex/icons';
9
- import { useWalletStore } from '@dodoex/wallet-web3';
10
- import { t, Trans } from '@lingui/macro';
11
- import { debounce } from 'lodash';
12
- import React from 'react';
13
- import InfiniteScroll from 'react-infinite-scroller';
14
- import {
15
- NoticeTransactionList,
16
- StateText,
17
- useTransactionList,
18
- } from '../hooks/useTransactionList';
19
- import { formatReadableTimeAgo } from '../utils/time';
20
- import { increaseArray } from '../utils/utils';
21
- import { useWalletConnectContext } from '../WalletConnectProvider';
22
-
23
- export default function ActivityList({
24
- getScrollParent,
25
- fetchTransactionQuery,
26
- }: {
27
- getScrollParent: () => HTMLDivElement | null;
28
- fetchTransactionQuery: ReturnType<typeof useTransactionList>;
29
- }) {
30
- const { account } = useWalletStore();
31
-
32
- return (
33
- <InfiniteScroll
34
- useWindow={false}
35
- hasMore={fetchTransactionQuery.hasNextPage}
36
- threshold={300}
37
- getScrollParent={getScrollParent}
38
- loadMore={debounce(() => {
39
- if (
40
- fetchTransactionQuery.hasNextPage &&
41
- !fetchTransactionQuery.isFetchingNextPage
42
- ) {
43
- fetchTransactionQuery.fetchNextPage();
44
- }
45
- }, 500)}
46
- >
47
- <TransactionList
48
- account={account}
49
- isLoading={fetchTransactionQuery.isLoading}
50
- isFetchingNextPage={fetchTransactionQuery.isFetchingNextPage}
51
- list={fetchTransactionQuery.list}
52
- />
53
- </InfiniteScroll>
54
- );
55
- }
56
-
57
- export function TransactionList({
58
- account,
59
- isLoading,
60
- isFetchingNextPage,
61
- list,
62
- }: {
63
- account: string | undefined;
64
- isLoading?: boolean;
65
- isFetchingNextPage?: boolean;
66
- list: NoticeTransactionList;
67
- }) {
68
- const { getChain } = useWalletConnectContext();
69
- return (
70
- <>
71
- {isLoading && (
72
- <>
73
- {increaseArray(3).map((i) => (
74
- <Box
75
- key={i}
76
- sx={{
77
- position: 'relative',
78
- display: 'flex',
79
- width: '100%',
80
- pl: 24,
81
- pr: 48,
82
- py: 20,
83
- }}
84
- >
85
- <Box
86
- sx={{
87
- position: 'absolute',
88
- bottom: 0,
89
- right: 24,
90
- left: 80,
91
- height: '1px',
92
- backgroundColor: 'border.default',
93
- }}
94
- />
95
- <Skeleton width={40} height={40} />
96
- <Box
97
- sx={{
98
- ml: 8,
99
- flex: 1,
100
- }}
101
- >
102
- <Skeleton width="100%" height={22} />
103
- <Skeleton
104
- width={128}
105
- height={16}
106
- sx={{
107
- mt: 4,
108
- }}
109
- />
110
- </Box>
111
- </Box>
112
- ))}
113
- </>
114
- )}
115
- {!isLoading && !list.length && (
116
- <Box
117
- sx={{
118
- display: 'flex',
119
- alignItems: 'center',
120
- justifyContent: 'space-between',
121
- flexDirection: 'column',
122
- mt: 56,
123
- gap: 12,
124
- typography: 'body2',
125
- color: 'text.secondary',
126
- }}
127
- >
128
- <EmptyDataIcon
129
- sx={{
130
- width: 60,
131
- height: 60,
132
- borderRadius: 12,
133
- }}
134
- />
135
- <Trans>This is empty</Trans>
136
- </Box>
137
- )}
138
- {!isLoading &&
139
- list?.map((item) => {
140
- if (!item?.key || !item.chainId) return null;
141
- const tx = item.extend?.tx;
142
- const status = item.extend?.status;
143
- let url = '';
144
- if (tx) {
145
- const scanUrl = getChain(item.chainId)?.scanUrl ?? '';
146
- if (status === StateText.Warning && account) {
147
- url = scanUrl + `/address/${account}`;
148
- } else {
149
- url = scanUrl + `/tx/${tx}`;
150
- }
151
- }
152
- return (
153
- <Box
154
- key={item.id}
155
- onClick={() => {
156
- if (!url) return;
157
- window.open(url);
158
- }}
159
- sx={{
160
- position: 'relative',
161
- display: 'flex',
162
- pl: 20,
163
- py: 16,
164
- borderRadius: 8,
165
- ...(url
166
- ? {
167
- cursor: 'pointer',
168
- '&:hover': {
169
- backgroundColor: 'hover.default',
170
- },
171
- '&:not(:hover) > svg:last-child': {
172
- visibility: 'hidden',
173
- },
174
- }
175
- : {}),
176
- }}
177
- >
178
- <Box
179
- sx={{
180
- position: 'relative',
181
- display: 'flex',
182
- alignItems: 'center',
183
- justifyContent: 'center',
184
- width: 40,
185
- height: 40,
186
- borderRadius: '50%',
187
- backgroundColor: 'background.paperContrast',
188
- }}
189
- >
190
- <SubmissionTypeIcon brief={item.key} chainId={item.chainId} />
191
- <Box
192
- sx={{
193
- position: 'absolute',
194
- bottom: -1,
195
- right: -1,
196
- display: 'flex',
197
- width: 16,
198
- height: 16,
199
- color: 'primary.contrastText',
200
- backgroundColor: 'background.paperContrast',
201
- borderWidth: 1,
202
- borderStyle: 'solid',
203
- borderColor: 'border.default',
204
- borderRadius: '50%',
205
- }}
206
- >
207
- <SubmissionStatusIcon status={status} />
208
- </Box>
209
- </Box>
210
- <Box
211
- sx={{
212
- ml: 12,
213
- flex: 1,
214
- }}
215
- >
216
- <Box
217
- sx={{
218
- display: 'flex',
219
- alignItems: 'center',
220
- fontWeight: 600,
221
- }}
222
- >
223
- {getTitleText(item.key, status)}
224
- </Box>
225
- <Box
226
- sx={{
227
- mt: 12,
228
- typography: 'h6',
229
- color: 'text.secondary',
230
- }}
231
- >
232
- {formatReadableTimeAgo({
233
- time: Number(item.createTime),
234
- })}
235
- </Box>
236
- </Box>
237
- {url ? (
238
- <Box
239
- component={ArrowRight}
240
- sx={{
241
- mr: 24,
242
- width: 18,
243
- height: 18,
244
- alignSelf: 'center',
245
- }}
246
- />
247
- ) : (
248
- <Box />
249
- )}
250
- </Box>
251
- );
252
- })}
253
- {isFetchingNextPage ? (
254
- <Box
255
- sx={{
256
- display: 'flex',
257
- alignItems: 'center',
258
- justifyContent: 'center',
259
- gap: 4,
260
- mt: 16,
261
- color: 'text.secondary',
262
- typography: 'body2',
263
- }}
264
- >
265
- <RotatingIcon
266
- sx={{
267
- width: 20,
268
- color: 'primary.main',
269
- }}
270
- />
271
- <Trans>Loading more</Trans>
272
- </Box>
273
- ) : (
274
- ''
275
- )}
276
- </>
277
- );
278
- }
279
-
280
- export function SubmissionStatusIcon({ status }: { status: string }) {
281
- switch (status) {
282
- case StateText.Success:
283
- return (
284
- <Box
285
- component={PassedIcon}
286
- sx={{
287
- width: '100%',
288
- height: '100%',
289
- }}
290
- />
291
- );
292
- case StateText.Failed:
293
- return (
294
- <Box
295
- component={RejectedIcon}
296
- sx={{
297
- width: '100%',
298
- height: '100%',
299
- }}
300
- />
301
- );
302
- case StateText.Warning:
303
- return (
304
- <Box
305
- component={InvalidIcon}
306
- sx={{
307
- width: '100%',
308
- height: '100%',
309
- }}
310
- />
311
- );
312
- case StateText.Running:
313
- return (
314
- <Box
315
- sx={{
316
- display: 'flex',
317
- alignItems: 'center',
318
- justifyContent: 'center',
319
- backgroundColor: 'primary.main',
320
- borderRadius: '50%',
321
- width: '100%',
322
- height: '100%',
323
- }}
324
- >
325
- <Box
326
- component="svg"
327
- width={24}
328
- height={24}
329
- viewBox="0 0 24 24"
330
- fill="none"
331
- xmlns="http://www.w3.org/2000/svg"
332
- sx={{
333
- width: '92%',
334
- height: '92%',
335
- color: 'primary.contrastText',
336
- }}
337
- >
338
- <path
339
- d="M6 13C6 11.35 6.67 9.85 7.76 8.76L6.34 7.34C4.9 8.79 4 10.79 4 13C4 17.08 7.05 20.44 11 20.93V18.91C8.17 18.43 6 15.97 6 13ZM20 13C20 8.58 16.42 5 12 5C11.94 5 11.88 5.01 11.82 5.01L12.91 3.92L11.5 2.5L8 6L11.5 9.5L12.91 8.09L11.83 7.01C11.89 7.01 11.95 7 12 7C15.31 7 18 9.69 18 13C18 15.97 15.83 18.43 13 18.91V20.93C16.95 20.44 20 17.08 20 13Z"
340
- fill="currentColor"
341
- />
342
- </Box>
343
- </Box>
344
- );
345
-
346
- default:
347
- break;
348
- }
349
- return null;
350
- }
351
-
352
- export function getTitleText(brief: string, status: string) {
353
- const prefix = getSubmitTitleByBrief(brief);
354
- const statusLangMap: { [key: string]: string } = {
355
- [StateText.Success]: t`Succeeded`,
356
- [StateText.Failed]: t`Failed`,
357
- [StateText.Running]: t`Pending`,
358
- [StateText.Warning]: t`Reset`,
359
- };
360
- const suffix = statusLangMap[status];
361
- return suffix ? `${prefix} ${suffix}` : prefix;
362
- }
363
-
364
- export function getSubmitTitleByBrief(brief: string) {
365
- const prefixTypeTextMap: {
366
- [key: string]: string;
367
- } = {
368
- 'common.approve.resetBrief': t`Reset`,
369
- 'common.approve.brief': t`Approve`,
370
- };
371
- const matchPrefixKey = Object.keys(prefixTypeTextMap).find(
372
- (prefixKey) => brief.indexOf(prefixKey) === 0,
373
- );
374
- if (matchPrefixKey) {
375
- return prefixTypeTextMap[matchPrefixKey];
376
- }
377
- switch (brief) {
378
- case 'tradingCard.submissionBrief':
379
- case 'limit.main.title':
380
- case 'exchange.tabs.RFQ':
381
- case 'bridge.order.execute-bridge':
382
- return t`Swap`;
383
- case 'bridge.order.execute-bridge':
384
- return t`Transfer`;
385
- case 'liquidity.operate.title':
386
- case 'pool.amm-v2.add-liquidity.title':
387
- case 'pool.amm-v3.add-liquidity.title':
388
- return t`Add Liquidity`;
389
- case 'liquidity.operate.remove.title':
390
- case 'pool.amm-v2.remove-liquidity.title':
391
- case 'pool.amm-v3.remove-liquidity.title':
392
- return t`Remove Liquidity`;
393
- case 'pool.my-pools.create-a-pool':
394
- case 'pool.amm-v2.create.title':
395
- case 'pool.amm-v3.create.title':
396
- return t`Pool Creation`;
397
- case 'mining.stake':
398
- return t`Stake`;
399
- case 'mining.submit.remove-title':
400
- return t`End mining\n`;
401
- case 'mining.deposit.receive-reward':
402
- case 'pool.amm-v3.receive-reward.title':
403
- return t`Claim Rewards`;
404
- case 'nav.create-mining':
405
- return t`Create Liquidity Mining`;
406
-
407
- case 'wallet.account.card.operate.send':
408
- return t`Send`;
409
-
410
- default:
411
- return null;
412
- }
413
- }
414
-
415
- type IconMap = {
416
- [key: string]: React.ReactElement;
417
- };
418
- export function SubmissionTypeIcon({
419
- brief,
420
- chainId,
421
- }: {
422
- brief: string;
423
- chainId: number;
424
- }) {
425
- const { getChain } = useWalletConnectContext();
426
- const prefixTypeIconMap: IconMap = {
427
- 'common.approve.resetBrief': (
428
- <svg
429
- width="24"
430
- height="24"
431
- viewBox="0 0 24 24"
432
- fill="none"
433
- xmlns="http://www.w3.org/2000/svg"
434
- >
435
- <path
436
- d="M6 13C6 11.35 6.67 9.85 7.76 8.76L6.34 7.34C4.9 8.79 4 10.79 4 13C4 17.08 7.05 20.44 11 20.93V18.91C8.17 18.43 6 15.97 6 13ZM20 13C20 8.58 16.42 5 12 5C11.94 5 11.88 5.01 11.82 5.01L12.91 3.92L11.5 2.5L8 6L11.5 9.5L12.91 8.09L11.83 7.01C11.89 7.01 11.95 7 12 7C15.31 7 18 9.69 18 13C18 15.97 15.83 18.43 13 18.91V20.93C16.95 20.44 20 17.08 20 13Z"
437
- fill="currentColor"
438
- />
439
- </svg>
440
- ),
441
- 'common.approve.brief': (
442
- <svg
443
- width="24"
444
- height="24"
445
- viewBox="0 0 24 24"
446
- fill="none"
447
- xmlns="http://www.w3.org/2000/svg"
448
- >
449
- <path
450
- fillRule="evenodd"
451
- clipRule="evenodd"
452
- d="M18 8H17V6C17 3.24 14.76 1 12 1C9.24 1 7 3.24 7 6V8H6C4.9 8 4 8.9 4 10V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V10C20 8.9 19.1 8 18 8ZM9 6C9 4.34 10.34 3 12 3C13.66 3 15 4.34 15 6V8H9V6ZM14 14C14 14.7364 13.5967 15.3831 13 15.7303V18H11V15.7303C10.4033 15.3831 10 14.7364 10 14C10 12.9 10.9 12 12 12C13.1 12 14 12.9 14 14Z"
453
- fill="currentColor"
454
- />
455
- </svg>
456
- ),
457
- };
458
- const matchPrefixKey = Object.keys(prefixTypeIconMap).find(
459
- (prefixKey) => brief.indexOf(prefixKey) === 0,
460
- );
461
- if (matchPrefixKey) {
462
- return (
463
- <Box
464
- sx={{
465
- width: 24,
466
- height: 24,
467
- color: 'text.secondary',
468
- }}
469
- >
470
- {prefixTypeIconMap[matchPrefixKey]}
471
- </Box>
472
- );
473
- }
474
-
475
- if (brief === 'wallet.account.card.operate.send') {
476
- return <ArrowBack className="w-6 h-6 rotate-90" />;
477
- }
478
-
479
- const swapTypes = [
480
- 'tradingCard.submissionBrief',
481
- 'limit.main.title',
482
- 'exchange.tabs.RFQ',
483
- 'bridge.order.execute-bridge',
484
- ];
485
- if (swapTypes.includes(brief)) {
486
- const chainLogo = getChain(chainId)?.logo;
487
- if (!chainLogo) return null;
488
- return (
489
- <Box
490
- sx={{
491
- width: 24,
492
- height: 24,
493
- color: 'text.secondary',
494
- }}
495
- >
496
- {chainLogo}
497
- </Box>
498
- );
499
- }
500
-
501
- const PoolIcon = (
502
- <svg
503
- width="24"
504
- height="24"
505
- viewBox="0 0 24 24"
506
- fill="none"
507
- xmlns="http://www.w3.org/2000/svg"
508
- >
509
- <path
510
- className="active-color"
511
- fillRule="evenodd"
512
- clipRule="evenodd"
513
- d="M7 3.77979C7.47668 3.77979 7.8871 4.11624 7.98058 4.58367C8.22292 5.79538 8.95318 6.97446 9.92213 7.76492C11.2407 8.84061 12 10.2058 12 11.7298C12 14.4891 9.76515 16.7798 7 16.7798C4.23484 16.7798 2 14.4891 2 11.7298C2 10.2237 2.74214 8.83345 4.0853 7.75892C5.05247 6.98519 5.77579 5.80184 6.01942 4.58367C6.1129 4.11624 6.52332 3.77979 7 3.77979Z"
514
- fill="currentColor"
515
- />
516
- <path
517
- fillRule="evenodd"
518
- clipRule="evenodd"
519
- d="M14 1.5C14.4794 1.5 14.8913 1.84022 14.982 2.31097C15.4329 4.65329 16.7694 6.47608 18.6541 8.07351C20.8277 9.91584 22 12.1786 22 14.6364C22 19.2681 18.3582 22.5 14 22.5C11.1133 22.5 8.5408 21.082 7.13281 18.7785C10.9673 18.7065 14 15.5225 14 11.7298C14 9.47755 12.8621 7.58357 11.1898 6.21802C12.0968 5.07887 12.732 3.79698 13.018 2.31097C13.1086 1.84022 13.5206 1.5 14 1.5Z"
520
- fill="currentColor"
521
- />
522
- </svg>
523
- );
524
-
525
- const MiningIcon = (
526
- <svg
527
- width="24"
528
- height="24"
529
- viewBox="0 0 24 24"
530
- fill="none"
531
- xmlns="http://www.w3.org/2000/svg"
532
- >
533
- <g clipPath="url(#clip0_331_763)">
534
- <path
535
- d="M9.69961 7.1L9.39961 8H14.6996L13.0996 3.3L12.6996 2H11.3996L9.69961 7.1Z"
536
- fill="currentColor"
537
- />
538
- <path
539
- d="M9.39961 10L12.0996 18.2L14.6996 10H9.39961Z"
540
- fill="currentColor"
541
- />
542
- <path
543
- d="M16.7996 8H22.4996C22.4996 7.9 22.3996 7.9 22.3996 7.8L18.0996 2.6C17.9996 2.5 17.8996 2.3 17.6996 2.2C17.5996 2.1 17.3996 2 17.1996 2C16.9996 2 16.8996 2 16.6996 2H14.7996L16.7996 8Z"
544
- fill="currentColor"
545
- />
546
- <path
547
- d="M16.7996 10L13.1996 21.1C13.2996 21 13.3996 20.9 13.3996 20.9L22.3996 10.2C22.3996 10.1 22.4996 10.1 22.4996 10H16.7996Z"
548
- fill="currentColor"
549
- />
550
- <path
551
- className="active-color"
552
- d="M7.29961 8L9.09961 2.7L9.29961 2H7.39961C7.29961 2 7.09961 2 6.89961 2.1C6.69961 2.1 6.59961 2.2 6.39961 2.3C6.29961 2.4 6.19961 2.5 6.09961 2.6L1.69961 7.9L1.59961 8H7.29961Z"
553
- fill="currentColor"
554
- />
555
- <path
556
- className="active-color"
557
- d="M7.29961 10H1.59961C1.59961 10.1 1.69961 10.1 1.69961 10.2L10.6996 21C10.7996 21.1 10.7996 21.2 10.8996 21.3L7.29961 10Z"
558
- fill="currentColor"
559
- />
560
- </g>
561
- <defs>
562
- <clipPath id="clip0_331_763">
563
- <rect
564
- width="20.9"
565
- height="19.2"
566
- fill="white"
567
- transform="translate(1.59961 2)"
568
- />
569
- </clipPath>
570
- </defs>
571
- </svg>
572
- );
573
-
574
- const typeIconMap: IconMap = {
575
- 'pool.my-pools.create-a-pool': PoolIcon,
576
- 'pool.detail.modify-dpp-parameters': PoolIcon,
577
- 'liquidity.operate.remove.title': PoolIcon,
578
- 'liquidity.operate.title': PoolIcon,
579
- 'pool.amm-v2.create.title': PoolIcon,
580
- 'pool.amm-v2.remove-liquidity.title': PoolIcon,
581
- 'pool.amm-v2.add-liquidity.title': PoolIcon,
582
- 'pool.amm-v3.create.title': PoolIcon,
583
- 'pool.amm-v3.add-liquidity.title': PoolIcon,
584
- 'pool.amm-v3.remove-liquidity.title': PoolIcon,
585
- 'pool.amm-v3.receive-reward.title': PoolIcon,
586
- 'nav.create-mining': MiningIcon,
587
- 'mining.deposit.receive-reward': MiningIcon,
588
- 'mining.stake': MiningIcon,
589
- 'mining.submit.remove-title': MiningIcon,
590
- };
591
- const Icon = typeIconMap[brief];
592
- if (!Icon) return null;
593
- return (
594
- <Box
595
- sx={{
596
- width: 24,
597
- height: 24,
598
- color: 'text.secondary',
599
- }}
600
- >
601
- {Icon}
602
- </Box>
603
- );
604
- }
1
+ 'use client';
2
+
3
+ import { Box, EmptyDataIcon, RotatingIcon, Skeleton } from '@dodoex/components';
4
+ import {
5
+ ArrowRight,
6
+ DoneFilled as PassedIcon,
7
+ ErrorFilled as RejectedIcon,
8
+ InvalidFilled as InvalidIcon,
9
+ ArrowBack,
10
+ } from '@dodoex/icons';
11
+ import { useWalletStore } from '@dodoex/wallet-web3';
12
+ import { t, Trans } from '@lingui/macro';
13
+ import { debounce } from 'lodash';
14
+ import React from 'react';
15
+ import InfiniteScroll from 'react-infinite-scroller';
16
+ import {
17
+ NoticeTransactionList,
18
+ StateText,
19
+ useTransactionList,
20
+ } from '../hooks/useTransactionList';
21
+ import { formatReadableTimeAgo } from '../utils/time';
22
+ import { increaseArray } from '../utils/utils';
23
+ import { useWalletConnectContext } from '../WalletConnectProvider';
24
+
25
+ export default function ActivityList({
26
+ getScrollParent,
27
+ fetchTransactionQuery,
28
+ }: {
29
+ getScrollParent: () => HTMLDivElement | null;
30
+ fetchTransactionQuery: ReturnType<typeof useTransactionList>;
31
+ }) {
32
+ const { account } = useWalletStore();
33
+
34
+ return (
35
+ <InfiniteScroll
36
+ useWindow={false}
37
+ hasMore={fetchTransactionQuery.hasNextPage}
38
+ threshold={300}
39
+ getScrollParent={getScrollParent}
40
+ loadMore={debounce(() => {
41
+ if (
42
+ fetchTransactionQuery.hasNextPage &&
43
+ !fetchTransactionQuery.isFetchingNextPage
44
+ ) {
45
+ fetchTransactionQuery.fetchNextPage();
46
+ }
47
+ }, 500)}
48
+ >
49
+ <TransactionList
50
+ account={account}
51
+ isLoading={fetchTransactionQuery.isLoading}
52
+ isFetchingNextPage={fetchTransactionQuery.isFetchingNextPage}
53
+ list={fetchTransactionQuery.list}
54
+ />
55
+ </InfiniteScroll>
56
+ );
57
+ }
58
+
59
+ export function TransactionList({
60
+ account,
61
+ isLoading,
62
+ isFetchingNextPage,
63
+ list,
64
+ }: {
65
+ account: string | undefined;
66
+ isLoading?: boolean;
67
+ isFetchingNextPage?: boolean;
68
+ list: NoticeTransactionList;
69
+ }) {
70
+ const { getChain } = useWalletConnectContext();
71
+ return (
72
+ <>
73
+ {isLoading && (
74
+ <>
75
+ {increaseArray(3).map((i) => (
76
+ <Box
77
+ key={i}
78
+ sx={{
79
+ position: 'relative',
80
+ display: 'flex',
81
+ width: '100%',
82
+ pl: 24,
83
+ pr: 48,
84
+ py: 20,
85
+ }}
86
+ >
87
+ <Box
88
+ sx={{
89
+ position: 'absolute',
90
+ bottom: 0,
91
+ right: 24,
92
+ left: 80,
93
+ height: '1px',
94
+ backgroundColor: 'border.default',
95
+ }}
96
+ />
97
+ <Skeleton width={40} height={40} />
98
+ <Box
99
+ sx={{
100
+ ml: 8,
101
+ flex: 1,
102
+ }}
103
+ >
104
+ <Skeleton width="100%" height={22} />
105
+ <Skeleton
106
+ width={128}
107
+ height={16}
108
+ sx={{
109
+ mt: 4,
110
+ }}
111
+ />
112
+ </Box>
113
+ </Box>
114
+ ))}
115
+ </>
116
+ )}
117
+ {!isLoading && !list.length && (
118
+ <Box
119
+ sx={{
120
+ display: 'flex',
121
+ alignItems: 'center',
122
+ justifyContent: 'space-between',
123
+ flexDirection: 'column',
124
+ mt: 56,
125
+ gap: 12,
126
+ typography: 'body2',
127
+ color: 'text.secondary',
128
+ }}
129
+ >
130
+ <EmptyDataIcon
131
+ sx={{
132
+ width: 60,
133
+ height: 60,
134
+ borderRadius: 12,
135
+ }}
136
+ />
137
+ <Trans>This is empty</Trans>
138
+ </Box>
139
+ )}
140
+ {!isLoading &&
141
+ list?.map((item) => {
142
+ if (!item?.key || !item.chainId) return null;
143
+ const tx = item.extend?.tx;
144
+ const status = item.extend?.status;
145
+ let url = '';
146
+ if (tx) {
147
+ const scanUrl = getChain(item.chainId)?.scanUrl ?? '';
148
+ if (status === StateText.Warning && account) {
149
+ url = scanUrl + `/address/${account}`;
150
+ } else {
151
+ url = scanUrl + `/tx/${tx}`;
152
+ }
153
+ }
154
+ return (
155
+ <Box
156
+ key={item.id}
157
+ onClick={() => {
158
+ if (!url) return;
159
+ window.open(url);
160
+ }}
161
+ sx={{
162
+ position: 'relative',
163
+ display: 'flex',
164
+ pl: 20,
165
+ py: 16,
166
+ borderRadius: 8,
167
+ ...(url
168
+ ? {
169
+ cursor: 'pointer',
170
+ '&:hover': {
171
+ backgroundColor: 'hover.default',
172
+ },
173
+ '&:not(:hover) > svg:last-child': {
174
+ visibility: 'hidden',
175
+ },
176
+ }
177
+ : {}),
178
+ }}
179
+ >
180
+ <Box
181
+ sx={{
182
+ position: 'relative',
183
+ display: 'flex',
184
+ alignItems: 'center',
185
+ justifyContent: 'center',
186
+ width: 40,
187
+ height: 40,
188
+ borderRadius: '50%',
189
+ backgroundColor: 'background.paperContrast',
190
+ }}
191
+ >
192
+ <SubmissionTypeIcon brief={item.key} chainId={item.chainId} />
193
+ <Box
194
+ sx={{
195
+ position: 'absolute',
196
+ bottom: -1,
197
+ right: -1,
198
+ display: 'flex',
199
+ width: 16,
200
+ height: 16,
201
+ color: 'primary.contrastText',
202
+ backgroundColor: 'background.paperContrast',
203
+ borderWidth: 1,
204
+ borderStyle: 'solid',
205
+ borderColor: 'border.default',
206
+ borderRadius: '50%',
207
+ }}
208
+ >
209
+ <SubmissionStatusIcon status={status} />
210
+ </Box>
211
+ </Box>
212
+ <Box
213
+ sx={{
214
+ ml: 12,
215
+ flex: 1,
216
+ }}
217
+ >
218
+ <Box
219
+ sx={{
220
+ display: 'flex',
221
+ alignItems: 'center',
222
+ fontWeight: 600,
223
+ }}
224
+ >
225
+ {getTitleText(item.key, status)}
226
+ </Box>
227
+ <Box
228
+ sx={{
229
+ mt: 12,
230
+ typography: 'h6',
231
+ color: 'text.secondary',
232
+ }}
233
+ >
234
+ {formatReadableTimeAgo({
235
+ time: Number(item.createTime),
236
+ })}
237
+ </Box>
238
+ </Box>
239
+ {url ? (
240
+ <Box
241
+ component={ArrowRight}
242
+ sx={{
243
+ mr: 24,
244
+ width: 18,
245
+ height: 18,
246
+ alignSelf: 'center',
247
+ }}
248
+ />
249
+ ) : (
250
+ <Box />
251
+ )}
252
+ </Box>
253
+ );
254
+ })}
255
+ {isFetchingNextPage ? (
256
+ <Box
257
+ sx={{
258
+ display: 'flex',
259
+ alignItems: 'center',
260
+ justifyContent: 'center',
261
+ gap: 4,
262
+ mt: 16,
263
+ color: 'text.secondary',
264
+ typography: 'body2',
265
+ }}
266
+ >
267
+ <RotatingIcon
268
+ sx={{
269
+ width: 20,
270
+ color: 'primary.main',
271
+ }}
272
+ />
273
+ <Trans>Loading more</Trans>
274
+ </Box>
275
+ ) : (
276
+ ''
277
+ )}
278
+ </>
279
+ );
280
+ }
281
+
282
+ export function SubmissionStatusIcon({ status }: { status: string }) {
283
+ switch (status) {
284
+ case StateText.Success:
285
+ return (
286
+ <Box
287
+ component={PassedIcon}
288
+ sx={{
289
+ width: '100%',
290
+ height: '100%',
291
+ }}
292
+ />
293
+ );
294
+ case StateText.Failed:
295
+ return (
296
+ <Box
297
+ component={RejectedIcon}
298
+ sx={{
299
+ width: '100%',
300
+ height: '100%',
301
+ }}
302
+ />
303
+ );
304
+ case StateText.Warning:
305
+ return (
306
+ <Box
307
+ component={InvalidIcon}
308
+ sx={{
309
+ width: '100%',
310
+ height: '100%',
311
+ }}
312
+ />
313
+ );
314
+ case StateText.Running:
315
+ return (
316
+ <Box
317
+ sx={{
318
+ display: 'flex',
319
+ alignItems: 'center',
320
+ justifyContent: 'center',
321
+ backgroundColor: 'primary.main',
322
+ borderRadius: '50%',
323
+ width: '100%',
324
+ height: '100%',
325
+ }}
326
+ >
327
+ <Box
328
+ component="svg"
329
+ width={24}
330
+ height={24}
331
+ viewBox="0 0 24 24"
332
+ fill="none"
333
+ xmlns="http://www.w3.org/2000/svg"
334
+ sx={{
335
+ width: '92%',
336
+ height: '92%',
337
+ color: 'primary.contrastText',
338
+ }}
339
+ >
340
+ <path
341
+ d="M6 13C6 11.35 6.67 9.85 7.76 8.76L6.34 7.34C4.9 8.79 4 10.79 4 13C4 17.08 7.05 20.44 11 20.93V18.91C8.17 18.43 6 15.97 6 13ZM20 13C20 8.58 16.42 5 12 5C11.94 5 11.88 5.01 11.82 5.01L12.91 3.92L11.5 2.5L8 6L11.5 9.5L12.91 8.09L11.83 7.01C11.89 7.01 11.95 7 12 7C15.31 7 18 9.69 18 13C18 15.97 15.83 18.43 13 18.91V20.93C16.95 20.44 20 17.08 20 13Z"
342
+ fill="currentColor"
343
+ />
344
+ </Box>
345
+ </Box>
346
+ );
347
+
348
+ default:
349
+ break;
350
+ }
351
+ return null;
352
+ }
353
+
354
+ export function getTitleText(brief: string, status: string) {
355
+ const prefix = getSubmitTitleByBrief(brief);
356
+ const statusLangMap: { [key: string]: string } = {
357
+ [StateText.Success]: t`Succeeded`,
358
+ [StateText.Failed]: t`Failed`,
359
+ [StateText.Running]: t`Pending`,
360
+ [StateText.Warning]: t`Reset`,
361
+ };
362
+ const suffix = statusLangMap[status];
363
+ return suffix ? `${prefix} ${suffix}` : prefix;
364
+ }
365
+
366
+ export function getSubmitTitleByBrief(brief: string) {
367
+ const prefixTypeTextMap: {
368
+ [key: string]: string;
369
+ } = {
370
+ 'common.approve.resetBrief': t`Reset`,
371
+ 'common.approve.brief': t`Approve`,
372
+ };
373
+ const matchPrefixKey = Object.keys(prefixTypeTextMap).find(
374
+ (prefixKey) => brief.indexOf(prefixKey) === 0,
375
+ );
376
+ if (matchPrefixKey) {
377
+ return prefixTypeTextMap[matchPrefixKey];
378
+ }
379
+ switch (brief) {
380
+ case 'tradingCard.submissionBrief':
381
+ case 'limit.main.title':
382
+ case 'exchange.tabs.RFQ':
383
+ case 'bridge.order.execute-bridge':
384
+ return t`Swap`;
385
+ case 'bridge.order.execute-bridge':
386
+ return t`Transfer`;
387
+ case 'liquidity.operate.title':
388
+ case 'pool.amm-v2.add-liquidity.title':
389
+ case 'pool.amm-v3.add-liquidity.title':
390
+ return t`Add Liquidity`;
391
+ case 'liquidity.operate.remove.title':
392
+ case 'pool.amm-v2.remove-liquidity.title':
393
+ case 'pool.amm-v3.remove-liquidity.title':
394
+ return t`Remove Liquidity`;
395
+ case 'pool.my-pools.create-a-pool':
396
+ case 'pool.amm-v2.create.title':
397
+ case 'pool.amm-v3.create.title':
398
+ return t`Pool Creation`;
399
+ case 'mining.stake':
400
+ return t`Stake`;
401
+ case 'mining.submit.remove-title':
402
+ return t`End mining\n`;
403
+ case 'mining.deposit.receive-reward':
404
+ case 'pool.amm-v3.receive-reward.title':
405
+ return t`Claim Rewards`;
406
+ case 'nav.create-mining':
407
+ return t`Create Liquidity Mining`;
408
+
409
+ case 'wallet.account.card.operate.send':
410
+ return t`Send`;
411
+
412
+ default:
413
+ return null;
414
+ }
415
+ }
416
+
417
+ type IconMap = {
418
+ [key: string]: React.ReactElement;
419
+ };
420
+ export function SubmissionTypeIcon({
421
+ brief,
422
+ chainId,
423
+ }: {
424
+ brief: string;
425
+ chainId: number;
426
+ }) {
427
+ const { getChain } = useWalletConnectContext();
428
+ const prefixTypeIconMap: IconMap = {
429
+ 'common.approve.resetBrief': (
430
+ <svg
431
+ width="24"
432
+ height="24"
433
+ viewBox="0 0 24 24"
434
+ fill="none"
435
+ xmlns="http://www.w3.org/2000/svg"
436
+ >
437
+ <path
438
+ d="M6 13C6 11.35 6.67 9.85 7.76 8.76L6.34 7.34C4.9 8.79 4 10.79 4 13C4 17.08 7.05 20.44 11 20.93V18.91C8.17 18.43 6 15.97 6 13ZM20 13C20 8.58 16.42 5 12 5C11.94 5 11.88 5.01 11.82 5.01L12.91 3.92L11.5 2.5L8 6L11.5 9.5L12.91 8.09L11.83 7.01C11.89 7.01 11.95 7 12 7C15.31 7 18 9.69 18 13C18 15.97 15.83 18.43 13 18.91V20.93C16.95 20.44 20 17.08 20 13Z"
439
+ fill="currentColor"
440
+ />
441
+ </svg>
442
+ ),
443
+ 'common.approve.brief': (
444
+ <svg
445
+ width="24"
446
+ height="24"
447
+ viewBox="0 0 24 24"
448
+ fill="none"
449
+ xmlns="http://www.w3.org/2000/svg"
450
+ >
451
+ <path
452
+ fillRule="evenodd"
453
+ clipRule="evenodd"
454
+ d="M18 8H17V6C17 3.24 14.76 1 12 1C9.24 1 7 3.24 7 6V8H6C4.9 8 4 8.9 4 10V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V10C20 8.9 19.1 8 18 8ZM9 6C9 4.34 10.34 3 12 3C13.66 3 15 4.34 15 6V8H9V6ZM14 14C14 14.7364 13.5967 15.3831 13 15.7303V18H11V15.7303C10.4033 15.3831 10 14.7364 10 14C10 12.9 10.9 12 12 12C13.1 12 14 12.9 14 14Z"
455
+ fill="currentColor"
456
+ />
457
+ </svg>
458
+ ),
459
+ };
460
+ const matchPrefixKey = Object.keys(prefixTypeIconMap).find(
461
+ (prefixKey) => brief.indexOf(prefixKey) === 0,
462
+ );
463
+ if (matchPrefixKey) {
464
+ return (
465
+ <Box
466
+ sx={{
467
+ width: 24,
468
+ height: 24,
469
+ color: 'text.secondary',
470
+ }}
471
+ >
472
+ {prefixTypeIconMap[matchPrefixKey]}
473
+ </Box>
474
+ );
475
+ }
476
+
477
+ if (brief === 'wallet.account.card.operate.send') {
478
+ return <ArrowBack className="w-6 h-6 rotate-90" />;
479
+ }
480
+
481
+ const swapTypes = [
482
+ 'tradingCard.submissionBrief',
483
+ 'limit.main.title',
484
+ 'exchange.tabs.RFQ',
485
+ 'bridge.order.execute-bridge',
486
+ ];
487
+ if (swapTypes.includes(brief)) {
488
+ const chainLogo = getChain(chainId)?.logo;
489
+ if (!chainLogo) return null;
490
+ return (
491
+ <Box
492
+ sx={{
493
+ width: 24,
494
+ height: 24,
495
+ color: 'text.secondary',
496
+ }}
497
+ >
498
+ {chainLogo}
499
+ </Box>
500
+ );
501
+ }
502
+
503
+ const PoolIcon = (
504
+ <svg
505
+ width="24"
506
+ height="24"
507
+ viewBox="0 0 24 24"
508
+ fill="none"
509
+ xmlns="http://www.w3.org/2000/svg"
510
+ >
511
+ <path
512
+ className="active-color"
513
+ fillRule="evenodd"
514
+ clipRule="evenodd"
515
+ d="M7 3.77979C7.47668 3.77979 7.8871 4.11624 7.98058 4.58367C8.22292 5.79538 8.95318 6.97446 9.92213 7.76492C11.2407 8.84061 12 10.2058 12 11.7298C12 14.4891 9.76515 16.7798 7 16.7798C4.23484 16.7798 2 14.4891 2 11.7298C2 10.2237 2.74214 8.83345 4.0853 7.75892C5.05247 6.98519 5.77579 5.80184 6.01942 4.58367C6.1129 4.11624 6.52332 3.77979 7 3.77979Z"
516
+ fill="currentColor"
517
+ />
518
+ <path
519
+ fillRule="evenodd"
520
+ clipRule="evenodd"
521
+ d="M14 1.5C14.4794 1.5 14.8913 1.84022 14.982 2.31097C15.4329 4.65329 16.7694 6.47608 18.6541 8.07351C20.8277 9.91584 22 12.1786 22 14.6364C22 19.2681 18.3582 22.5 14 22.5C11.1133 22.5 8.5408 21.082 7.13281 18.7785C10.9673 18.7065 14 15.5225 14 11.7298C14 9.47755 12.8621 7.58357 11.1898 6.21802C12.0968 5.07887 12.732 3.79698 13.018 2.31097C13.1086 1.84022 13.5206 1.5 14 1.5Z"
522
+ fill="currentColor"
523
+ />
524
+ </svg>
525
+ );
526
+
527
+ const MiningIcon = (
528
+ <svg
529
+ width="24"
530
+ height="24"
531
+ viewBox="0 0 24 24"
532
+ fill="none"
533
+ xmlns="http://www.w3.org/2000/svg"
534
+ >
535
+ <g clipPath="url(#clip0_331_763)">
536
+ <path
537
+ d="M9.69961 7.1L9.39961 8H14.6996L13.0996 3.3L12.6996 2H11.3996L9.69961 7.1Z"
538
+ fill="currentColor"
539
+ />
540
+ <path
541
+ d="M9.39961 10L12.0996 18.2L14.6996 10H9.39961Z"
542
+ fill="currentColor"
543
+ />
544
+ <path
545
+ d="M16.7996 8H22.4996C22.4996 7.9 22.3996 7.9 22.3996 7.8L18.0996 2.6C17.9996 2.5 17.8996 2.3 17.6996 2.2C17.5996 2.1 17.3996 2 17.1996 2C16.9996 2 16.8996 2 16.6996 2H14.7996L16.7996 8Z"
546
+ fill="currentColor"
547
+ />
548
+ <path
549
+ d="M16.7996 10L13.1996 21.1C13.2996 21 13.3996 20.9 13.3996 20.9L22.3996 10.2C22.3996 10.1 22.4996 10.1 22.4996 10H16.7996Z"
550
+ fill="currentColor"
551
+ />
552
+ <path
553
+ className="active-color"
554
+ d="M7.29961 8L9.09961 2.7L9.29961 2H7.39961C7.29961 2 7.09961 2 6.89961 2.1C6.69961 2.1 6.59961 2.2 6.39961 2.3C6.29961 2.4 6.19961 2.5 6.09961 2.6L1.69961 7.9L1.59961 8H7.29961Z"
555
+ fill="currentColor"
556
+ />
557
+ <path
558
+ className="active-color"
559
+ d="M7.29961 10H1.59961C1.59961 10.1 1.69961 10.1 1.69961 10.2L10.6996 21C10.7996 21.1 10.7996 21.2 10.8996 21.3L7.29961 10Z"
560
+ fill="currentColor"
561
+ />
562
+ </g>
563
+ <defs>
564
+ <clipPath id="clip0_331_763">
565
+ <rect
566
+ width="20.9"
567
+ height="19.2"
568
+ fill="white"
569
+ transform="translate(1.59961 2)"
570
+ />
571
+ </clipPath>
572
+ </defs>
573
+ </svg>
574
+ );
575
+
576
+ const typeIconMap: IconMap = {
577
+ 'pool.my-pools.create-a-pool': PoolIcon,
578
+ 'pool.detail.modify-dpp-parameters': PoolIcon,
579
+ 'liquidity.operate.remove.title': PoolIcon,
580
+ 'liquidity.operate.title': PoolIcon,
581
+ 'pool.amm-v2.create.title': PoolIcon,
582
+ 'pool.amm-v2.remove-liquidity.title': PoolIcon,
583
+ 'pool.amm-v2.add-liquidity.title': PoolIcon,
584
+ 'pool.amm-v3.create.title': PoolIcon,
585
+ 'pool.amm-v3.add-liquidity.title': PoolIcon,
586
+ 'pool.amm-v3.remove-liquidity.title': PoolIcon,
587
+ 'pool.amm-v3.receive-reward.title': PoolIcon,
588
+ 'nav.create-mining': MiningIcon,
589
+ 'mining.deposit.receive-reward': MiningIcon,
590
+ 'mining.stake': MiningIcon,
591
+ 'mining.submit.remove-title': MiningIcon,
592
+ };
593
+ const Icon = typeIconMap[brief];
594
+ if (!Icon) return null;
595
+ return (
596
+ <Box
597
+ sx={{
598
+ width: 24,
599
+ height: 24,
600
+ color: 'text.secondary',
601
+ }}
602
+ >
603
+ {Icon}
604
+ </Box>
605
+ );
606
+ }