@daimo/pay 1.1.4 → 1.2.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 (82) hide show
  1. package/build/index.d.ts +37 -105
  2. package/build/package.json.js +3 -4
  3. package/build/package.json.js.map +1 -1
  4. package/build/src/assets/icons.js +2 -1
  5. package/build/src/assets/icons.js.map +1 -1
  6. package/build/src/components/Common/AmountInput/AmountInputField.js +59 -0
  7. package/build/src/components/Common/AmountInput/AmountInputField.js.map +1 -0
  8. package/build/src/components/Common/AmountInput/index.js +143 -0
  9. package/build/src/components/Common/AmountInput/index.js.map +1 -0
  10. package/build/src/components/Common/ChainSelectList/index.js +1 -1
  11. package/build/src/components/Common/Modal/index.js +9 -4
  12. package/build/src/components/Common/Modal/index.js.map +1 -1
  13. package/build/src/components/Common/Modal/styles.js +2 -1
  14. package/build/src/components/Common/Modal/styles.js.map +1 -1
  15. package/build/src/components/Common/OptionsList/styles.js +1 -1
  16. package/build/src/components/Common/OrderHeader/index.js +22 -111
  17. package/build/src/components/Common/OrderHeader/index.js.map +1 -1
  18. package/build/src/components/Common/PaymentBreakdown/index.js +48 -0
  19. package/build/src/components/{Pages/WaitingOther → Common/PaymentBreakdown}/index.js.map +1 -1
  20. package/build/src/components/Common/SwitchButton/index.js +39 -0
  21. package/build/src/components/Common/SwitchButton/index.js.map +1 -0
  22. package/build/src/components/Common/TokenChainLogo/index.js +24 -0
  23. package/build/src/components/Common/TokenChainLogo/index.js.map +1 -0
  24. package/build/src/components/DaimoPay.js +7 -2
  25. package/build/src/components/DaimoPay.js.map +1 -1
  26. package/build/src/components/DaimoPayButton/index.js +0 -1
  27. package/build/src/components/DaimoPayButton/index.js.map +1 -1
  28. package/build/src/components/DaimoPayModal/index.js +86 -26
  29. package/build/src/components/DaimoPayModal/index.js.map +1 -1
  30. package/build/src/components/Pages/PayWithToken/index.js +4 -109
  31. package/build/src/components/Pages/PayWithToken/index.js.map +1 -1
  32. package/build/src/components/Pages/SelectAmount/index.js +16 -0
  33. package/build/src/components/Pages/SelectAmount/index.js.map +1 -0
  34. package/build/src/components/Pages/SelectDepositAddressAmount/index.js +60 -0
  35. package/build/src/components/Pages/SelectDepositAddressAmount/index.js.map +1 -0
  36. package/build/src/components/Pages/SelectDepositAddressChain/index.js +7 -2
  37. package/build/src/components/Pages/SelectDepositAddressChain/index.js.map +1 -1
  38. package/build/src/components/Pages/SelectExternalAmount/index.js +62 -0
  39. package/build/src/components/Pages/SelectExternalAmount/index.js.map +1 -0
  40. package/build/src/components/Pages/SelectMethod/index.js +14 -7
  41. package/build/src/components/Pages/SelectMethod/index.js.map +1 -1
  42. package/build/src/components/Pages/SelectToken/index.js +36 -41
  43. package/build/src/components/Pages/SelectToken/index.js.map +1 -1
  44. package/build/src/components/Pages/Solana/ConnectorSolana/index.js +8 -2
  45. package/build/src/components/Pages/Solana/ConnectorSolana/index.js.map +1 -1
  46. package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js +14 -8
  47. package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js.map +1 -1
  48. package/build/src/components/Pages/Solana/SelectSolanaAmount/index.js +16 -0
  49. package/build/src/components/Pages/Solana/SelectSolanaAmount/index.js.map +1 -0
  50. package/build/src/components/Pages/Solana/SelectSolanaToken/index.js +37 -21
  51. package/build/src/components/Pages/Solana/SelectSolanaToken/index.js.map +1 -1
  52. package/build/src/components/Pages/WaitingExternal/index.js +53 -0
  53. package/build/src/components/Pages/WaitingExternal/index.js.map +1 -0
  54. package/build/src/components/Spinners/ExternalPaymentSpinner/index.js +20 -0
  55. package/build/src/components/Spinners/ExternalPaymentSpinner/index.js.map +1 -0
  56. package/build/src/components/Spinners/TokenLogoSpinner/index.js +13 -0
  57. package/build/src/components/Spinners/TokenLogoSpinner/index.js.map +1 -0
  58. package/build/src/components/Spinners/TokenLogoSpinner/styles.js +40 -0
  59. package/build/src/components/Spinners/TokenLogoSpinner/styles.js.map +1 -0
  60. package/build/src/components/Spinners/styles.js +32 -0
  61. package/build/src/components/Spinners/styles.js.map +1 -0
  62. package/build/src/hooks/useDepositAddressOptions.js +8 -5
  63. package/build/src/hooks/useDepositAddressOptions.js.map +1 -1
  64. package/build/src/hooks/useExternalPaymentOptions.js +7 -6
  65. package/build/src/hooks/useExternalPaymentOptions.js.map +1 -1
  66. package/build/src/hooks/useOrderUsdLimits.js +26 -0
  67. package/build/src/hooks/useOrderUsdLimits.js.map +1 -0
  68. package/build/src/hooks/usePaymentState.js +46 -14
  69. package/build/src/hooks/usePaymentState.js.map +1 -1
  70. package/build/src/hooks/useSolanaPaymentOptions.js +7 -5
  71. package/build/src/hooks/useSolanaPaymentOptions.js.map +1 -1
  72. package/build/src/hooks/useWalletPaymentOptions.js +9 -8
  73. package/build/src/hooks/useWalletPaymentOptions.js.map +1 -1
  74. package/build/src/index.js +1 -1
  75. package/build/src/utils/exports.js +4 -1
  76. package/build/src/utils/exports.js.map +1 -1
  77. package/build/src/utils/format.js +83 -0
  78. package/build/src/utils/format.js.map +1 -0
  79. package/build/src/utils/validateInput.js +36 -0
  80. package/build/src/utils/validateInput.js.map +1 -0
  81. package/package.json +3 -4
  82. package/build/src/components/Pages/WaitingOther/index.js +0 -91
