@rango-dev/widget-embedded 0.28.3-next.4 → 0.28.3-next.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rango-dev/widget-embedded",
3
- "version": "0.28.3-next.4",
3
+ "version": "0.28.3-next.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "source": "./src/index.ts",
@@ -30,7 +30,7 @@
30
30
  "@rango-dev/queue-manager-rango-preset": "^0.33.3-next.0",
31
31
  "@rango-dev/queue-manager-react": "^0.26.0",
32
32
  "@rango-dev/signer-solana": "^0.28.1-next.2",
33
- "@rango-dev/ui": "^0.34.1-next.3",
33
+ "@rango-dev/ui": "^0.34.1-next.4",
34
34
  "@rango-dev/wallets-react": "^0.19.1-next.1",
35
35
  "@rango-dev/wallets-shared": "^0.33.1-next.1",
36
36
  "bignumber.js": "^9.1.1",
@@ -118,7 +118,7 @@ export const ConfirmButton = styled('div', {
118
118
 
119
119
  export const StyledTextField = styled(TextField, {
120
120
  backgroundColor: '$neutral100',
121
- padding: '$0 $15 $15 $15',
121
+ padding: '$15',
122
122
  });
123
123
 
124
124
  export const Wallets = styled('div', { overflow: 'visible', width: '100%' });
@@ -137,13 +137,6 @@ export const CustomDestinationButton = styled('div', {
137
137
  backgroundColor: '$$color',
138
138
  borderBottomRightRadius: '0',
139
139
  borderBottomLeftRadius: '0',
140
- '&:hover': {
141
- $$color: '$colors$secondary100',
142
- [`.${darkTheme} &`]: {
143
- $$color: '$colors$neutral100',
144
- },
145
- backgroundColor: '$$color',
146
- },
147
140
  '&:focus-visible': {
148
141
  $$background: '$colors$secondary100',
149
142
  [`.${darkTheme} &`]: {
@@ -1,6 +1,7 @@
1
1
  import type { PropTypes } from './ConfirmWalletsModal.types';
2
2
  import type { ConnectedWallet } from '../../store/wallets';
3
3
  import type { ConfirmSwapWarnings, Wallet } from '../../types';
4
+ import type { MouseEvent } from 'react';
4
5
 
5
6
  import { i18n } from '@lingui/core';
6
7
  import {
@@ -9,12 +10,15 @@ import {
9
10
  Button,
10
11
  ChevronDownIcon,
11
12
  ChevronLeftIcon,
13
+ CloseIcon,
12
14
  Divider,
15
+ IconButton,
13
16
  MessageBox,
17
+ PasteIcon,
14
18
  Typography,
15
19
  WalletIcon,
16
20
  } from '@rango-dev/ui';
17
- import React, { useEffect, useState } from 'react';
21
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
18
22
  import { useNavigate } from 'react-router-dom';
19
23
 
20
24
  import { WIDGET_UI_ID } from '../../constants';
@@ -23,7 +27,10 @@ import { getQuoteUpdateWarningMessage } from '../../constants/warnings';
23
27
  import { useAppStore } from '../../store/AppStore';
24
28
  import { useQuoteStore } from '../../store/quote';
25
29
  import { useWalletsStore } from '../../store/wallets';
26
- import { getBlockchainShortNameFor } from '../../utils/meta';
30
+ import {
31
+ getBlockchainDisplayNameFor,
32
+ getBlockchainShortNameFor,
33
+ } from '../../utils/meta';
27
34
  import { isConfirmSwapDisabled } from '../../utils/swap';
28
35
  import { getQuoteWallets } from '../../utils/wallets';
29
36
  import { WatermarkedModal } from '../common/WatermarkedModal';
@@ -52,8 +59,11 @@ const NUMBER_OF_WALLETS_TO_DISPLAY = 2;
52
59
  export function ConfirmWalletsModal(props: PropTypes) {
53
60
  //TODO: move component's logics to a custom hook
54
61
  const { open, onClose, onCancel, onCheckBalance, loading } = props;
62
+ const navigate = useNavigate();
55
63
  const config = useAppStore().config;
56
64
 
65
+ const customDestinationInputRef = useRef<HTMLInputElement | null>(null);
66
+
57
67
  const blockchains = useAppStore().blockchains();
58
68
  const {
59
69
  selectedQuote,
@@ -75,6 +85,8 @@ export function ConfirmWalletsModal(props: PropTypes) {
75
85
  !!customDestination
76
86
  );
77
87
 
88
+ const isFirefox = navigator?.userAgent.includes('Firefox');
89
+
78
90
  const quoteWallets = getQuoteWallets({
79
91
  filter: 'all',
80
92
  quote: selectedQuote,
@@ -93,16 +105,34 @@ export function ConfirmWalletsModal(props: PropTypes) {
93
105
  const isWalletRequiredFor = (blockchain: string) =>
94
106
  requiredWallets.includes(blockchain);
95
107
 
96
- const getInitialSelectableWallets = () =>
97
- connectedWallets.filter((connectedWallet) => {
98
- return (
99
- connectedWallet.selected && quoteWallets.includes(connectedWallet.chain)
100
- );
101
- });
108
+ const getInitialSelectableWallets = useCallback(
109
+ () =>
110
+ connectedWallets.filter((connectedWallet) => {
111
+ return (
112
+ connectedWallet.selected &&
113
+ quoteWallets.includes(connectedWallet.chain)
114
+ );
115
+ }),
116
+ [connectedWallets.length, quoteWallets.length]
117
+ );
102
118
 
103
119
  const [selectableWallets, setSelectableWallets] = useState<ConnectedWallet[]>(
104
120
  getInitialSelectableWallets()
105
121
  );
122
+ const [nextSelectedWallets, setNextSelectedWallets] = useState<
123
+ {
124
+ blockchain: string;
125
+ walletType: string;
126
+ }[]
127
+ >([]);
128
+
129
+ const addNextSelectedWallets = (blockchain: string, walletType: string) =>
130
+ setNextSelectedWallets((prevState) =>
131
+ prevState.concat({
132
+ blockchain,
133
+ walletType,
134
+ })
135
+ );
106
136
 
107
137
  const isInsufficientBalanceModalOpen = balanceWarnings.length > 0;
108
138
 
@@ -143,6 +173,39 @@ export function ConfirmWalletsModal(props: PropTypes) {
143
173
  });
144
174
  };
145
175
 
176
+ const handleClearCustomDestination = () => setCustomDestination('');
177
+
178
+ const handlePasteCustomDestination = async (
179
+ event: MouseEvent<HTMLButtonElement>
180
+ ) => {
181
+ event.preventDefault();
182
+ if (navigator.clipboard !== undefined) {
183
+ const pastedText = await navigator.clipboard.readText();
184
+ setCustomDestination(pastedText);
185
+ customDestinationInputRef?.current?.focus();
186
+ }
187
+ };
188
+
189
+ const handleCustomDestinationCollapsibleOpenChange = (checked: boolean) => {
190
+ if (!checked) {
191
+ resetCustomDestination();
192
+ } else {
193
+ if (!isWalletRequiredFor(lastStepToBlockchain?.name ?? '')) {
194
+ setSelectableWallets((selectableWallets) =>
195
+ selectableWallets.map((selectableWallet) => {
196
+ if (selectableWallet.chain === lastStepToBlockchain?.name) {
197
+ return {
198
+ ...selectableWallet,
199
+ selected: false,
200
+ };
201
+ }
202
+ return selectableWallet;
203
+ })
204
+ );
205
+ }
206
+ }
207
+ };
208
+
146
209
  const onChange = (wallet: Wallet) => {
147
210
  if (showMoreWalletFor) {
148
211
  setShowMoreWalletFor('');
@@ -162,7 +225,11 @@ export function ConfirmWalletsModal(props: PropTypes) {
162
225
  }
163
226
 
164
227
  onCancel();
165
- if (wallet.chain === lastStepToBlockchain?.name && showCustomDestination) {
228
+ if (
229
+ wallet.chain === lastStepToBlockchain?.name &&
230
+ showCustomDestination &&
231
+ !isWalletRequiredFor(lastStepToBlockchain.name)
232
+ ) {
166
233
  setShowCustomDestination(false);
167
234
  setCustomDestination(null);
168
235
  }
@@ -210,11 +277,49 @@ export function ConfirmWalletsModal(props: PropTypes) {
210
277
  }
211
278
  };
212
279
 
280
+ const renderCustomDestinationSuffix = () => {
281
+ if (customDestination) {
282
+ return (
283
+ <IconButton onClick={handleClearCustomDestination} variant="ghost">
284
+ <CloseIcon size={12} color="gray" />
285
+ </IconButton>
286
+ );
287
+ } else if (!isFirefox) {
288
+ return (
289
+ <IconButton onClick={handlePasteCustomDestination} variant="ghost">
290
+ <PasteIcon size={16} />
291
+ </IconButton>
292
+ );
293
+ }
294
+
295
+ return null;
296
+ };
297
+
213
298
  useEffect(() => {
214
- setSelectableWallets((selectableWallets) =>
215
- selectableWallets.concat(
299
+ setSelectableWallets((selectableWallets) => {
300
+ let nextState: typeof selectableWallets = [];
301
+
302
+ //if wallet disconnected we should unselect the wallet from the list
303
+ selectableWallets.forEach((selectableWallet) => {
304
+ const walletDisconnected = !connectedWallets.some(
305
+ (connectedWallet) =>
306
+ connectedWallet.chain === selectableWallet.chain &&
307
+ connectedWallet.walletType === selectableWallet.walletType &&
308
+ connectedWallet.address === selectableWallet.address
309
+ );
310
+ if (!walletDisconnected) {
311
+ nextState.push(selectableWallet);
312
+ }
313
+ });
314
+
315
+ /**
316
+ * We confirm if a newly connected wallet for a blockchain is marked as selected in the store,
317
+ * but there are no selected wallets for that blockchain in our list.
318
+ * If this condition is met, we include the wallet in our list as selected.
319
+ */
320
+ nextState = nextState.concat(
216
321
  connectedWallets.filter((connectedWallet) => {
217
- const anyWalletSelected = !!selectableWallets.find(
322
+ const anyWalletSelected = !!nextState.find(
218
323
  (selectableWallet) =>
219
324
  selectableWallet.chain === connectedWallet.chain
220
325
  );
@@ -225,16 +330,35 @@ export function ConfirmWalletsModal(props: PropTypes) {
225
330
  quoteWallets.includes(connectedWallet.chain)
226
331
  );
227
332
  })
228
- )
229
- );
230
- }, [connectedWallets.length]);
333
+ );
334
+ return nextState;
335
+ });
336
+ }, [connectedWallets.length, quoteWallets.length]);
337
+
338
+ useEffect(() => {
339
+ const nextState: typeof nextSelectedWallets = [];
340
+
341
+ if (nextSelectedWallets.length > 0) {
342
+ nextSelectedWallets.forEach((selectedWallet) => {
343
+ const wallet = connectedWallets.find(
344
+ (wallet) =>
345
+ wallet.chain === selectedWallet.blockchain &&
346
+ wallet.walletType === selectedWallet.walletType
347
+ );
348
+ if (wallet) {
349
+ onChange(wallet);
350
+ } else {
351
+ nextState.push(selectedWallet);
352
+ }
353
+ });
354
+ setNextSelectedWallets(nextState);
355
+ }
356
+ }, [connectedWallets.length, nextSelectedWallets.length]);
231
357
 
232
358
  const modalContainer = document.getElementById(
233
359
  WIDGET_UI_ID.SWAP_BOX_ID
234
360
  ) as HTMLDivElement;
235
361
 
236
- const navigate = useNavigate();
237
-
238
362
  return (
239
363
  <WatermarkedModal
240
364
  open={open}
@@ -319,7 +443,10 @@ export function ConfirmWalletsModal(props: PropTypes) {
319
443
  chain={showMoreWalletFor}
320
444
  isSelected={isSelected}
321
445
  selectWallet={onChange}
322
- onShowMore={setShowMoreWalletFor.bind(null, showMoreWalletFor)}
446
+ onShowMore={() => setShowMoreWalletFor(showMoreWalletFor)}
447
+ onConnect={(walletType) => {
448
+ addNextSelectedWallets(showMoreWalletFor, walletType);
449
+ }}
323
450
  />
324
451
  </div>
325
452
  </WalletsContainer>
@@ -380,38 +507,18 @@ export function ConfirmWalletsModal(props: PropTypes) {
380
507
  onShowMore={() =>
381
508
  setShowMoreWalletFor(blockchain?.name ?? '')
382
509
  }
510
+ onConnect={(walletType) => {
511
+ addNextSelectedWallets(requiredWallet, walletType);
512
+ }}
383
513
  />
384
514
  </ListContainer>
385
515
  {!isLastWallet && <Divider size={32} />}
386
516
  {isLastWallet && config?.customDestination && (
387
517
  <CustomDestination>
388
518
  <CustomCollapsible
389
- onOpenChange={(checked) => {
390
- if (!checked) {
391
- resetCustomDestination();
392
- } else {
393
- if (
394
- !isWalletRequiredFor(
395
- lastStepToBlockchain?.name ?? ''
396
- )
397
- ) {
398
- setSelectableWallets((selectableWallets) =>
399
- selectableWallets.map((selectableWallet) => {
400
- if (
401
- selectableWallet.chain ===
402
- lastStepToBlockchain?.name
403
- ) {
404
- return {
405
- ...selectableWallet,
406
- selected: false,
407
- };
408
- }
409
- return selectableWallet;
410
- })
411
- );
412
- }
413
- }
414
- }}
519
+ onOpenChange={
520
+ handleCustomDestinationCollapsibleOpenChange
521
+ }
415
522
  hasSelected
416
523
  open={showCustomDestination}
417
524
  triggerAnchor="top"
@@ -420,7 +527,14 @@ export function ConfirmWalletsModal(props: PropTypes) {
420
527
  <div className="button__content">
421
528
  <WalletIcon size={18} color="info" />
422
529
  <Divider size={4} direction="horizontal" />
423
- <Typography variant="label" size="medium">
530
+ <Typography
531
+ variant="label"
532
+ size="medium"
533
+ color={
534
+ showCustomDestination
535
+ ? '$neutral600'
536
+ : undefined
537
+ }>
424
538
  {i18n.t('Send to a different address')}
425
539
  </Typography>
426
540
  </div>
@@ -428,25 +542,34 @@ export function ConfirmWalletsModal(props: PropTypes) {
428
542
  orientation={
429
543
  showCustomDestination ? 'up' : 'down'
430
544
  }>
431
- <ChevronDownIcon size={10} color="gray" />
545
+ <ChevronDownIcon size={10} color="secondary" />
432
546
  </ExpandedIcon>
433
547
  </CustomDestinationButton>
434
548
  }
435
549
  onClickTrigger={() =>
436
550
  setShowCustomDestination((prev) => !prev)
437
551
  }>
438
- <Divider size={4} />
439
552
  <StyledTextField
440
- placeholder={i18n.t('Your destination address')}
553
+ ref={customDestinationInputRef}
554
+ style={{
555
+ padding: 0,
556
+ paddingRight: customDestination ? '8px' : '5px',
557
+ }}
558
+ autoFocus
559
+ placeholder={i18n.t(
560
+ 'Enter {blockchainName} address',
561
+ {
562
+ blockchainName: getBlockchainDisplayNameFor(
563
+ requiredWallet,
564
+ blockchains
565
+ ),
566
+ }
567
+ )}
441
568
  value={customDestination || ''}
569
+ suffix={renderCustomDestinationSuffix()}
442
570
  onChange={(e) => {
443
571
  const value = e.target.value;
444
- if (!value.length) {
445
- setShowCustomDestination(false);
446
- setCustomDestination(null);
447
- } else {
448
- setCustomDestination(value.length ? value : null);
449
- }
572
+ setCustomDestination(value);
450
573
  }}
451
574
  {...(!customDestination && { autoFocus: true })}
452
575
  />
@@ -50,7 +50,8 @@ interface WalletNamespacesModalState {
50
50
 
51
51
  const ACCOUNT_ADDRESS_MAX_CHARACTERS = 7;
52
52
  export function WalletList(props: PropTypes) {
53
- const { chain, isSelected, selectWallet, limit, onShowMore } = props;
53
+ const { chain, isSelected, selectWallet, limit, onShowMore, onConnect } =
54
+ props;
54
55
  const isActiveTab = useUiStore.use.isActiveTab();
55
56
 
56
57
  const connectedWallets = useWalletsStore.use.connectedWallets();
@@ -80,7 +81,8 @@ export function WalletList(props: PropTypes) {
80
81
  setOpenWalletStateModal(type);
81
82
  }, TIME_TO_IGNORE_MODAL);
82
83
  },
83
- onConnect: () => {
84
+ onConnect: (type) => {
85
+ onConnect?.(type);
84
86
  if (modalTimerId) {
85
87
  clearTimeout(modalTimerId);
86
88
  }
@@ -6,4 +6,5 @@ export type PropTypes = {
6
6
  selectWallet: (wallet: Wallet) => void;
7
7
  limit?: number;
8
8
  onShowMore: () => void;
9
+ onConnect?: (walletType: string) => void;
9
10
  };
@@ -12,9 +12,10 @@ import {
12
12
  CopyIcon,
13
13
  Divider,
14
14
  IconButton,
15
- LinkIcon,
16
15
  QuoteCost,
16
+ RangoExplorerIcon,
17
17
  StepDetails,
18
+ Tooltip,
18
19
  Typography,
19
20
  useCopyToClipboard,
20
21
  } from '@rango-dev/ui';
@@ -321,7 +322,12 @@ export function SwapDetails(props: SwapDetailsProps) {
321
322
  <StyledLink
322
323
  target="_blank"
323
324
  href={`${SCANNER_BASE_URL}/swap/${requestId}`}>
324
- <LinkIcon size={16} />
325
+ <Tooltip
326
+ container={getContainer()}
327
+ content={i18n.t('View on Rango Explorer')}
328
+ side="bottom">
329
+ <RangoExplorerIcon size={20} />
330
+ </Tooltip>
325
331
  </StyledLink>
326
332
  </div>
327
333
  </div>
@@ -65,10 +65,15 @@ export const useWalletsStore = createSelectors(
65
65
  .filter((wallet) => wallet.walletType !== accounts[0].walletType)
66
66
  .concat(
67
67
  accounts.map((account) => {
68
- const shouldMarkWalletAsSelected = !state.connectedWallets.find(
68
+ const shouldMarkWalletAsSelected = !state.connectedWallets.some(
69
69
  (connectedWallet) =>
70
70
  connectedWallet.chain === account.chain &&
71
- connectedWallet.selected
71
+ connectedWallet.selected &&
72
+ /**
73
+ * Sometimes, the connect function can be called multiple times for a particular wallet type when using the auto-connect feature.
74
+ * This check is there to make sure the chosen wallet doesn't end up unselected.
75
+ */
76
+ connectedWallet.walletType !== account.walletType
72
77
  );
73
78
  return {
74
79
  balances: [],