@rango-dev/widget-embedded 0.30.1 → 0.31.1-next.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/CHANGELOG.md +35 -0
- package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts +1 -653
- package/dist/components/ConfirmWalletsModal/ConfirmWallets.styles.d.ts.map +1 -1
- package/dist/components/ConfirmWalletsModal/ConfirmWalletsModal.d.ts.map +1 -1
- package/dist/components/ConfirmWalletsModal/WalletList.d.ts.map +1 -1
- package/dist/components/CustomDestination/CustomDestination.d.ts +4 -0
- package/dist/components/CustomDestination/CustomDestination.d.ts.map +1 -0
- package/dist/components/CustomDestination/CustomDestination.styles.d.ts +495 -0
- package/dist/components/CustomDestination/CustomDestination.styles.d.ts.map +1 -0
- package/dist/components/CustomDestination/CustomDestination.types.d.ts +7 -0
- package/dist/components/CustomDestination/CustomDestination.types.d.ts.map +1 -0
- package/dist/components/FilterSelector/FilterSelector.d.ts +4 -0
- package/dist/components/FilterSelector/FilterSelector.d.ts.map +1 -0
- package/dist/components/FilterSelector/FilterSelector.style.d.ts +962 -0
- package/dist/components/FilterSelector/FilterSelector.style.d.ts.map +1 -0
- package/dist/components/FilterSelector/FilterSelector.type.d.ts +14 -0
- package/dist/components/FilterSelector/FilterSelector.type.d.ts.map +1 -0
- package/dist/components/FilterSelector/FilterSelectorContent.d.ts +4 -0
- package/dist/components/FilterSelector/FilterSelectorContent.d.ts.map +1 -0
- package/dist/components/FilterSelector/index.d.ts +2 -0
- package/dist/components/FilterSelector/index.d.ts.map +1 -0
- package/dist/components/Quote/Quote.styles.d.ts +3 -3
- package/dist/components/RefreshModal/RefreshModal.styles.d.ts +3 -3
- package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
- package/dist/components/SwapDetails/SwapDetails.d.ts.map +1 -1
- package/dist/components/SwapDetails/SwapDetails.styles.d.ts.map +1 -1
- package/dist/components/SwapDetailsModal/SwapDetailsModal.WalletState.d.ts.map +1 -1
- package/dist/components/TokenList/TokenList.d.ts.map +1 -1
- package/dist/containers/Inputs/Inputs.d.ts.map +1 -1
- package/dist/containers/Wallets/Wallets.d.ts.map +1 -1
- package/dist/containers/WidgetInfo/WidgetInfo.d.ts.map +1 -1
- package/dist/containers/WidgetInfo/WidgetInfo.types.d.ts +5 -1
- package/dist/containers/WidgetInfo/WidgetInfo.types.d.ts.map +1 -1
- package/dist/hooks/useObserveBalanceChanges/index.d.ts +2 -0
- package/dist/hooks/useObserveBalanceChanges/index.d.ts.map +1 -0
- package/dist/hooks/useObserveBalanceChanges/useObserveBalanceChanges.d.ts +4 -0
- package/dist/hooks/useObserveBalanceChanges/useObserveBalanceChanges.d.ts.map +1 -0
- package/dist/hooks/usePrepareBlockchainList/usePrepareBlockchainList.helpers.d.ts +2 -2
- package/dist/hooks/useTheme.d.ts.map +1 -1
- package/dist/hooks/useUpdateQuoteInputs/index.d.ts +2 -0
- package/dist/hooks/useUpdateQuoteInputs/index.d.ts.map +1 -0
- package/dist/hooks/useUpdateQuoteInputs/useUpdateQuoteInputs.d.ts +3 -0
- package/dist/hooks/useUpdateQuoteInputs/useUpdateQuoteInputs.d.ts.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +4 -4
- package/dist/pages/HistoryPage.d.ts.map +1 -1
- package/dist/pages/Home.d.ts.map +1 -1
- package/dist/pages/WalletsPage.d.ts.map +1 -1
- package/dist/services/cacheService.d.ts +21 -0
- package/dist/services/cacheService.d.ts.map +1 -0
- package/dist/store/notification.d.ts.map +1 -1
- package/dist/store/slices/config.d.ts +2 -1
- package/dist/store/slices/config.d.ts.map +1 -1
- package/dist/store/slices/data.d.ts +2 -1
- package/dist/store/slices/data.d.ts.map +1 -1
- package/dist/store/utils/data.d.ts +20 -0
- package/dist/store/utils/data.d.ts.map +1 -0
- package/dist/store/utils/data.test.d.ts +2 -0
- package/dist/store/utils/data.test.d.ts.map +1 -0
- package/dist/store/utils/index.d.ts +2 -0
- package/dist/store/utils/index.d.ts.map +1 -0
- package/dist/test-utils/fixtures.d.ts +1 -0
- package/dist/test-utils/fixtures.d.ts.map +1 -1
- package/dist/types/config.d.ts +5 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/event.d.ts +21 -15
- package/dist/types/event.d.ts.map +1 -1
- package/dist/types/notification.d.ts +1 -0
- package/dist/types/notification.d.ts.map +1 -1
- package/dist/types/quote.d.ts +9 -1
- package/dist/types/quote.d.ts.map +1 -1
- package/dist/types/wallets.d.ts +1 -1
- package/dist/types/wallets.d.ts.map +1 -1
- package/dist/utils/events.d.ts +8 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/settings.d.ts +1 -1
- package/dist/utils/swap.d.ts +1 -1
- package/dist/utils/swap.d.ts.map +1 -1
- package/dist/widget-embedded.build.json +1 -1
- package/package.json +10 -10
- package/src/components/ConfirmWalletsModal/ConfirmWallets.styles.ts +1 -49
- package/src/components/ConfirmWalletsModal/ConfirmWalletsModal.tsx +61 -179
- package/src/components/ConfirmWalletsModal/WalletList.tsx +16 -4
- package/src/components/CustomDestination/CustomDestination.styles.ts +45 -0
- package/src/components/CustomDestination/CustomDestination.tsx +148 -0
- package/src/components/CustomDestination/CustomDestination.types.ts +7 -0
- package/src/components/FilterSelector/FilterSelector.style.ts +95 -0
- package/src/components/FilterSelector/FilterSelector.tsx +42 -0
- package/src/components/FilterSelector/FilterSelector.type.ts +15 -0
- package/src/components/FilterSelector/FilterSelectorContent.tsx +60 -0
- package/src/components/FilterSelector/index.ts +1 -0
- package/src/components/SearchInput/SearchInput.tsx +2 -0
- package/src/components/SwapDetails/SwapDetails.styles.ts +10 -0
- package/src/components/SwapDetails/SwapDetails.tsx +25 -13
- package/src/components/SwapDetailsModal/SwapDetailsModal.WalletState.tsx +28 -13
- package/src/components/TokenList/TokenList.tsx +8 -2
- package/src/containers/Inputs/Inputs.tsx +1 -0
- package/src/containers/Wallets/Wallets.tsx +7 -2
- package/src/containers/WidgetInfo/WidgetInfo.tsx +26 -0
- package/src/containers/WidgetInfo/WidgetInfo.types.ts +5 -1
- package/src/hooks/useObserveBalanceChanges/index.ts +1 -0
- package/src/hooks/useObserveBalanceChanges/useObserveBalanceChanges.ts +68 -0
- package/src/hooks/useTheme.ts +11 -5
- package/src/hooks/useUpdateQuoteInputs/index.ts +1 -0
- package/src/hooks/useUpdateQuoteInputs/useUpdateQuoteInputs.ts +50 -0
- package/src/index.ts +4 -0
- package/src/pages/HistoryPage.tsx +180 -26
- package/src/pages/Home.tsx +6 -3
- package/src/pages/WalletsPage.tsx +29 -13
- package/src/services/cacheService.ts +35 -0
- package/src/store/notification.ts +13 -1
- package/src/store/quote.ts +1 -1
- package/src/store/slices/config.ts +39 -1
- package/src/store/slices/data.test.ts +84 -0
- package/src/store/slices/data.ts +44 -24
- package/src/store/utils/data.test.ts +175 -0
- package/src/store/utils/data.ts +117 -0
- package/src/store/utils/index.ts +1 -0
- package/src/test-utils/fixtures.ts +14 -7
- package/src/types/config.ts +3 -1
- package/src/types/event.ts +32 -16
- package/src/types/notification.ts +1 -0
- package/src/types/quote.ts +11 -0
- package/src/types/wallets.ts +1 -1
- package/src/utils/events.ts +27 -0
- package/src/utils/swap.ts +4 -4
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
BlockchainAndTokenConfig,
|
|
5
5
|
QuoteEventData,
|
|
6
6
|
Tokens,
|
|
7
|
+
UiEventData,
|
|
7
8
|
WalletEventData,
|
|
8
9
|
WalletInfoWithExtra,
|
|
9
10
|
WidgetColors,
|
|
@@ -71,6 +72,7 @@ import { widgetEventEmitter } from './services/eventEmitter';
|
|
|
71
72
|
import {
|
|
72
73
|
WidgetEvents as MainEvents,
|
|
73
74
|
QuoteEventTypes,
|
|
75
|
+
UiEventTypes,
|
|
74
76
|
WalletEventTypes,
|
|
75
77
|
WidgetEvents,
|
|
76
78
|
} from './types';
|
|
@@ -108,6 +110,7 @@ export type {
|
|
|
108
110
|
WidgetVariant,
|
|
109
111
|
WalletEventData,
|
|
110
112
|
QuoteEventData,
|
|
113
|
+
UiEventData,
|
|
111
114
|
WalletInfoWithExtra,
|
|
112
115
|
};
|
|
113
116
|
export {
|
|
@@ -133,6 +136,7 @@ export {
|
|
|
133
136
|
MainEvents,
|
|
134
137
|
QuoteEventTypes,
|
|
135
138
|
WalletEventTypes,
|
|
139
|
+
UiEventTypes,
|
|
136
140
|
RouteEventType,
|
|
137
141
|
StepEventType,
|
|
138
142
|
StepExecutionEventStatus,
|
|
@@ -1,15 +1,30 @@
|
|
|
1
|
-
import type { PendingSwap, PendingSwapStep } from 'rango-types';
|
|
2
|
-
|
|
3
1
|
import { i18n } from '@lingui/core';
|
|
4
2
|
import { useManager } from '@rango-dev/queue-manager-react';
|
|
5
|
-
import {
|
|
6
|
-
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
darkTheme,
|
|
6
|
+
Divider,
|
|
7
|
+
MessageBox,
|
|
8
|
+
NotFound,
|
|
9
|
+
styled,
|
|
10
|
+
Typography,
|
|
11
|
+
} from '@rango-dev/ui';
|
|
12
|
+
import {
|
|
13
|
+
type PendingSwap,
|
|
14
|
+
type PendingSwapStep,
|
|
15
|
+
TransactionStatus,
|
|
16
|
+
} from 'rango-types';
|
|
17
|
+
import React, { useMemo, useState } from 'react';
|
|
7
18
|
import { useNavigate } from 'react-router-dom';
|
|
8
19
|
|
|
20
|
+
import { WatermarkedModal } from '../components/common/WatermarkedModal';
|
|
21
|
+
import { FilterSelector } from '../components/FilterSelector';
|
|
22
|
+
import { SuffixContainer } from '../components/HeaderButtons/HeaderButtons.styles';
|
|
9
23
|
import { HistoryGroupedList } from '../components/HistoryGroupedList';
|
|
10
24
|
import { NotFoundContainer } from '../components/HistoryGroupedList/HistoryGroupedList.styles';
|
|
11
25
|
import { Layout, PageContainer } from '../components/Layout';
|
|
12
26
|
import { SearchInput } from '../components/SearchInput';
|
|
27
|
+
import { getContainer } from '../utils/common';
|
|
13
28
|
import { groupSwapsByDate } from '../utils/date';
|
|
14
29
|
import { containsText } from '../utils/numbers';
|
|
15
30
|
import { getPendingSwaps } from '../utils/queue';
|
|
@@ -23,6 +38,33 @@ const HistoryGroupedListContainer = styled('div', {
|
|
|
23
38
|
height: '100%',
|
|
24
39
|
});
|
|
25
40
|
|
|
41
|
+
const SearchAndFilterBar = styled('div', {
|
|
42
|
+
display: 'flex',
|
|
43
|
+
justifyContent: 'center',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const Description = styled('div', {
|
|
48
|
+
'._typography': {
|
|
49
|
+
color: '$neutral700',
|
|
50
|
+
[`.${darkTheme}&`]: {
|
|
51
|
+
color: '$neutral900',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const transactionStatusFilters = [
|
|
57
|
+
{
|
|
58
|
+
id: TransactionStatus.SUCCESS,
|
|
59
|
+
title: i18n.t('Complete'),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: TransactionStatus.RUNNING,
|
|
63
|
+
title: i18n.t('Running'),
|
|
64
|
+
},
|
|
65
|
+
{ id: TransactionStatus.FAILED, title: i18n.t('Failed') },
|
|
66
|
+
];
|
|
67
|
+
|
|
26
68
|
const isStepContainsText = (steps: PendingSwapStep[], value: string) => {
|
|
27
69
|
if (!steps?.length) {
|
|
28
70
|
return false;
|
|
@@ -41,50 +83,116 @@ export function HistoryPage() {
|
|
|
41
83
|
const { manager, state } = useManager();
|
|
42
84
|
const list: PendingSwap[] = getPendingSwaps(manager).map(({ swap }) => swap);
|
|
43
85
|
const [searchedFor, setSearchedFor] = useState<string>('');
|
|
86
|
+
const [openFilterSelector, setOpenFilterSelector] = useState(false);
|
|
44
87
|
const loading = !state.loadedFromPersistor;
|
|
45
|
-
|
|
88
|
+
const [filterBy, setFilterBy] = useState('');
|
|
89
|
+
const [openClearModal, setOpenClearModal] = useState(false);
|
|
46
90
|
const searchHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
47
91
|
const value = event.target.value;
|
|
48
92
|
setSearchedFor(value);
|
|
49
93
|
};
|
|
50
94
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
95
|
+
const filteredList = useMemo(() => {
|
|
96
|
+
if (!searchedFor && !filterBy) {
|
|
97
|
+
return list;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return list.filter((swap) => {
|
|
101
|
+
const { inputAmount, status, steps, requestId } = swap;
|
|
102
|
+
|
|
103
|
+
const matchesSearch =
|
|
104
|
+
!searchedFor ||
|
|
105
|
+
containsText(inputAmount, searchedFor) ||
|
|
106
|
+
containsText(status, searchedFor) ||
|
|
107
|
+
isStepContainsText(steps, searchedFor) ||
|
|
108
|
+
containsText(requestId, searchedFor);
|
|
109
|
+
|
|
110
|
+
const matchesFilter = !filterBy || filterBy === status;
|
|
111
|
+
|
|
112
|
+
return matchesSearch && matchesFilter;
|
|
113
|
+
});
|
|
114
|
+
}, [list, searchedFor, filterBy]);
|
|
61
115
|
|
|
62
116
|
const isEmpty = !filteredList?.length && !loading;
|
|
63
117
|
|
|
118
|
+
const onCloseModal = () => setOpenClearModal(false);
|
|
119
|
+
const onClear = async () => {
|
|
120
|
+
try {
|
|
121
|
+
await manager?.clearQueue();
|
|
122
|
+
|
|
123
|
+
setOpenClearModal(false);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
console.log(e);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const isClearButtonDisabled = useMemo(() => {
|
|
129
|
+
return !list.some(
|
|
130
|
+
(swap) =>
|
|
131
|
+
swap.status === TransactionStatus.SUCCESS ||
|
|
132
|
+
swap.status === TransactionStatus.FAILED
|
|
133
|
+
);
|
|
134
|
+
}, [list]);
|
|
135
|
+
|
|
64
136
|
return (
|
|
65
137
|
<Layout
|
|
66
138
|
header={{
|
|
67
139
|
title: i18n.t('History'),
|
|
140
|
+
suffix: (
|
|
141
|
+
<SuffixContainer>
|
|
142
|
+
<Button
|
|
143
|
+
disabled={isClearButtonDisabled}
|
|
144
|
+
variant="ghost"
|
|
145
|
+
size="xsmall"
|
|
146
|
+
onClick={() => setOpenClearModal(true)}>
|
|
147
|
+
<Typography size="medium" variant="label" color="error">
|
|
148
|
+
{i18n.t('Clear all Txs')}
|
|
149
|
+
</Typography>
|
|
150
|
+
</Button>
|
|
151
|
+
</SuffixContainer>
|
|
152
|
+
),
|
|
68
153
|
}}>
|
|
69
154
|
<PageContainer>
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
155
|
+
<SearchAndFilterBar>
|
|
156
|
+
<SearchInput
|
|
157
|
+
setValue={setSearchedFor}
|
|
158
|
+
fullWidth
|
|
159
|
+
variant="contained"
|
|
160
|
+
placeholder={i18n.t('Search Transaction')}
|
|
161
|
+
autoFocus
|
|
162
|
+
onChange={searchHandler}
|
|
163
|
+
style={{ height: 36 }}
|
|
164
|
+
value={searchedFor}
|
|
165
|
+
/>
|
|
166
|
+
<Divider size={10} direction="horizontal" />
|
|
167
|
+
|
|
168
|
+
<FilterSelector
|
|
169
|
+
filterBy={filterBy}
|
|
170
|
+
open={openFilterSelector}
|
|
171
|
+
onOpenChange={(open) => setOpenFilterSelector(open)}
|
|
172
|
+
onClickItem={(id) => setFilterBy(id)}
|
|
173
|
+
list={transactionStatusFilters}
|
|
174
|
+
/>
|
|
175
|
+
</SearchAndFilterBar>
|
|
176
|
+
|
|
79
177
|
<Divider size="16" />
|
|
80
178
|
<HistoryGroupedListContainer>
|
|
81
179
|
{isEmpty && (
|
|
82
180
|
<NotFoundContainer>
|
|
83
181
|
<Divider size={32} />
|
|
84
182
|
<NotFound
|
|
85
|
-
title={
|
|
183
|
+
title={
|
|
184
|
+
searchedFor
|
|
185
|
+
? i18n.t('No results found')
|
|
186
|
+
: i18n.t('No transactions')
|
|
187
|
+
}
|
|
188
|
+
titleColor={!searchedFor ? '$info' : undefined}
|
|
189
|
+
hasIcon={!!searchedFor}
|
|
86
190
|
description={
|
|
87
|
-
searchedFor
|
|
191
|
+
searchedFor
|
|
192
|
+
? i18n.t('Try using different keywords')
|
|
193
|
+
: i18n.t(
|
|
194
|
+
'Your transaction history is stored locally and will appear here after you start a swap'
|
|
195
|
+
)
|
|
88
196
|
}
|
|
89
197
|
/>
|
|
90
198
|
</NotFoundContainer>
|
|
@@ -99,6 +207,52 @@ export function HistoryPage() {
|
|
|
99
207
|
)}
|
|
100
208
|
</HistoryGroupedListContainer>
|
|
101
209
|
</PageContainer>
|
|
210
|
+
<WatermarkedModal
|
|
211
|
+
open={openClearModal}
|
|
212
|
+
onClose={onCloseModal}
|
|
213
|
+
container={getContainer()}>
|
|
214
|
+
<Divider size={20} />
|
|
215
|
+
<MessageBox
|
|
216
|
+
type="warning"
|
|
217
|
+
title={i18n.t('Clear History')}
|
|
218
|
+
description={
|
|
219
|
+
<Description>
|
|
220
|
+
<Typography variant="body" size="medium">
|
|
221
|
+
{i18n.t(
|
|
222
|
+
'This would clear all succeeded/failed transactions from widget. Do you wish to proceed?'
|
|
223
|
+
)}
|
|
224
|
+
</Typography>
|
|
225
|
+
<Divider size={'24'} />
|
|
226
|
+
|
|
227
|
+
<Typography variant="body" size="small">
|
|
228
|
+
{i18n.t(
|
|
229
|
+
'Hint: This does not clear your transaction history on blockchain. Only hides them in this page.'
|
|
230
|
+
)}
|
|
231
|
+
</Typography>
|
|
232
|
+
</Description>
|
|
233
|
+
}
|
|
234
|
+
/>
|
|
235
|
+
<Divider size={40} />
|
|
236
|
+
|
|
237
|
+
<Divider size={10} />
|
|
238
|
+
<Button
|
|
239
|
+
variant="contained"
|
|
240
|
+
type="primary"
|
|
241
|
+
size="large"
|
|
242
|
+
onClick={onClear}>
|
|
243
|
+
{i18n.t('Yes, Clear history')}
|
|
244
|
+
</Button>
|
|
245
|
+
<Divider size={10} />
|
|
246
|
+
<Button
|
|
247
|
+
variant="outlined"
|
|
248
|
+
type="primary"
|
|
249
|
+
size="large"
|
|
250
|
+
onClick={onCloseModal}>
|
|
251
|
+
<Typography variant="title" size="medium" color="primary">
|
|
252
|
+
{i18n.t('No, Cancel')}
|
|
253
|
+
</Typography>
|
|
254
|
+
</Button>
|
|
255
|
+
</WatermarkedModal>
|
|
102
256
|
</Layout>
|
|
103
257
|
);
|
|
104
258
|
}
|
package/src/pages/Home.tsx
CHANGED
|
@@ -19,7 +19,9 @@ import { useAppStore } from '../store/AppStore';
|
|
|
19
19
|
import { useQuoteStore } from '../store/quote';
|
|
20
20
|
import { useUiStore } from '../store/ui';
|
|
21
21
|
import { useWalletsStore } from '../store/wallets';
|
|
22
|
+
import { UiEventTypes } from '../types';
|
|
22
23
|
import { isVariantExpandable } from '../utils/configs';
|
|
24
|
+
import { emitPreventableEvent } from '../utils/events';
|
|
23
25
|
import { getSwapButtonState, isTokensIdentical } from '../utils/swap';
|
|
24
26
|
|
|
25
27
|
const MainContainer = styled('div', {
|
|
@@ -115,14 +117,12 @@ export function Home() {
|
|
|
115
117
|
navigate(route);
|
|
116
118
|
}
|
|
117
119
|
};
|
|
118
|
-
|
|
119
120
|
const onClickOnQuote = (quote: SelectedQuote) => {
|
|
120
121
|
if (selectedQuote?.requestId !== quote.requestId) {
|
|
121
122
|
setShowQuoteWarningModal(false);
|
|
122
123
|
setSelectedQuote(quote);
|
|
123
124
|
}
|
|
124
125
|
};
|
|
125
|
-
|
|
126
126
|
return (
|
|
127
127
|
<MainContainer>
|
|
128
128
|
<Layout
|
|
@@ -138,7 +138,10 @@ export function Home() {
|
|
|
138
138
|
fullWidth
|
|
139
139
|
onClick={() => {
|
|
140
140
|
if (swapButtonState.action === 'connect-wallet') {
|
|
141
|
-
|
|
141
|
+
emitPreventableEvent(
|
|
142
|
+
{ type: UiEventTypes.CLICK_CONNECT_WALLET },
|
|
143
|
+
() => onHandleNavigation(navigationRoutes.wallets)
|
|
144
|
+
);
|
|
142
145
|
} else if (swapButtonState.action === 'confirm-warning') {
|
|
143
146
|
setShowQuoteWarningModal(true);
|
|
144
147
|
} else {
|
|
@@ -80,12 +80,14 @@ export function WalletsPage() {
|
|
|
80
80
|
setSelectedWalletType(type);
|
|
81
81
|
}, TIME_TO_IGNORE_MODAL);
|
|
82
82
|
},
|
|
83
|
-
onConnect: () => {
|
|
83
|
+
onConnect: (type) => {
|
|
84
84
|
if (modalTimerId) {
|
|
85
85
|
clearTimeout(modalTimerId);
|
|
86
86
|
}
|
|
87
87
|
setTimeout(() => {
|
|
88
|
-
|
|
88
|
+
if (selectedWalletType === type) {
|
|
89
|
+
setOpenModal(false);
|
|
90
|
+
}
|
|
89
91
|
}, TIME_TO_CLOSE_MODAL);
|
|
90
92
|
},
|
|
91
93
|
});
|
|
@@ -126,21 +128,35 @@ export function WalletsPage() {
|
|
|
126
128
|
|
|
127
129
|
const filteredWallets = filterWalletsByCategory(list, blockchainCategory);
|
|
128
130
|
|
|
129
|
-
const handleWalletItemClick = (
|
|
131
|
+
const handleWalletItemClick = (wallet: WalletInfoWithExtra) => {
|
|
130
132
|
if (isSingleWalletActive(list, config.multiWallets)) {
|
|
131
133
|
return;
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
if (
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
if (
|
|
137
|
+
!!wallet.namespaces?.length &&
|
|
138
|
+
wallet.state === WalletState.DISCONNECTED
|
|
139
|
+
) {
|
|
140
|
+
if (wallet.namespaces.length > 1) {
|
|
141
|
+
setNamespacesModalState({
|
|
142
|
+
open: true,
|
|
143
|
+
providerType: wallet.type,
|
|
144
|
+
providerImage: wallet.image,
|
|
145
|
+
availableNamespaces: wallet.namespaces,
|
|
146
|
+
singleNamespace: wallet.singleNamespace,
|
|
147
|
+
});
|
|
148
|
+
} else if (wallet.needsDerivationPath) {
|
|
149
|
+
setDerivationPathModalState({
|
|
150
|
+
open: true,
|
|
151
|
+
providerType: wallet.type,
|
|
152
|
+
providerImage: wallet.image,
|
|
153
|
+
namespace: wallet.namespaces[0],
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
void handleClick(wallet.type);
|
|
157
|
+
}
|
|
142
158
|
} else {
|
|
143
|
-
void handleClick(type);
|
|
159
|
+
void handleClick(wallet.type);
|
|
144
160
|
}
|
|
145
161
|
};
|
|
146
162
|
|
|
@@ -208,7 +224,7 @@ export function WalletsPage() {
|
|
|
208
224
|
key={key}
|
|
209
225
|
{...wallet}
|
|
210
226
|
container={getContainer()}
|
|
211
|
-
onClick={(
|
|
227
|
+
onClick={() => handleWalletItemClick(wallet)}
|
|
212
228
|
isLoading={fetchMetaStatus === 'loading'}
|
|
213
229
|
disabled={!isActiveTab}
|
|
214
230
|
/>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Token } from 'rango-sdk';
|
|
2
|
+
|
|
3
|
+
interface CacheServiceInterface<V> {
|
|
4
|
+
get<K extends keyof V>(key: K): V[K] | undefined;
|
|
5
|
+
set<K extends keyof V>(key: K, value: V[K]): void;
|
|
6
|
+
remove<K extends keyof V>(key: K): void;
|
|
7
|
+
clear(): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class CacheService<T> implements CacheServiceInterface<T> {
|
|
11
|
+
#cache: Map<string, T[keyof T]> = new Map();
|
|
12
|
+
|
|
13
|
+
get<K extends keyof T>(key: K): T[K] | undefined {
|
|
14
|
+
return this.#cache.get(key as string) as T[K];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
set<K extends keyof T>(key: K, value: T[K]): void {
|
|
18
|
+
this.#cache.set(key as string, value);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
remove<K extends keyof T>(key: K): void {
|
|
22
|
+
this.#cache.delete(key as string);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
clear(): void {
|
|
26
|
+
this.#cache.clear();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type CachedEntries = {
|
|
31
|
+
supportedSourceTokens: Token[];
|
|
32
|
+
supportedDestinationTokens: Token[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const cacheService = new CacheService<CachedEntries>();
|
|
@@ -36,6 +36,7 @@ export const useNotificationStore = createSelectors(
|
|
|
36
36
|
creationTime: Date.now(),
|
|
37
37
|
requestId: route.requestId,
|
|
38
38
|
route: {
|
|
39
|
+
creationTime: parseInt(route.creationTime),
|
|
39
40
|
from: {
|
|
40
41
|
blockchain: fromStep.fromBlockchain,
|
|
41
42
|
address: fromStep.fromSymbolAddress,
|
|
@@ -67,7 +68,18 @@ export const useNotificationStore = createSelectors(
|
|
|
67
68
|
getNotifications: () => {
|
|
68
69
|
const { isSynced, notifications } = get();
|
|
69
70
|
return isSynced
|
|
70
|
-
? notifications.sort((a, b) =>
|
|
71
|
+
? notifications.sort((a, b) => {
|
|
72
|
+
if (a.route.creationTime && !b.route.creationTime) {
|
|
73
|
+
return -1;
|
|
74
|
+
}
|
|
75
|
+
if (!a.route.creationTime && b.route.creationTime) {
|
|
76
|
+
return 1;
|
|
77
|
+
}
|
|
78
|
+
if (!a.route.creationTime && !b.route.creationTime) {
|
|
79
|
+
return b.creationTime - a.creationTime;
|
|
80
|
+
}
|
|
81
|
+
return b.route.creationTime - a.route.creationTime;
|
|
82
|
+
})
|
|
71
83
|
: [];
|
|
72
84
|
},
|
|
73
85
|
syncNotifications: (swaps) => {
|
package/src/store/quote.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import type { DataSlice } from './data';
|
|
1
2
|
import type { SettingsSlice } from './settings';
|
|
2
3
|
import type { WidgetConfig } from '../../types';
|
|
3
4
|
import type { StateCreatorWithInitialData } from '../app';
|
|
4
5
|
|
|
6
|
+
import { cacheService } from '../../services/cacheService';
|
|
7
|
+
import { matchTokensFromConfigWithMeta } from '../utils';
|
|
8
|
+
|
|
5
9
|
export const DEFAULT_CONFIG: WidgetConfig = {
|
|
6
10
|
apiKey: '',
|
|
7
11
|
title: undefined,
|
|
@@ -54,7 +58,7 @@ export interface ConfigSlice {
|
|
|
54
58
|
|
|
55
59
|
export const createConfigSlice: StateCreatorWithInitialData<
|
|
56
60
|
WidgetConfig,
|
|
57
|
-
ConfigSlice & SettingsSlice,
|
|
61
|
+
ConfigSlice & SettingsSlice & DataSlice,
|
|
58
62
|
ConfigSlice
|
|
59
63
|
> = (initialData, set, get) => {
|
|
60
64
|
return {
|
|
@@ -87,6 +91,40 @@ export const createConfigSlice: StateCreatorWithInitialData<
|
|
|
87
91
|
// Actions
|
|
88
92
|
updateConfig: (nextConfig: WidgetConfig) => {
|
|
89
93
|
const currentConfig = get().config;
|
|
94
|
+
const {
|
|
95
|
+
_tokensMapByTokenHash: tokensMapByTokenHash,
|
|
96
|
+
_tokensMapByBlockchainName: tokensMapByBlockchainName,
|
|
97
|
+
} = get();
|
|
98
|
+
|
|
99
|
+
const supportedSourceTokens = matchTokensFromConfigWithMeta({
|
|
100
|
+
type: 'source',
|
|
101
|
+
config: {
|
|
102
|
+
blockchains: nextConfig.from?.blockchains,
|
|
103
|
+
tokens: nextConfig.from?.tokens,
|
|
104
|
+
},
|
|
105
|
+
meta: {
|
|
106
|
+
tokensMapByBlockchainName,
|
|
107
|
+
tokensMapByTokenHash,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const supportedDestinationTokens = matchTokensFromConfigWithMeta({
|
|
112
|
+
type: 'destination',
|
|
113
|
+
config: {
|
|
114
|
+
blockchains: nextConfig.to?.blockchains,
|
|
115
|
+
tokens: nextConfig.to?.tokens,
|
|
116
|
+
},
|
|
117
|
+
meta: {
|
|
118
|
+
tokensMapByBlockchainName,
|
|
119
|
+
tokensMapByTokenHash,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
cacheService.set('supportedSourceTokens', supportedSourceTokens);
|
|
124
|
+
cacheService.set(
|
|
125
|
+
'supportedDestinationTokens',
|
|
126
|
+
supportedDestinationTokens
|
|
127
|
+
);
|
|
90
128
|
|
|
91
129
|
set({
|
|
92
130
|
config: {
|
|
@@ -3,6 +3,7 @@ import type { EvmBlockchainMeta, Token } from 'rango-sdk';
|
|
|
3
3
|
|
|
4
4
|
import { assert, beforeEach, describe, expect, test } from 'vitest';
|
|
5
5
|
|
|
6
|
+
import { cacheService } from '../../services/cacheService';
|
|
6
7
|
import {
|
|
7
8
|
createEvmBlockchain,
|
|
8
9
|
createInitialAppStore,
|
|
@@ -17,6 +18,7 @@ let customTokens: [Token, Token, Token];
|
|
|
17
18
|
let rangoBlockchain: EvmBlockchainMeta;
|
|
18
19
|
|
|
19
20
|
beforeEach(() => {
|
|
21
|
+
cacheService.clear();
|
|
20
22
|
rangoBlockchain = createEvmBlockchain();
|
|
21
23
|
|
|
22
24
|
customTokens = [
|
|
@@ -52,6 +54,10 @@ beforeEach(() => {
|
|
|
52
54
|
customTokens.forEach((token) => {
|
|
53
55
|
const tokenHash = createTokenHash(token);
|
|
54
56
|
initData._tokensMapByTokenHash.set(tokenHash, token);
|
|
57
|
+
if (!initData._tokensMapByBlockchainName[token.blockchain]) {
|
|
58
|
+
initData._tokensMapByBlockchainName[token.blockchain] = [];
|
|
59
|
+
}
|
|
60
|
+
initData._tokensMapByBlockchainName[token.blockchain].push(tokenHash);
|
|
55
61
|
});
|
|
56
62
|
|
|
57
63
|
const appStore = createAppStore();
|
|
@@ -351,3 +357,81 @@ describe('search in tokens', () => {
|
|
|
351
357
|
expect(secondResult).toBe('RNG');
|
|
352
358
|
});
|
|
353
359
|
});
|
|
360
|
+
|
|
361
|
+
describe('supported tokens from config', () => {
|
|
362
|
+
test('Should ensure tokens include only tokens from the config', () => {
|
|
363
|
+
const rangoToken = customTokens[0];
|
|
364
|
+
const djangoToken = customTokens[1];
|
|
365
|
+
|
|
366
|
+
const configTokens = [rangoToken, djangoToken];
|
|
367
|
+
|
|
368
|
+
appStoreState = updateAppStoreConfig(appStoreState, {
|
|
369
|
+
from: {
|
|
370
|
+
tokens: configTokens,
|
|
371
|
+
},
|
|
372
|
+
to: { tokens: configTokens },
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
let sourceTokens = appStoreState.tokens({
|
|
376
|
+
type: 'source',
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
let destinationTokens = appStoreState.tokens({
|
|
380
|
+
type: 'destination',
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
expect(configTokens).toMatchObject(sourceTokens);
|
|
384
|
+
expect(configTokens).toMatchObject(destinationTokens);
|
|
385
|
+
|
|
386
|
+
appStoreState = updateAppStoreConfig(appStoreState, {
|
|
387
|
+
from: {
|
|
388
|
+
blockchains: [rangoBlockchain.name],
|
|
389
|
+
tokens: {
|
|
390
|
+
[rangoBlockchain.name]: {
|
|
391
|
+
tokens: configTokens,
|
|
392
|
+
isExcluded: false,
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
to: {
|
|
397
|
+
blockchains: [rangoBlockchain.name],
|
|
398
|
+
tokens: {
|
|
399
|
+
[rangoBlockchain.name]: {
|
|
400
|
+
tokens: configTokens,
|
|
401
|
+
isExcluded: false,
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
sourceTokens = appStoreState.tokens({
|
|
408
|
+
type: 'source',
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
destinationTokens = appStoreState.tokens({
|
|
412
|
+
type: 'destination',
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
expect(sourceTokens).toMatchObject(sourceTokens);
|
|
416
|
+
expect(configTokens).toMatchObject(destinationTokens);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test('Check tokens calculation is caching', () => {
|
|
420
|
+
const rangoToken = customTokens[0];
|
|
421
|
+
const djangoToken = customTokens[1];
|
|
422
|
+
|
|
423
|
+
appStoreState = updateAppStoreConfig(appStoreState, {
|
|
424
|
+
from: {
|
|
425
|
+
tokens: [rangoToken, djangoToken],
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
expect(cacheService.get('supportedSourceTokens')?.length ?? 0).toBe(0);
|
|
430
|
+
|
|
431
|
+
appStoreState.tokens({
|
|
432
|
+
type: 'source',
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
expect(cacheService.get('supportedSourceTokens')?.length ?? 0).toBe(2);
|
|
436
|
+
});
|
|
437
|
+
});
|