@@ -0,0 +1,16 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import MultiCurrencySelectAmount from '../../Common/AmountInput/index.js';
3
+ import { PageContent } from '../../Common/Modal/styles.js';
4
+ import { usePayContext, ROUTES } from '../../DaimoPay.js';
5
+
6
+ const SelectAmount = () => {
7
+ const { paymentState } = usePayContext();
8
+ const { selectedTokenOption, setSelectedTokenOption } = paymentState;
9
+ if (selectedTokenOption == null) {
10
+ return jsx(PageContent, {});
11
+ }
12
+ return (jsx(MultiCurrencySelectAmount, { selectedTokenOption: selectedTokenOption, setSelectedTokenOption: setSelectedTokenOption, nextPage: ROUTES.PAY_WITH_TOKEN }));
13
+ };
14
+
15
+ export { SelectAmount as default };
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
@@ -0,0 +1,60 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useState, useEffect } from 'react';
3
+ import { usePayContext, ROUTES } from '../../DaimoPay.js';
4
+ import { PageContent, ModalContent, ModalBody } from '../../Common/Modal/styles.js';
5
+ import styled from '../../../styles/styled/index.js';
6
+ import { formatUsd, USD_DECIMALS } from '../../../utils/format.js';
7
+ import { isValidNumber, sanitizeNumber } from '../../../utils/validateInput.js';
8
+ import AmountInputField from '../../Common/AmountInput/AmountInputField.js';
9
+ import Button from '../../Common/Button/index.js';
10
+ import ExternalPaymentSpinner from '../../Spinners/ExternalPaymentSpinner/index.js';
11
+
12
+ // TODO: min amount for deposit address should come from the backend
13
+ const MIN_USD_VALUE = 20;
14
+ const SelectDepositAddressAmount = () => {
15
+ const { paymentState, setRoute, triggerResize } = usePayContext();
16
+ const { selectedDepositAddressOption } = paymentState;
17
+ const maxUsdLimit = paymentState.getOrderUsdLimit();
18
+ const minimumMessage = `Minimum ${formatUsd(MIN_USD_VALUE, "up")}`;
19
+ const [usdInput, setUsdInput] = useState("");
20
+ const [message, setMessage] = useState(minimumMessage);
21
+ const [continueDisabled, setContinueDisabled] = useState(true);
22
+ useEffect(() => {
23
+ triggerResize();
24
+ }, [message]);
25
+ if (selectedDepositAddressOption == null) {
26
+ return jsx(PageContent, {});
27
+ }
28
+ const handleAmountChange = (e) => {
29
+ const value = e.target.value;
30
+ if (value !== "" && !isValidNumber(value, USD_DECIMALS))
31
+ return;
32
+ setUsdInput(value);
33
+ if (Number(value) > maxUsdLimit) {
34
+ setMessage(`Maximum ${formatUsd(maxUsdLimit)}`);
35
+ }
36
+ else {
37
+ setMessage(minimumMessage);
38
+ }
39
+ const usd = Number(sanitizeNumber(value));
40
+ setContinueDisabled(usd <= 0 || usd < MIN_USD_VALUE || usd > maxUsdLimit);
41
+ };
42
+ const handleKeyDown = (e) => {
43
+ if (e.key === "Enter" && !continueDisabled) {
44
+ handleContinue();
45
+ }
46
+ };
47
+ const handleContinue = () => {
48
+ paymentState.setChosenUsd(Number(sanitizeNumber(usdInput)));
49
+ setRoute(ROUTES.WAITING_DEPOSIT_ADDRESS);
50
+ };
51
+ return (jsxs(PageContent, { children: [jsx(ExternalPaymentSpinner, { logoURI: selectedDepositAddressOption.logoURI, logoShape: "circle", showSpinner: false }), jsxs(ModalContent, { "$preserveDisplay": true, children: [jsx(AmountInputContainer, { children: jsx(AmountInputField, { value: usdInput, onChange: handleAmountChange, onKeyDown: handleKeyDown }) }), message && jsx(ModalBody, { children: message }), jsx(Button, { onClick: handleContinue, disabled: continueDisabled, children: "Continue" })] })] }));
52
+ };
53
+ const AmountInputContainer = styled.div `
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ `;
58
+
59
+ export { SelectDepositAddressAmount as default };
60
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -7,7 +7,7 @@ import { OrderHeader } from '../../Common/OrderHeader/index.js';
7
7
 
8
8
  const SelectDepositAddressChain = () => {
9
9
  const { setRoute, paymentState } = usePayContext();
10
- const { setSelectedDepositAddressOption, depositAddressOptions } = paymentState;
10
+ const { isDepositFlow, setSelectedDepositAddressOption, depositAddressOptions, } = paymentState;
11
11
  return (jsxs(PageContent, { children: [jsx(OrderHeader, { minified: true }), !depositAddressOptions.loading &&
12
12
  depositAddressOptions.options?.length === 0 && (jsxs(ModalContent, { style: {
13
13
  display: "flex",
@@ -22,7 +22,12 @@ const SelectDepositAddressChain = () => {
22
22
  icons: [option.logoURI],
23
23
  onClick: () => {
24
24
  setSelectedDepositAddressOption(option);
25
- setRoute(ROUTES.WAITING_DEPOSIT_ADDRESS);
25
+ if (isDepositFlow) {
26
+ setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_AMOUNT);
27
+ }
28
+ else {
29
+ setRoute(ROUTES.WAITING_DEPOSIT_ADDRESS);
30
+ }
26
31
  },
27
32
  };
28
33
  }) ?? [] })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,62 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useState, useEffect } from 'react';
3
+ import { usePayContext, ROUTES } from '../../DaimoPay.js';
4
+ import { PageContent, ModalContent, ModalBody } from '../../Common/Modal/styles.js';
5
+ import styled from '../../../styles/styled/index.js';
6
+ import { formatUsd, USD_DECIMALS } from '../../../utils/format.js';
7
+ import { isValidNumber, sanitizeNumber } from '../../../utils/validateInput.js';
8
+ import AmountInputField from '../../Common/AmountInput/AmountInputField.js';
9
+ import Button from '../../Common/Button/index.js';
10
+ import ExternalPaymentSpinner from '../../Spinners/ExternalPaymentSpinner/index.js';
11
+
12
+ const SelectExternalAmount = () => {
13
+ const { paymentState, setRoute, triggerResize } = usePayContext();
14
+ const { selectedExternalOption } = paymentState;
15
+ const maxUsdLimit = paymentState.getOrderUsdLimit();
16
+ const minimumMessage = (selectedExternalOption?.minimumUsd ?? 0) > 0
17
+ ? `Minimum ${formatUsd(selectedExternalOption?.minimumUsd ?? 0, "up")}`
18
+ : null;
19
+ const [usdInput, setUsdInput] = useState("");
20
+ const [message, setMessage] = useState(minimumMessage);
21
+ const [continueDisabled, setContinueDisabled] = useState(true);
22
+ useEffect(() => {
23
+ triggerResize();
24
+ }, [message]);
25
+ if (selectedExternalOption == null) {
26
+ return jsx(PageContent, {});
27
+ }
28
+ const handleAmountChange = (e) => {
29
+ const value = e.target.value;
30
+ if (value !== "" && !isValidNumber(value, USD_DECIMALS))
31
+ return;
32
+ setUsdInput(value);
33
+ if (Number(value) > maxUsdLimit) {
34
+ setMessage(`Maximum ${formatUsd(maxUsdLimit)}`);
35
+ }
36
+ else {
37
+ setMessage(minimumMessage);
38
+ }
39
+ const usd = Number(sanitizeNumber(value));
40
+ setContinueDisabled(usd <= 0 ||
41
+ usd < (selectedExternalOption.minimumUsd ?? 0) ||
42
+ usd > maxUsdLimit);
43
+ };
44
+ const handleKeyDown = (e) => {
45
+ if (e.key === "Enter" && !continueDisabled) {
46
+ handleContinue();
47
+ }
48
+ };
49
+ const handleContinue = () => {
50
+ paymentState.setChosenUsd(Number(sanitizeNumber(usdInput)));
51
+ setRoute(ROUTES.WAITING_EXTERNAL);
52
+ };
53
+ return (jsxs(PageContent, { children: [jsx(ExternalPaymentSpinner, { logoURI: selectedExternalOption.logoURI, logoShape: selectedExternalOption.logoShape, showSpinner: false }), jsxs(ModalContent, { "$preserveDisplay": true, children: [jsx(AmountInputContainer, { children: jsx(AmountInputField, { value: usdInput, onChange: handleAmountChange, onKeyDown: handleKeyDown }) }), message && jsx(ModalBody, { children: message }), jsx(Button, { onClick: handleContinue, disabled: continueDisabled, children: "Continue" })] })] }));
54
+ };
55
+ const AmountInputContainer = styled.div `
56
+ display: flex;
57
+ align-items: center;
58
+ justify-content: center;
59
+ `;
60
+
61
+ export { SelectExternalAmount as default };
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -45,19 +45,21 @@ function getSolanaOption() {
45
45
  };
46
46
  }
47
47
  function getDepositAddressOption(depositAddressOptions) {
48
- const { setRoute } = usePayContext();
49
- if (!depositAddressOptions.loading &&
50
- depositAddressOptions.options.length === 0) {
51
- return null;
52
- }
48
+ const { setRoute, paymentState } = usePayContext();
49
+ // TODO: API should serve the subtitle and disabled
50
+ const disabled = !paymentState.isDepositFlow &&
51
+ !depositAddressOptions.loading &&
52
+ depositAddressOptions.options.length === 0;
53
+ const subtitle = disabled ? "Minimum $20.00" : "Bitcoin, Tron, Zcash...";
53
54
  return {
54
55
  id: "depositAddress",
55
56
  title: "Pay on another chain",
56
- subtitle: "Bitcoin, Tron, Zcash...",
57
+ subtitle,
57
58
  icons: [jsx(Bitcoin, {}), jsx(Tron, {}), jsx(Zcash, {})],
58
59
  onClick: () => {
59
60
  setRoute(ROUTES.SELECT_DEPOSIT_ADDRESS_CHAIN);
60
61
  },
62
+ disabled,
61
63
  };
62
64
  }
63
65
  const SelectMethod = () => {
@@ -101,7 +103,12 @@ const SelectMethod = () => {
101
103
  icons: [option.logoURI],
102
104
  onClick: () => {
103
105
  setSelectedExternalOption(option);
104
- setRoute(ROUTES.WAITING_OTHER);
106
+ if (paymentState.isDepositFlow) {
107
+ setRoute(ROUTES.SELECT_EXTERNAL_AMOUNT);
108
+ }
109
+ else {
110
+ setRoute(ROUTES.WAITING_EXTERNAL);
111
+ }
105
112
  },
106
113
  disabled: option.disabled,
107
114
  subtitle: option.message,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,61 +1,56 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { usePayContext, ROUTES } from '../../DaimoPay.js';
3
3
  import { PageContent, ModalContent, ModalH1 } from '../../Common/Modal/styles.js';
4
- import { capitalize, getDisplayPrice } from '@daimo/common';
4
+ import { capitalize } from '@daimo/common';
5
5
  import { getChainName } from '@daimo/contract';
6
- import { motion } from 'framer-motion';
7
- import { chainToLogo } from '../../../assets/chains.js';
8
- import styled from '../../../styles/styled/index.js';
6
+ import { formatUsd, roundTokenAmount } from '../../../utils/format.js';
9
7
  import Button from '../../Common/Button/index.js';
10
8
  import OptionsList from '../../Common/OptionsList/index.js';
11
9
  import { OrderHeader } from '../../Common/OrderHeader/index.js';
10
+ import TokenChainLogo from '../../Common/TokenChainLogo/index.js';
12
11
 
13
12
  function getDaimoTokenKey(token) {
14
13
  return `${token.chainId}-${token.token}`;
15
14
  }
16
- const TokenChainLogo = ({ token }) => {
17
- return (jsxs(TokenChainContainer, { children: [jsx("img", { src: token.logoURI, alt: token.symbol, style: { borderRadius: 9999 } }), jsx(ChainContainer, { children: chainToLogo[token.chainId] })] }));
18
- };
19
- const TokenChainContainer = styled(motion.div) `
20
- width: 100%;
21
- height: 100%;
22
- `;
23
- const ChainContainer = styled(motion.div) `
24
- position: absolute;
25
- width: 16px;
26
- height: 16px;
27
- border-radius: 9999px;
28
- overflow: hidden;
29
- bottom: 0px;
30
- right: 0px;
31
- `;
32
15
  const SelectToken = () => {
33
16
  const { setRoute, paymentState } = usePayContext();
34
- const { setSelectedTokenOption, walletPaymentOptions } = paymentState;
35
- return (jsxs(PageContent, { children: [jsx(OrderHeader, { minified: true }), !walletPaymentOptions.isLoading &&
36
- walletPaymentOptions.options?.length === 0 && (jsxs(ModalContent, { style: {
17
+ const { isDepositFlow, walletPaymentOptions, setSelectedTokenOption } = paymentState;
18
+ const optionsList = walletPaymentOptions.options?.map((option) => {
19
+ const capitalizedChainName = capitalize(getChainName(option.balance.token.chainId));
20
+ const titlePrice = isDepositFlow
21
+ ? formatUsd(option.balance.usd)
22
+ : roundTokenAmount(option.required.amount, option.required.token);
23
+ const title = `${titlePrice} ${option.balance.token.symbol} on ${capitalizedChainName}`;
24
+ const balanceStr = `${roundTokenAmount(option.balance.amount, option.balance.token)} ${option.balance.token.symbol}`;
25
+ const subtitle = option.disabledReason ??
26
+ `${isDepositFlow ? "" : "Balance: "}${balanceStr}`;
27
+ const disabled = option.disabledReason != null;
28
+ return {
29
+ id: getDaimoTokenKey(option.balance.token),
30
+ title,
31
+ subtitle,
32
+ icons: [
33
+ jsx(TokenChainLogo, { token: option.balance.token }, getDaimoTokenKey(option.balance.token)),
34
+ ],
35
+ onClick: () => {
36
+ setSelectedTokenOption(option);
37
+ if (isDepositFlow) {
38
+ setRoute(ROUTES.SELECT_AMOUNT);
39
+ }
40
+ else {
41
+ setRoute(ROUTES.PAY_WITH_TOKEN);
42
+ }
43
+ },
44
+ disabled,
45
+ };
46
+ }) ?? [];
47
+ return (jsxs(PageContent, { children: [jsx(OrderHeader, { minified: true }), !walletPaymentOptions.isLoading && optionsList.length === 0 && (jsxs(ModalContent, { style: {
37
48
  display: "flex",
38
49
  alignItems: "center",
39
50
  justifyContent: "center",
40
51
  paddingTop: 16,
41
52
  paddingBottom: 16,
42
- }, children: [jsx(ModalH1, { children: "Insufficient balance." }), jsx(Button, { onClick: () => setRoute(ROUTES.SELECT_METHOD), children: "Select Another Method" })] })), jsx(OptionsList, { requiredSkeletons: 4, isLoading: walletPaymentOptions.isLoading, options: walletPaymentOptions.options?.map((option) => {
43
- const capitalizedChainName = capitalize(getChainName(option.required.token.chainId));
44
- const title = `${getDisplayPrice(option.required)} ${option.required.token.symbol} on ${capitalizedChainName}`;
45
- const subtitle = `Balance: ${getDisplayPrice(option.balance)} ${option.balance.token.symbol}`;
46
- return {
47
- id: getDaimoTokenKey(option.required.token),
48
- title,
49
- subtitle,
50
- icons: [
51
- jsx(TokenChainLogo, { token: option.required.token }, getDaimoTokenKey(option.required.token)),
52
- ],
53
- onClick: () => {
54
- setSelectedTokenOption(option);
55
- setRoute(ROUTES.PAY_WITH_TOKEN);
56
- },
57
- };
58
- }) ?? [] })] }));
53
+ }, children: [jsx(ModalH1, { children: "Insufficient balance." }), jsx(Button, { onClick: () => setRoute(ROUTES.SELECT_METHOD), children: "Select Another Method" })] })), jsx(OptionsList, { requiredSkeletons: 4, isLoading: walletPaymentOptions.isLoading, options: optionsList })] }));
59
54
  };
60
55
 
61
56
  export { SelectToken as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -6,7 +6,6 @@ import { motion, AnimatePresence } from 'framer-motion';
6
6
  import styled from '../../../../styles/styled/index.js';
7
7
  import { usePayContext, ROUTES } from '../../../DaimoPay.js';
8
8
  import SquircleSpinner from '../../../Spinners/SquircleSpinner/index.js';
9
- import { LoadingContainer } from '../../WaitingOther/index.js';
10
9
 
11
10
  const ConnectSolana = () => {
12
11
  const solanaWallets = useWallet();
@@ -28,6 +27,13 @@ const ConnectSolana = () => {
28
27
  return null;
29
28
  return (jsxs(PageContent, { children: [jsx(LoadingContainer, { children: jsx(AnimationContainer, { children: jsx(AnimatePresence, { children: jsx(SquircleSpinner, { logo: jsx("div", { style: { borderRadius: "22.5%", overflow: "hidden" }, children: jsx("img", { src: selectedWallet?.adapter.icon, alt: selectedWallet?.adapter.name }) }), loading: true }) }) }) }), jsx(ModalContent, { style: { paddingBottom: 0 }, children: isConnected ? (jsx(ModalH1, { children: "Connected" })) : (jsxs(Fragment, { children: [jsx(ModalH1, { children: "Requesting Connection" }), jsxs(ModalBody, { children: ["Open ", selectedWallet?.adapter.name, " to continue."] })] })) })] }));
30
29
  };
30
+ const LoadingContainer = styled(motion.div) `
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ margin: 10px auto 16px;
35
+ height: 120px;
36
+ `;
31
37
  const AnimationContainer = styled(motion.div) `
32
38
  user-select: none;
33
39
  position: relative;
@@ -41,5 +47,5 @@ const AnimationContainer = styled(motion.div) `
41
47
  }
42
48
  `;
43
49
 
44
- export { ConnectSolana as default };
50
+ export { LoadingContainer, ConnectSolana as default };
45
51
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,14 +1,15 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { useState, useEffect } from 'react';
3
3
  import { usePayContext, ROUTES } from '../../../DaimoPay.js';
4
- import { WalletSignTransactionError } from '@solana/wallet-adapter-base';
4
+ import { WalletSignTransactionError, WalletSendTransactionError } from '@solana/wallet-adapter-base';
5
5
  import { PageContent, ModalContent, ModalH1 } from '../../../Common/Modal/styles.js';
6
6
  import { assert } from '@daimo/common';
7
- import { motion, AnimatePresence } from 'framer-motion';
7
+ import { motion } from 'framer-motion';
8
8
  import { css } from 'styled-components';
9
9
  import styled from '../../../../styles/styled/index.js';
10
10
  import Button from '../../../Common/Button/index.js';
11
- import CircleSpinner from '../../../Spinners/CircleSpinner/index.js';
11
+ import PaymentBreakdown from '../../../Common/PaymentBreakdown/index.js';
12
+ import TokenLogoSpinner from '../../../Spinners/TokenLogoSpinner/index.js';
12
13
 
13
14
  var PayState;
14
15
  (function (PayState) {
@@ -19,7 +20,7 @@ var PayState;
19
20
  })(PayState || (PayState = {}));
20
21
  const PayWithSolanaToken = () => {
21
22
  const { triggerResize, paymentState, setRoute } = usePayContext();
22
- const { selectedSolanaTokenOption, payWithSolanaToken } = paymentState;
23
+ const { payParams, generatePreviewOrder, selectedSolanaTokenOption, payWithSolanaToken, } = paymentState;
23
24
  const [payState, setPayState] = useState(PayState.RequestingPayment);
24
25
  const handleTransfer = async () => {
25
26
  try {
@@ -33,7 +34,8 @@ const PayWithSolanaToken = () => {
33
34
  }
34
35
  catch (error) {
35
36
  console.error(error);
36
- if (error instanceof WalletSignTransactionError) {
37
+ if (error instanceof WalletSignTransactionError ||
38
+ error instanceof WalletSendTransactionError) {
37
39
  setPayState(PayState.RequestCancelled);
38
40
  }
39
41
  else {
@@ -52,16 +54,20 @@ const PayWithSolanaToken = () => {
52
54
  useEffect(() => {
53
55
  triggerResize();
54
56
  }, [payState]);
55
- return (jsxs(PageContent, { children: [jsx(LoadingContainer, { children: jsx(AnimationContainer, { "$circle": true, children: jsx(AnimatePresence, { children: jsx(CircleSpinner, { logo: jsx("img", { src: selectedSolanaTokenOption?.required.token.logoURI, alt: selectedSolanaTokenOption?.required.token.symbol }, selectedSolanaTokenOption?.required.token.logoURI), loading: true, unavailable: false }, "CircleSpinner") }) }) }), jsxs(ModalContent, { style: { paddingBottom: 0 }, children: [jsx(ModalH1, { children: payState }), payState === PayState.RequestCancelled && (jsx(Button, { onClick: handleTransfer, children: "Retry Payment" })), payState === PayState.RequestFailed && (jsx(Button, { onClick: () => setRoute(ROUTES.SELECT_METHOD), children: "Select Another Method" }))] })] }));
57
+ return (jsxs(PageContent, { children: [selectedSolanaTokenOption && (jsx(TokenLogoSpinner, { token: selectedSolanaTokenOption.required.token })), jsxs(ModalContent, { style: { paddingBottom: 0 }, children: [jsx(ModalH1, { children: payState }), selectedSolanaTokenOption && (jsx(PaymentBreakdown, { paymentOption: selectedSolanaTokenOption })), payState === PayState.RequestCancelled && (jsx(Button, { onClick: handleTransfer, children: "Retry Payment" })), payState === PayState.RequestFailed && (jsx(Button, { onClick: () => {
58
+ assert(payParams != null, "payParams cannot be null in deposit flow");
59
+ generatePreviewOrder(payParams);
60
+ setRoute(ROUTES.SELECT_METHOD);
61
+ }, children: "Select Another Method" }))] })] }));
56
62
  };
57
- const LoadingContainer = styled(motion.div) `
63
+ styled(motion.div) `
58
64
  display: flex;
59
65
  align-items: center;
60
66
  justify-content: center;
61
67
  margin: 10px auto 16px;
62
68
  height: 120px;
63
69
  `;
64
- const AnimationContainer = styled(motion.div) `
70
+ styled(motion.div) `
65
71
  user-select: none;
66
72
  position: relative;
67
73
  --spinner-error-opacity: 0;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,16 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { usePayContext, ROUTES } from '../../../DaimoPay.js';
3
+ import MultiCurrencySelectAmount from '../../../Common/AmountInput/index.js';
4
+ import { PageContent } from '../../../Common/Modal/styles.js';
5
+
6
+ const SelectSolanaAmount = () => {
7
+ const { paymentState } = usePayContext();
8
+ const { selectedSolanaTokenOption, setSelectedSolanaTokenOption } = paymentState;
9
+ if (selectedSolanaTokenOption == null) {
10
+ return jsx(PageContent, {});
11
+ }
12
+ return (jsx(MultiCurrencySelectAmount, { selectedTokenOption: selectedSolanaTokenOption, setSelectedTokenOption: setSelectedSolanaTokenOption, nextPage: ROUTES.SOLANA_PAY_WITH_TOKEN }));
13
+ };
14
+
15
+ export { SelectSolanaAmount as default };
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
@@ -1,37 +1,53 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { usePayContext, ROUTES } from '../../../DaimoPay.js';
3
3
  import { PageContent, ModalContent, ModalH1 } from '../../../Common/Modal/styles.js';
4
- import { getDisplayPrice } from '@daimo/common';
4
+ import { formatUsd, roundTokenAmount } from '../../../../utils/format.js';
5
5
  import Button from '../../../Common/Button/index.js';
6
6
  import OptionsList from '../../../Common/OptionsList/index.js';
7
7
  import { OrderHeader } from '../../../Common/OrderHeader/index.js';
8
+ import TokenChainLogo from '../../../Common/TokenChainLogo/index.js';
8
9
 
10
+ function getDaimoSolanaTokenKey(token) {
11
+ return `${token.chainId}-${token.token}`;
12
+ }
9
13
  const SelectSolanaToken = () => {
10
14
  const { paymentState, setRoute } = usePayContext();
11
- const { solanaPaymentOptions, setSelectedSolanaTokenOption } = paymentState;
12
- return (jsxs(PageContent, { children: [jsx(OrderHeader, { minified: true }), !solanaPaymentOptions.isLoading &&
13
- solanaPaymentOptions.options?.length === 0 && (jsxs(ModalContent, { style: {
15
+ const { isDepositFlow, solanaPaymentOptions, setSelectedSolanaTokenOption } = paymentState;
16
+ const optionsList = solanaPaymentOptions.options?.map((option) => {
17
+ const titlePrice = isDepositFlow
18
+ ? formatUsd(option.balance.usd)
19
+ : roundTokenAmount(option.required.amount, option.required.token);
20
+ const title = `${titlePrice} ${option.balance.token.symbol} on Solana`;
21
+ const balanceStr = `${roundTokenAmount(option.balance.amount, option.balance.token)} ${option.balance.token.symbol}`;
22
+ const subtitle = option.disabledReason ??
23
+ `${isDepositFlow ? "" : "Balance: "}${balanceStr}`;
24
+ const disabled = option.disabledReason != null;
25
+ return {
26
+ id: getDaimoSolanaTokenKey(option.balance.token),
27
+ title,
28
+ subtitle,
29
+ icons: [
30
+ jsx(TokenChainLogo, { token: option.balance.token }, getDaimoSolanaTokenKey(option.balance.token)),
31
+ ],
32
+ onClick: () => {
33
+ setSelectedSolanaTokenOption(option);
34
+ if (isDepositFlow) {
35
+ setRoute(ROUTES.SOLANA_SELECT_AMOUNT);
36
+ }
37
+ else {
38
+ setRoute(ROUTES.SOLANA_PAY_WITH_TOKEN);
39
+ }
40
+ },
41
+ disabled,
42
+ };
43
+ }) ?? [];
44
+ return (jsxs(PageContent, { children: [jsx(OrderHeader, { minified: true }), !solanaPaymentOptions.isLoading && optionsList.length === 0 && (jsxs(ModalContent, { style: {
14
45
  display: "flex",
15
46
  alignItems: "center",
16
47
  justifyContent: "center",
17
48
  paddingTop: 16,
18
49
  paddingBottom: 16,
19
- }, children: [jsx(ModalH1, { children: "Insufficient balance." }), jsx(Button, { onClick: () => setRoute(ROUTES.SELECT_METHOD), children: "Select Another Method" })] })), jsx(OptionsList, { requiredSkeletons: 4, isLoading: solanaPaymentOptions.isLoading, options: solanaPaymentOptions.options?.map((option) => {
20
- const title = `${getDisplayPrice(option.required)} ${option.required.token.symbol}`;
21
- const subtitle = `Balance: ${getDisplayPrice(option.balance)} ${option.balance.token.symbol}`;
22
- return {
23
- id: `${option.required.token.token}-${option.required.token.symbol}`,
24
- title,
25
- subtitle,
26
- icons: [
27
- jsx("img", { src: option.required.token.logoURI, alt: option.required.token.symbol, style: { borderRadius: 9999 } }),
28
- ],
29
- onClick: () => {
30
- setSelectedSolanaTokenOption(option);
31
- setRoute(ROUTES.SOLANA_PAY_WITH_TOKEN);
32
- },
33
- };
34
- }) ?? [] })] }));
50
+ }, children: [jsx(ModalH1, { children: "Insufficient balance." }), jsx(Button, { onClick: () => setRoute(ROUTES.SELECT_METHOD), children: "Select Another Method" })] })), jsx(OptionsList, { requiredSkeletons: 4, isLoading: solanaPaymentOptions.isLoading, options: optionsList })] }));
35
51
  };
36
52
 
37
53
  export { SelectSolanaToken as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,53 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { useState, useEffect } from 'react';
3
+ import { usePayContext, ROUTES } from '../../DaimoPay.js';
4
+ import { PageContent, ModalContent, ModalH1, ModalBody } from '../../Common/Modal/styles.js';
5
+ import { ExternalLinkIcon } from '../../../assets/icons.js';
6
+ import Button from '../../Common/Button/index.js';
7
+ import ExternalPaymentSpinner from '../../Spinners/ExternalPaymentSpinner/index.js';
8
+
9
+ const WaitingExternal = () => {
10
+ const context = usePayContext();
11
+ const { triggerResize, paymentState, setRoute } = context;
12
+ const trpc = context.trpc;
13
+ const { selectedExternalOption, payWithExternal, paymentWaitingMessage, daimoPayOrder, } = paymentState;
14
+ const [externalURL, setExternalURL] = useState(null);
15
+ useEffect(() => {
16
+ const checkForSourcePayment = async () => {
17
+ if (!daimoPayOrder)
18
+ return;
19
+ const found = await trpc.findSourcePayment.query({
20
+ orderId: daimoPayOrder.id.toString(),
21
+ });
22
+ if (found) {
23
+ setRoute(ROUTES.CONFIRMATION);
24
+ }
25
+ };
26
+ const interval = setInterval(checkForSourcePayment, 1000);
27
+ return () => clearInterval(interval);
28
+ }, [daimoPayOrder?.id]);
29
+ useEffect(() => {
30
+ if (!selectedExternalOption)
31
+ return;
32
+ payWithExternal(selectedExternalOption.id).then((url) => {
33
+ setExternalURL(url);
34
+ setTimeout(() => {
35
+ window.open(url, "_blank");
36
+ });
37
+ });
38
+ }, [selectedExternalOption]);
39
+ const waitingMessageLength = paymentWaitingMessage?.length;
40
+ useEffect(() => {
41
+ triggerResize();
42
+ }, [waitingMessageLength, externalURL]);
43
+ if (!selectedExternalOption) {
44
+ return jsx(PageContent, {});
45
+ }
46
+ return (jsxs(PageContent, { children: [jsx(ExternalPaymentSpinner, { logoURI: selectedExternalOption.logoURI, logoShape: selectedExternalOption.logoShape }), jsxs(ModalContent, { style: { marginLeft: 24, marginRight: 24 }, children: [jsx(ModalH1, { children: "Waiting for Payment" }), paymentWaitingMessage && (jsx(ModalBody, { style: { marginTop: 12, marginBottom: 12 }, children: paymentWaitingMessage }))] }), jsx(Button, { icon: jsx(ExternalLinkIcon, {}), onClick: () => {
47
+ if (externalURL)
48
+ window.open(externalURL, "_blank");
49
+ }, children: "Open in New Tab" })] }));
50
+ };
51
+
52
+ export { WaitingExternal as default };
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,20 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { AnimatePresence } from 'framer-motion';
3
+ import CircleSpinner from '../CircleSpinner/index.js';
4
+ import SquircleSpinner from '../SquircleSpinner/index.js';
5
+ import { LoadingContainer, AnimationContainer } from '../styles.js';
6
+
7
+ const ExternalPaymentSpinner = ({ logoURI, logoShape, showSpinner = true, }) => {
8
+ const optionSpinner = (() => {
9
+ if (logoShape === "circle") {
10
+ return (jsx(CircleSpinner, { logo: jsx("img", { src: logoURI }), loading: showSpinner, unavailable: false }));
11
+ }
12
+ else {
13
+ return (jsx(SquircleSpinner, { logo: jsx("img", { src: logoURI }), loading: showSpinner }));
14
+ }
15
+ })();
16
+ return (jsx(LoadingContainer, { children: jsx(AnimationContainer, { "$circle": logoShape === "circle", children: jsx(AnimatePresence, { children: optionSpinner }) }) }));
17
+ };
18
+
19
+ export { ExternalPaymentSpinner as default };
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,13 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { AnimatePresence } from 'framer-motion';
3
+ import { chainToLogo } from '../../../assets/chains.js';
4
+ import CircleSpinner from '../CircleSpinner/index.js';
5
+ import { LoadingContainer, AnimationContainer } from '../styles.js';
6
+ import { ChainLogoContainer } from './styles.js';
7
+
8
+ const TokenLogoSpinner = ({ token, showSpinner = true, }) => {
9
+ return (jsx(LoadingContainer, { children: jsx(AnimationContainer, { "$circle": true, children: jsxs(AnimatePresence, { children: [chainToLogo[token.chainId] && (jsx(ChainLogoContainer, { children: chainToLogo[token.chainId] }, "ChainLogoContainer")), jsx(CircleSpinner, { logo: jsx("img", { src: token.logoURI, alt: token.symbol }, token.logoURI), loading: showSpinner, unavailable: false }, "CircleSpinner")] }) }) }));
10
+ };
11
+
12
+ export { TokenLogoSpinner as default };
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}