diff --git a/client/src/components/Deposit/CurrencySelector.tsx b/client/src/components/Deposit/CurrencySelector.tsx new file mode 100644 index 000000000..55aed848e --- /dev/null +++ b/client/src/components/Deposit/CurrencySelector.tsx @@ -0,0 +1,208 @@ +import React, { useReducer, useRef } from "react"; +import styled from 'styled-components'; +import { X, ChevronDown } from 'react-feather'; +import Link from '@mui/material/Link'; + +import { ThemedText } from '@theme/text'; +import { colors } from '@theme/colors'; +import { Overlay } from '@components/modals/Overlay'; +import { CurrencyRow } from '@components/modals/CurrencyRow'; +import { paymentPlatformInfo, PaymentPlatformType } from '@helpers/types'; +import { useOnClickOutside } from '@hooks/useOnClickOutside'; +import { ZKP2P_SURVEY_FORM_LINK } from "../../helpers/docUrls"; +import usePlatformSettings from "@hooks/usePlatformSettings"; + + +export const CurrencySelector: React.FC = () => { + const [isOpen, toggleOpen] = useReducer((s) => !s, false) + + const ref = useRef(null) + useOnClickOutside(ref, isOpen ? toggleOpen : undefined) + + /* + * Contexts + */ + + const { paymentPlatform, currencyIndex, setCurrencyIndex } = usePlatformSettings(); + + /* + * Handlers + */ + + const handleOverlayClick = () => { + toggleOpen(); + }; + + const handleSelectCurrency = (currencyIndex: number) => { + if (setCurrencyIndex) { + setCurrencyIndex(currencyIndex); + + toggleOpen(); + } + }; + + /* + * Component + */ + + return ( + + + + + {paymentPlatformInfo[paymentPlatform as PaymentPlatformType].platformCurrencies[currencyIndex ?? 0]} + + + + + {isOpen && ( + + + + + + + Select a currency + + + + + + + + + { paymentPlatformInfo[paymentPlatform as PaymentPlatformType].platformCurrencies.map((currency, currIndex) => ( + handleSelectCurrency(currIndex)} + /> + ))} +
+ + + + + Let us know which currencies you are interested in seeing ZKP2P add support + for. + Give feedback ↗ + + +
+
+ )} +
+ ); +}; + +const Wrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; +`; + +const CurrencyAndChevronContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + border-radius: 24px; + background: ${colors.selectorColor}; + border: 1px solid rgba(255, 255, 255, 0.2); + padding: 4px 8px 4px 4px; + gap: 6px; + cursor: pointer; + + &:hover { + background-color: ${colors.selectorHover}; + border: 1px solid ${colors.selectorHoverBorder}; + } +`; + +const CurrencyLabel = styled.div` + color: #FFF; + font-weight: 700; + letter-spacing: 0.02em; + padding: 1px 5px 0px 5px; +`; + +const CurrencySvg = styled.img` + border-radius: 18px; + width: 24px; + height: 24px; +`; + +const StyledChevronDown = styled(ChevronDown)` + width: 20px; + height: 20px; + color: #FFF; +`; + +const ModalAndOverlayContainer = styled.div` + width: 100vw; + height: 100vh; + display: flex; + justify-content: center; + position: fixed; + align-items: flex-start; + top: 0; + left: 0; + z-index: 10; +`; + +const ModalContainer = styled.div` + width: 400px; + display: flex; + flex-direction: column; + border-radius: 16px; + border: 1px solid rgba(255, 255, 255, 0.2); + background-color: ${colors.container}; + color: #FFF; + align-items: center; + z-index: 20; + + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const TableHeader = styled.div` + box-sizing: border-box; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 16px 16px 20px; +`; + +const HorizontalDivider = styled.div` + width: 100%; + border-top: 1px solid ${colors.defaultBorderColor}; +`; + +const StyledX = styled(X)` + color: #FFF; +`; + +const Table = styled.div` + width: 100%; + color: #616161; + height: 284px; + + overflow-y: auto; + scrollbar-width: thin; +`; + +const TableFooter = styled.div` + padding: 20px; + font-size: 14px; + text-align: left; + line-height: 1.5; +`; \ No newline at end of file diff --git a/client/src/components/Deposit/DepositRow.tsx b/client/src/components/Deposit/DepositRow.tsx index e13ae0b57..b1d53e196 100644 --- a/client/src/components/Deposit/DepositRow.tsx +++ b/client/src/components/Deposit/DepositRow.tsx @@ -11,6 +11,7 @@ interface PositionRowProps { outstandingIntentAmount: string; intentCount: string; conversionRate: string; + conversionCurrency: string; rowIndex: number; isCancelDepositLoading: boolean; handleWithdrawClick: () => void; @@ -22,6 +23,7 @@ export const PositionRow: React.FC = ({ outstandingIntentAmount, intentCount, conversionRate, + conversionCurrency, rowIndex, isCancelDepositLoading, handleWithdrawClick @@ -50,7 +52,7 @@ export const PositionRow: React.FC = ({ - {conversionRate} + {conversionRate} {conversionCurrency} diff --git a/client/src/components/Deposit/DepositTable.tsx b/client/src/components/Deposit/DepositTable.tsx index 81ab515f0..e0cf604f8 100644 --- a/client/src/components/Deposit/DepositTable.tsx +++ b/client/src/components/Deposit/DepositTable.tsx @@ -39,6 +39,7 @@ export interface DepositPrime { outstandingIntentAmount: string; intentCount: string; conversionRate: string; + conversionCurrency: string; } interface PositionTableProps { @@ -66,7 +67,7 @@ export const PositionTable: React.FC = ({ wiseRampAbi } = useSmartContracts(); const { refetchUsdcBalance } = useBalances(); - const { PaymentPlatform, paymentPlatform, currencyIndex } = usePlatformSettings(); + const { PaymentPlatform, paymentPlatform } = usePlatformSettings(); const { isRegistered: isVenmoRegistered @@ -213,23 +214,33 @@ export const PositionTable: React.FC = ({ useEffect(() => { let depositsToDisplay: DepositWithAvailableLiquidity[] | null = []; + let conversionCurrenciesToDisplay: string[] = []; if (paymentPlatform) { switch (paymentPlatform) { case PaymentPlatform.VENMO: - depositsToDisplay = venmoDeposits; + depositsToDisplay = venmoDeposits ?? []; + conversionCurrenciesToDisplay = depositsToDisplay.map(() => 'USD') break; case PaymentPlatform.HDFC: - depositsToDisplay = hdfcDeposits; + depositsToDisplay = hdfcDeposits ?? []; + conversionCurrenciesToDisplay = depositsToDisplay.map(() => 'INR') break; case PaymentPlatform.GARANTI: - depositsToDisplay = garantiDeposits; + depositsToDisplay = garantiDeposits ?? []; + conversionCurrenciesToDisplay = depositsToDisplay.map(() => 'TRY') break; case PaymentPlatform.WISE: - const paymentPlatformCurrency = paymentPlatformInfo[PaymentPlatform.WISE].platformCurrencies[currencyIndex ?? 0]; - depositsToDisplay = wiseDeposits?.filter(deposit => keccak256(paymentPlatformCurrency) === deposit.deposit.receiveCurrencyId) || null + depositsToDisplay = wiseDeposits ?? []; + + conversionCurrenciesToDisplay = depositsToDisplay.map(depositToDisplay => ( + paymentPlatformInfo[PaymentPlatform.WISE].platformCurrencies.find( + currency => keccak256(currency) === depositToDisplay.deposit.receiveCurrencyId + ) ?? 'EUR' + )) + break; default: @@ -241,7 +252,7 @@ export const PositionTable: React.FC = ({ setPositionsRowData([]); } else { var sanitizedPositions: DepositPrime[] = []; - sanitizedPositions = depositsToDisplay.map((depositWithLiquidity: DepositWithAvailableLiquidity) => { + sanitizedPositions = depositsToDisplay.map((depositWithLiquidity: DepositWithAvailableLiquidity, index: number) => { const deposit = depositWithLiquidity.deposit const depositor = deposit.depositor; @@ -250,6 +261,7 @@ export const PositionTable: React.FC = ({ const intentCount = deposit.intentHashes.length.toString(); const outstandingIntentAmount = toUsdcString(deposit.outstandingIntentAmount, true); const conversionRate = conversionRateToMultiplierString(deposit.conversionRate); + const conversionCurrency = conversionCurrenciesToDisplay[index]; return { depositor, @@ -257,7 +269,8 @@ export const PositionTable: React.FC = ({ totalDepositAmount, outstandingIntentAmount, intentCount, - conversionRate + conversionRate, + conversionCurrency }; }); @@ -265,7 +278,7 @@ export const PositionTable: React.FC = ({ } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [venmoDeposits, hdfcDeposits, garantiDeposits, wiseDeposits, paymentPlatform, currencyIndex]); + }, [venmoDeposits, hdfcDeposits, garantiDeposits, wiseDeposits, paymentPlatform]); useEffect(() => { const executeWithdrawDeposit = async () => { @@ -395,7 +408,7 @@ export const PositionTable: React.FC = ({ ) : !isRegistered ? ( - + @@ -413,7 +426,7 @@ export const PositionTable: React.FC = ({ ) : positionsRowData.length === 0 ? ( - + @@ -426,7 +439,7 @@ export const PositionTable: React.FC = ({ ) : ( - + @@ -435,7 +448,7 @@ export const PositionTable: React.FC = ({ - + @@ -448,6 +461,7 @@ export const PositionTable: React.FC = ({ outstandingIntentAmount={positionRow.outstandingIntentAmount} intentCount={positionRow.intentCount} conversionRate={positionRow.conversionRate} + conversionCurrency={positionRow.conversionCurrency} rowIndex={rowIndex} isCancelDepositLoading={rowIndex === selectedRowIndexToWithdraw && (isSubmitWithdrawLoading || isSubmitWithdrawMining)} handleWithdrawClick={() => { diff --git a/client/src/components/Deposit/Input.tsx b/client/src/components/Deposit/Input.tsx index 96d0d0e97..924cc6cdb 100644 --- a/client/src/components/Deposit/Input.tsx +++ b/client/src/components/Deposit/Input.tsx @@ -2,6 +2,7 @@ import React, { ChangeEvent } from "react"; import styled from 'styled-components'; import QuestionHelper from '@components/common/QuestionHelper'; +import { CurrencySelector } from "@components/Deposit/CurrencySelector"; import { colors } from '@theme/colors'; @@ -17,6 +18,8 @@ interface InputProps { inputLabel?: string; readOnly?: boolean; accessoryLabel?: string; + hasSelector?: boolean; + selectorDisabled?: boolean; helperText?: string; enableMax?: boolean valueFontSize?: string; @@ -35,6 +38,8 @@ export const Input: React.FC = ({ type = "text", readOnly = false, accessoryLabel="", + hasSelector = false, + selectorDisabled = false, helperText="", valueFontSize="24px", enableMax=false, @@ -75,25 +80,31 @@ export const Input: React.FC = ({ - - - - {accessoryLabel} - - - {enableMax && accessoryLabel && ( - - Max - - )} - - - {inputLabel ? ( - - {inputLabel} - - ) : null} - + {hasSelector ? ( + + + + ) : ( + + + + {accessoryLabel} + + + {enableMax && accessoryLabel && ( + + Max + + )} + + + {inputLabel ? ( + + {inputLabel} + + ) : null} + + )} ); }; @@ -184,6 +195,14 @@ const StyledInput = styled.input` } `; +const SelectorAccessory = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + color: #CED4DA; + padding-top: 8px; +`; + const AccessoryAndInputLabelWrapper = styled.div` display: flex; flex-direction: column; diff --git a/client/src/components/Deposit/wise/NewPosition.tsx b/client/src/components/Deposit/wise/NewPosition.tsx index 1b29529f3..4af05f8b8 100644 --- a/client/src/components/Deposit/wise/NewPosition.tsx +++ b/client/src/components/Deposit/wise/NewPosition.tsx @@ -533,7 +533,7 @@ export const NewPosition: React.FC = ({ value={receiveAmountInput} onChange={(e) => handleInputChange(e.currentTarget.value, setReceiveAmountInput)} type="number" - inputLabel={paymentPlatformInfo[PaymentPlatform.WISE].platformCurrencies[currencyIndex ?? 0]} + hasSelector={true} placeholder="1050" helperText={wiseStrings.get('NEW_DEPOSIT_RECEIVE_TOOLTIP')} /> diff --git a/client/src/components/Liquidity/DepositsTable.tsx b/client/src/components/Liquidity/DepositsTable.tsx index f56db1d97..6f96d3706 100644 --- a/client/src/components/Liquidity/DepositsTable.tsx +++ b/client/src/components/Liquidity/DepositsTable.tsx @@ -77,21 +77,30 @@ export const DepositsTable: React.FC = () => { useEffect(() => { let depositStoreToDisplay: DepositWithAvailableLiquidity[] = []; + let conversionCurrenciesToDisplay: string[] = []; switch (paymentPlatform) { case PaymentPlatform.VENMO: depositStoreToDisplay = venmoDepositStore ?? []; + conversionCurrenciesToDisplay = depositStoreToDisplay.map(() => 'USD') break; case PaymentPlatform.HDFC: depositStoreToDisplay = hdfcDepositStore ?? []; + conversionCurrenciesToDisplay = depositStoreToDisplay.map(() => 'EUR') break; case PaymentPlatform.GARANTI: depositStoreToDisplay = garantiDepositStore ?? []; + conversionCurrenciesToDisplay = depositStoreToDisplay.map(() => 'TRY') break; case PaymentPlatform.WISE: depositStoreToDisplay = wiseDepositStore ?? []; + conversionCurrenciesToDisplay = depositStoreToDisplay.map(depositToDisplay => ( + paymentPlatformInfo[PaymentPlatform.WISE].platformCurrencies.find( + currency => keccak256(currency) === depositToDisplay.deposit.receiveCurrencyId + ) ?? 'EUR' + )) break; default: @@ -101,25 +110,15 @@ export const DepositsTable: React.FC = () => { if (depositStoreToDisplay.length === 0) { setPositionsRowData([]); } else { - const preprocessedMulticurrencyDeposits = depositStoreToDisplay.filter((depositWithLiquidity: DepositWithAvailableLiquidity) => { - const deposit = depositWithLiquidity.deposit; - if (deposit.receiveCurrencyId) { - const platformCurrency = paymentPlatformInfo[deposit.platformType].platformCurrencies[currencyIndex ?? 0]; - return deposit.receiveCurrencyId === keccak256(platformCurrency); - } else { - return true - } - }); - var sanitizedDeposits: DepositPrime[] = []; - sanitizedDeposits = preprocessedMulticurrencyDeposits.map((depositWithLiquidity: DepositWithAvailableLiquidity) => { + sanitizedDeposits = depositStoreToDisplay.map((depositWithLiquidity: DepositWithAvailableLiquidity, index: number) => { const deposit = depositWithLiquidity.deposit const platformType = deposit.platformType const depositor = deposit.depositor; const availableDepositAmount = toUsdcString(depositWithLiquidity.availableLiquidity, true); const totalDepositAmount = toUsdcString(deposit.depositAmount, true); const conversionRate = conversionRateToMultiplierString(deposit.conversionRate); - const conversionCurrency = paymentPlatformInfo[deposit.platformType].platformCurrencies[currencyIndex ?? 0]; + const conversionCurrency = conversionCurrenciesToDisplay[index]; return { depositor, @@ -127,7 +126,7 @@ export const DepositsTable: React.FC = () => { availableDepositAmount, totalDepositAmount, conversionRate, - conversionCurrency + conversionCurrency, }; }); @@ -172,7 +171,7 @@ export const DepositsTable: React.FC = () => { Liquidity - + diff --git a/client/src/components/Notary/NotarizationTable.tsx b/client/src/components/Notary/NotarizationTable.tsx index 6f7fbf29f..c851ea0c8 100644 --- a/client/src/components/Notary/NotarizationTable.tsx +++ b/client/src/components/Notary/NotarizationTable.tsx @@ -162,7 +162,7 @@ export const NotarizationTable: React.FC = ({ case NotaryVerificationCircuit.TRANSFER: return { detected_copy: 'The following transaction was detected from your Wise account history', - metadata_copy: `€${selectedRequest.metadata} EUR on ${selectedRequest.date}`, + metadata_copy: `€${selectedRequest.metadata} EUR on ${selectedRequest.date}`, // TODO: update this metadata_type_copy: 'transaction', transaction_type_copy: 'order' }; diff --git a/client/src/components/Registration/garanti/ExistingRegistration.tsx b/client/src/components/Registration/garanti/ExistingRegistration.tsx index 8f1e10671..325d41dd1 100644 --- a/client/src/components/Registration/garanti/ExistingRegistration.tsx +++ b/client/src/components/Registration/garanti/ExistingRegistration.tsx @@ -134,7 +134,7 @@ export const ExistingRegistration: React.FC = ({ ) : ( - + { !isRegistered && ( diff --git a/client/src/components/Registration/hdfc/ExistingRegistration.tsx b/client/src/components/Registration/hdfc/ExistingRegistration.tsx index e54fbbcef..0aba40e21 100644 --- a/client/src/components/Registration/hdfc/ExistingRegistration.tsx +++ b/client/src/components/Registration/hdfc/ExistingRegistration.tsx @@ -134,7 +134,7 @@ export const ExistingRegistration: React.FC = ({ ) : ( - + { !isRegistered && ( diff --git a/client/src/components/Registration/venmo/ExistingRegistration.tsx b/client/src/components/Registration/venmo/ExistingRegistration.tsx index d421de0ef..dcc98936b 100644 --- a/client/src/components/Registration/venmo/ExistingRegistration.tsx +++ b/client/src/components/Registration/venmo/ExistingRegistration.tsx @@ -138,7 +138,7 @@ export const ExistingRegistration: React.FC = ({ ) : ( - + { !isRegistered && ( diff --git a/client/src/components/Registration/wise/ExistingRegistration.tsx b/client/src/components/Registration/wise/ExistingRegistration.tsx index 67ab1dbd4..257d91438 100644 --- a/client/src/components/Registration/wise/ExistingRegistration.tsx +++ b/client/src/components/Registration/wise/ExistingRegistration.tsx @@ -133,7 +133,7 @@ export const ExistingRegistration: React.FC = ({ ) : ( - + { !isRegistered && ( diff --git a/client/src/components/Swap/CurrencySelector.tsx b/client/src/components/Swap/CurrencySelector.tsx new file mode 100644 index 000000000..b798989fc --- /dev/null +++ b/client/src/components/Swap/CurrencySelector.tsx @@ -0,0 +1,237 @@ +import React, { useReducer, useRef } from "react"; +import styled from 'styled-components'; +import { X, ChevronDown } from 'react-feather'; +import Link from '@mui/material/Link'; + +import { ThemedText } from '@theme/text'; +import { colors } from '@theme/colors'; +import { Overlay } from '@components/modals/Overlay'; +import { CurrencyRow } from '@components/modals/CurrencyRow'; +import { paymentPlatformInfo, PaymentPlatformType } from '@helpers/types'; +import { useOnClickOutside } from '@hooks/useOnClickOutside'; +import { ZKP2P_SURVEY_FORM_LINK } from "../../helpers/docUrls"; +import usePlatformSettings from "@hooks/usePlatformSettings"; + + +export const CurrencySelector: React.FC = () => { + const [isOpen, toggleOpen] = useReducer((s) => !s, false) + + const ref = useRef(null) + useOnClickOutside(ref, isOpen ? toggleOpen : undefined) + + /* + * Contexts + */ + + const { paymentPlatform, currencyIndex, setCurrencyIndex } = usePlatformSettings(); + + /* + * Handlers + */ + + const handleOverlayClick = () => { + toggleOpen(); + }; + + const handleSelectCurrency = (currencyIndex: number) => { + if (setCurrencyIndex) { + setCurrencyIndex(currencyIndex); + + toggleOpen(); + } + }; + + /* + * Component + */ + + return ( + + + + + + + + {'Currency'} + + + {paymentPlatformInfo[paymentPlatform as PaymentPlatformType].platformCurrencies[currencyIndex ?? 0]} + + + + + + + + {isOpen && ( + + + + + + + Select a Currency + + + + + + + + + { paymentPlatformInfo[paymentPlatform as PaymentPlatformType].platformCurrencies.map((currency, currIndex) => ( + handleSelectCurrency(currIndex)} + /> + ))} +
+ + + + + Let us know which currencies you are interested in seeing ZKP2P add support + for. + Give feedback ↗ + + +
+
+ )} +
+ ); +}; + +const Wrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; +`; + +const CurrencyContainer = styled.div` + display: flex; + flex-direction: row; + width: 188px; + border-radius: 16px; + border: 1px solid ${colors.defaultBorderColor}; + justify-content: space-between; + align-items: center; + background: ${colors.selectorColor}; + padding: 1.1rem 1rem; + cursor: pointer; + + &:hover { + background-color: ${colors.selectorHover}; + border: 1px solid ${colors.selectorHoverBorder}; + } +`; + +const CurrencyLogoAndNameContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + gap: 1rem; + justify-content: flex-start; +`; + +const CurrencyNameContainer = styled.div` + display: flex; + flex-direction: column; + gap: 0.4rem; + justify-content: center; + text-align: left; +`; + +const CurrencyHeader = styled.div` + font-size: 14px; + color: #CED4DA; +`; + +const CurrencyNameLabel = styled.div` + font-size: 16px; + color: #FFF; +`; + +const CurrencySvg = styled.img` + border-radius: 18px; + width: 32px; + height: 32px; +`; + +const StyledChevronDown = styled(ChevronDown)` + width: 20px; + height: 20px; + color: #FFF; +`; + +const ModalAndOverlayContainer = styled.div` + width: 100vw; + height: 100vh; + display: flex; + justify-content: center; + position: fixed; + align-items: flex-start; + top: 0; + left: 0; + z-index: 10; +`; + +const ModalContainer = styled.div` + width: 400px; + display: flex; + flex-direction: column; + border-radius: 16px; + border: 1px solid rgba(255, 255, 255, 0.2); + background-color: ${colors.container}; + color: #FFF; + align-items: center; + z-index: 20; + + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const TableHeader = styled.div` + box-sizing: border-box; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 16px 16px 20px; +`; + +const HorizontalDivider = styled.div` + width: 100%; + border-top: 1px solid ${colors.defaultBorderColor}; +`; + +const StyledX = styled(X)` + color: #FFF; +`; + +const Table = styled.div` + width: 100%; + color: #616161; + height: 284px; + + overflow-y: auto; + scrollbar-width: thin; +`; + +const TableFooter = styled.div` + padding: 20px; + font-size: 14px; + text-align: left; + line-height: 1.5; +`; diff --git a/client/src/components/Swap/Input.tsx b/client/src/components/Swap/Input.tsx index 609037d32..b63498aae 100644 --- a/client/src/components/Swap/Input.tsx +++ b/client/src/components/Swap/Input.tsx @@ -1,8 +1,5 @@ import React, { ChangeEvent } from "react"; import styled from 'styled-components'; - -import { TokenSelector } from '@components/Swap/TokenSelector'; -import { PlatformSelector } from '@components/modals/PlatformSelector'; import { colors } from '@theme/colors'; @@ -68,10 +65,10 @@ export const Input: React.FC = ({ {inputLabel ? ( - - ) : ( - - )} + + {inputLabel} + + ) : null} @@ -209,3 +206,10 @@ const AccessoryTextButton = styled.div` cursor: pointer; color: #FFFFFF; `; + +const InputLabel = styled.div` + pointer-events: none; + color: #9ca3af; + font-size: 20px; + text-align: right; +`; diff --git a/client/src/components/Swap/OnRamperIntentRow.tsx b/client/src/components/Swap/OnRamperIntentRow.tsx index d3b8df147..c1070451e 100644 --- a/client/src/components/Swap/OnRamperIntentRow.tsx +++ b/client/src/components/Swap/OnRamperIntentRow.tsx @@ -10,7 +10,7 @@ import { ReviewRequirements } from '@components/modals/ReviewRequirements'; import usePlatformSettings from "@hooks/usePlatformSettings"; import useSmartContracts from "@hooks/useSmartContracts"; import { alchemyMainnetEthersProvider } from "index"; -import { paymentPlatformInfo, PaymentPlatformType } from "@helpers/types"; +import { PaymentPlatformType } from "@helpers/types"; interface IntentRowProps { paymentPlatform: PaymentPlatformType | undefined; @@ -21,6 +21,7 @@ interface IntentRowProps { depositorName?: string; depositorAddress: string; recipientAddress: string; + receiveCurrencyId: string; handleCompleteOrderClick: () => void; shouldAutoSelectIntent: boolean; resetShouldAutoSelectIntent: () => void; @@ -37,6 +38,7 @@ export const IntentRow: React.FC = ({ depositorAddress, depositorName, recipientAddress, + receiveCurrencyId, handleCompleteOrderClick, shouldAutoSelectIntent, resetShouldAutoSelectIntent, @@ -50,7 +52,6 @@ export const IntentRow: React.FC = ({ const { blockscanUrl } = useSmartContracts(); const { PaymentPlatform, - currencyIndex, reviewedRequirementsForPlatform, markPlatformRequirementsAsReviewed } = usePlatformSettings(); @@ -76,28 +77,40 @@ export const IntentRow: React.FC = ({ case PaymentPlatform.VENMO: return { qrLink: `https://venmo.com/code?user_id=${depositorVenmoId}`, - currencySymbol: paymentPlatformInfo[paymentPlatform].currencySymbols[currencyIndex ?? 0], + currencySymbol: '$', paymentPlatformName: 'Venmo', }; case PaymentPlatform.HDFC: return { qrLink: `upi://pay?pa=${depositorVenmoId.replace(/\0/g, '')}&am=${amountUSDToSend}&cu=INR`, - currencySymbol: paymentPlatformInfo[paymentPlatform].currencySymbols[currencyIndex ?? 0], + currencySymbol: '₹', paymentPlatformName: 'HDFC', }; case PaymentPlatform.GARANTI: return { qrLink: ``, - currencySymbol: paymentPlatformInfo[paymentPlatform].currencySymbols[currencyIndex ?? 0], + currencySymbol: '₺', paymentPlatformName: 'Garanti', }; case PaymentPlatform.WISE: + let currencySymbol = ''; + switch (receiveCurrencyId) { + case '0xfff16d60be267153303bbfa66e593fb8d06e24ea5ef24b6acca5224c2ca6b907': + currencySymbol = '€'; + break; + case '0x90832e2dc3221e4d56977c1aa8f6a6706b9ad6542fbbdaac13097d0fa5e42e67': + currencySymbol = '£'; + break; + case '0xc241cc1f9752d2d53d1ab67189223a3f330e48b75f73ebf86f50b2c78fe8df88': + currencySymbol = 'SGD$'; + break; + } return { qrLink: `https://wise.com/pay/me/${depositorVenmoId}`, - currencySymbol: paymentPlatformInfo[paymentPlatform].currencySymbols[currencyIndex ?? 0], + currencySymbol, paymentPlatformName: 'Wise', }; @@ -183,6 +196,7 @@ export const IntentRow: React.FC = ({ onBackClick={handleModalBackClicked} onCompleteClick={handleCompleteOrderClick} paymentPlatform={paymentPlatform || PaymentPlatform.VENMO} + receiveCurrencyId={receiveCurrencyId} /> ) } diff --git a/client/src/components/Swap/OnRamperIntentTable.tsx b/client/src/components/Swap/OnRamperIntentTable.tsx index eb765408a..bda9ce987 100644 --- a/client/src/components/Swap/OnRamperIntentTable.tsx +++ b/client/src/components/Swap/OnRamperIntentTable.tsx @@ -349,6 +349,7 @@ export const OnRamperIntentTable: React.FC = ({ const venmoIdString = currentIntent.depositorVenmoId.toString(); const depositorAddress = storedDeposit.deposit.depositor; const recipientAddress = currentIntent.intent.to; + const receiveCurrencyId = storedDeposit.deposit.receiveCurrencyId ?? ''; const sanitizedIntent: IntentRowData = { paymentPlatform, @@ -359,6 +360,7 @@ export const OnRamperIntentTable: React.FC = ({ depositorVenmoId: venmoIdString, depositorAddress, recipientAddress, + receiveCurrencyId, handleCompleteOrderClick: () => { if (onIntentRowClick) { onIntentRowClick(); @@ -454,6 +456,7 @@ export const OnRamperIntentTable: React.FC = ({ depositorAddress={intentsRow.depositorAddress} depositorName={intentsRow.depositorName} recipientAddress={intentsRow.recipientAddress} + receiveCurrencyId={intentsRow.receiveCurrencyId} handleCompleteOrderClick={intentsRow.handleCompleteOrderClick} shouldAutoSelectIntent={intentsRow.shouldAutoSelectIntent} resetShouldAutoSelectIntent={intentsRow.resetShouldAutoSelectIntent} diff --git a/client/src/components/Swap/PlatformSelector.tsx b/client/src/components/Swap/PlatformSelector.tsx new file mode 100644 index 000000000..baa2f4f4c --- /dev/null +++ b/client/src/components/Swap/PlatformSelector.tsx @@ -0,0 +1,229 @@ +import React, { useReducer, useRef } from "react"; +import styled from 'styled-components'; +import { X, ChevronDown } from 'react-feather'; +import Link from '@mui/material/Link'; + +import { ThemedText } from '@theme/text'; +import { colors } from '@theme/colors'; +import { Overlay } from '@components/modals/Overlay'; +import { PlatformRow } from '@components/modals/PlatformRow'; +import { paymentPlatforms, paymentPlatformInfo, PaymentPlatformType } from '@helpers/types'; +import { useOnClickOutside } from '@hooks/useOnClickOutside'; +import { ZKP2P_SURVEY_FORM_LINK } from "../../helpers/docUrls"; +import usePlatformSettings from "@hooks/usePlatformSettings"; + + +export const PlatformSelector: React.FC = () => { + const [isOpen, toggleOpen] = useReducer((s) => !s, false) + + const ref = useRef(null) + useOnClickOutside(ref, isOpen ? toggleOpen : undefined) + + /* + * Contexts + */ + + const { paymentPlatform, setPaymentPlatform, setCurrencyIndex } = usePlatformSettings(); + + /* + * Handlers + */ + + const handleOverlayClick = () => { + toggleOpen(); + }; + + const handleSelectPlatform = (platform: PaymentPlatformType) => { + if (setPaymentPlatform && setCurrencyIndex) { + setPaymentPlatform(platform); + setCurrencyIndex(0); + + toggleOpen(); + } + }; + + /* + * Component + */ + + return ( + + + + + + {'Platform'} + + + {paymentPlatformInfo[paymentPlatform as PaymentPlatformType].platformName} + + + + + + + + {isOpen && ( + + + + + + + Select a Platform + + + + + + + + + {paymentPlatforms.map((platform, index) => ( + handleSelectPlatform(platform)} + /> + ))} +
+ + + + + Let us know which networks you are interested in seeing ZKP2P add support + for. + Give feedback ↗ + + +
+
+ )} +
+ ); +}; + +const Wrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; +`; + +const CurrencyContainer = styled.div` + display: flex; + flex-direction: row; + width: 188px; + border-radius: 16px; + border: 1px solid ${colors.defaultBorderColor}; + justify-content: space-between; + align-items: center; + background: ${colors.selectorColor}; + padding: 1.1rem 1rem; + cursor: pointer; + + &:hover { + background-color: ${colors.selectorHover}; + border: 1px solid ${colors.selectorHoverBorder}; + } +`; + +const CurrencyLogoAndNameContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + gap: 1rem; + justify-content: flex-start; +`; + +const CurrencyNameContainer = styled.div` + display: flex; + flex-direction: column; + gap: 0.4rem; + justify-content: center; + text-align: left; +`; + +const CurrencyHeader = styled.div` + font-size: 14px; + color: #CED4DA; +`; + +const CurrencyNameLabel = styled.div` + font-size: 16px; + color: #FFF; +`; + +const StyledChevronDown = styled(ChevronDown)` + width: 20px; + height: 20px; + color: #FFF; +`; + +const ModalAndOverlayContainer = styled.div` + width: 100vw; + height: 100vh; + display: flex; + justify-content: center; + position: fixed; + align-items: flex-start; + top: 0; + left: 0; + z-index: 10; +`; + +const ModalContainer = styled.div` + width: 400px; + display: flex; + flex-direction: column; + border-radius: 16px; + border: 1px solid rgba(255, 255, 255, 0.2); + background-color: ${colors.container}; + color: #FFF; + align-items: center; + z-index: 20; + + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const TableHeader = styled.div` + box-sizing: border-box; + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 16px 16px 20px; +`; + +const HorizontalDivider = styled.div` + width: 100%; + border-top: 1px solid ${colors.defaultBorderColor}; +`; + +const StyledX = styled(X)` + color: #FFF; +`; + +const Table = styled.div` + width: 100%; + color: #616161; + height: 284px; + + overflow-y: auto; + scrollbar-width: thin; +`; + +const TableFooter = styled.div` + padding: 20px; + font-size: 14px; + text-align: left; + line-height: 1.5; +`; diff --git a/client/src/components/Swap/SwapModal.tsx b/client/src/components/Swap/SwapModal.tsx index 0fe695a72..0a9b1b7d9 100644 --- a/client/src/components/Swap/SwapModal.tsx +++ b/client/src/components/Swap/SwapModal.tsx @@ -24,6 +24,7 @@ interface SwapModalProps { onBackClick: () => void onCompleteClick: () => void paymentPlatform: PaymentPlatformType + receiveCurrencyId: string; } export const SwapModal: React.FC = ({ @@ -34,7 +35,8 @@ export const SwapModal: React.FC = ({ amount, onBackClick, onCompleteClick, - paymentPlatform + paymentPlatform, + receiveCurrencyId }) => { /* @@ -86,10 +88,23 @@ export const SwapModal: React.FC = ({ }; case PaymentPlatform.WISE: + let currencySymbol = ''; + switch (receiveCurrencyId) { + case '0xfff16d60be267153303bbfa66e593fb8d06e24ea5ef24b6acca5224c2ca6b907': + currencySymbol = '€'; + break; + case '0x90832e2dc3221e4d56977c1aa8f6a6706b9ad6542fbbdaac13097d0fa5e42e67': + currencySymbol = '£'; + break; + case '0xc241cc1f9752d2d53d1ab67189223a3f330e48b75f73ebf86f50b2c78fe8df88': + currencySymbol = 'SGD$'; + break; + } + return { troubleScanningQRCodeLink: ZKP2P_TG_INDIA_CHAT_LINK, paymentPlatformName: 'Wise', - instructionsText: `Scan and send €${amount}
to ${venmoId}`, + instructionsText: `Scan and send ${currencySymbol}${amount}
to ${venmoId}`, }; default: diff --git a/client/src/components/Swap/index.tsx b/client/src/components/Swap/index.tsx index 5953df707..a7192f11b 100644 --- a/client/src/components/Swap/index.tsx +++ b/client/src/components/Swap/index.tsx @@ -12,9 +12,12 @@ import { ThemedText } from '@theme/text'; import { colors } from '@theme/colors'; import { IndicativeQuote } from '@helpers/types'; import { InstructionDrawer } from '@components/Swap/InstructionDrawer'; +import { CurrencySelector } from '@components/Swap/CurrencySelector'; +import { PlatformSelector } from '@components/Swap/PlatformSelector'; import { SettingsDropdown } from './SettingsDropdown'; import { DEPOSIT_REFETCH_INTERVAL, EMPTY_STRING, ZERO } from "@helpers/constants"; import { toBigInt, toUsdcString, conversionRateToMultiplierString } from '@helpers/units' +import { paymentPlatformInfo } from '@helpers/types'; import useAccount from '@hooks/useAccount'; import useBalances from '@hooks/useBalance'; import useSmartContracts from '@hooks/useSmartContracts'; @@ -72,7 +75,7 @@ const SwapForm: React.FC = ({ wiseRampAddress, wiseRampAbi } = useSmartContracts(); - const { paymentPlatform, PaymentPlatform } = usePlatformSettings(); + const { paymentPlatform, PaymentPlatform, currencyIndex } = usePlatformSettings(); const { isRegistered, @@ -505,12 +508,17 @@ const SwapForm: React.FC = ({ + + + + handleInputChange(event, 'requestedUSDC')} type="number" + inputLabel="USDC" accessoryLabel={usdcBalanceLabel} accessoryButtonLabel="Max" onAccessoryButtonClick={setInputToMax} @@ -525,7 +533,7 @@ const SwapForm: React.FC = ({ type="number" accessoryLabel={bestAvailableRateLabel} accessoryLabelAlignment="left" - inputLabel="$" + inputLabel={paymentPlatformInfo[paymentPlatform || PaymentPlatform.VENMO].platformCurrencies[currencyIndex ?? 0]} placeholder="0.00" readOnly={true} /> @@ -616,6 +624,14 @@ const MainContentWrapper = styled.div` justify-content: center; `; +const PlatformCurrencyContainer = styled.div` + display: flex; + flex-direction: row; + gap: 0.5rem; + align-items: center; + justify-content: space-between; +`; + const CTAButton = styled(Button)` display: flex; align-items: center; diff --git a/client/src/components/modals/CurrencyRow.tsx b/client/src/components/modals/CurrencyRow.tsx new file mode 100644 index 000000000..e14cc40e4 --- /dev/null +++ b/client/src/components/modals/CurrencyRow.tsx @@ -0,0 +1,78 @@ +import React from "react"; +import styled from 'styled-components'; + + +interface CurrencyRowProps { + platformCurrency: string; + isSelected: boolean; + flagSvg: string; + onRowClick: () => void; +} + +export const CurrencyRow: React.FC = ({ + platformCurrency, + isSelected, + flagSvg, + onRowClick, +}: CurrencyRowProps) => { + CurrencyRow.displayName = "CurrencyRow"; + + return ( + + + + + {platformCurrency} + + + + ); +}; + +const Container = styled.div<{ selected: boolean }>` + display: flex; + flex-direction: row; + height: 54px; + padding: 12px 24px 12px 20px; + + ${({ selected }) => selected && ` + background-color: #191D28; + box-shadow: none; + `} + + ${({ selected }) => !selected && ` + &:hover { + background-color: #191D28; + box-shadow: none; + } + `} +`; + +const DetailsContainer = styled.div` + display: flex; + align-items: center; + gap: 1.25rem; + flex: 1; +`; + +const PlatformAndCurrencyLabel = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + flex-grow: 1; +`; + +const PlatformLabel = styled.div` + display: flex; + flex-direction: row; + padding-top: 2px; + color: #FFFFFF; +`; + +const FlagSvg = styled.img` + width: 36px; + height: 36px; +`; diff --git a/client/src/components/modals/PlatformRow.tsx b/client/src/components/modals/PlatformRow.tsx index 4fd2fdab4..cf5d86e4e 100644 --- a/client/src/components/modals/PlatformRow.tsx +++ b/client/src/components/modals/PlatformRow.tsx @@ -4,19 +4,13 @@ import styled from 'styled-components'; interface PlatformRowProps { platformName: string; - platformCurrency: string; isSelected: boolean; - onlyDisplayPlatform: boolean; - flagSvg: string; onRowClick: () => void; } export const PlatformRow: React.FC = ({ platformName, - platformCurrency, isSelected, - onlyDisplayPlatform, - flagSvg, onRowClick, }: PlatformRowProps) => { PlatformRow.displayName = "PlatformRow"; @@ -27,10 +21,8 @@ export const PlatformRow: React.FC = ({ selected={isSelected} > - {platformName} - { !onlyDisplayPlatform && {platformCurrency} } @@ -75,14 +67,4 @@ const PlatformLabel = styled.div` flex-direction: row; padding-top: 2px; color: #FFFFFF; -`; - -const CurrencyLabel = styled.div` - padding-top: 4px; - color: #FFFFFF; -`; - -const FlagSvg = styled.img` - width: 36px; - height: 36px; -`; +`; \ No newline at end of file diff --git a/client/src/components/modals/PlatformSelector.tsx b/client/src/components/modals/PlatformSelector.tsx index 368c442db..7e40adaff 100644 --- a/client/src/components/modals/PlatformSelector.tsx +++ b/client/src/components/modals/PlatformSelector.tsx @@ -7,14 +7,13 @@ import { ThemedText } from '@theme/text'; import { colors } from '@theme/colors'; import { Overlay } from '@components/modals/Overlay'; import { PlatformRow } from '@components/modals/PlatformRow'; -import { SVGIconThemed } from '@components/SVGIcon/SVGIconThemed'; import { paymentPlatforms, paymentPlatformInfo, PaymentPlatformType } from '@helpers/types'; import { useOnClickOutside } from '@hooks/useOnClickOutside'; import { ZKP2P_SURVEY_FORM_LINK } from "../../helpers/docUrls"; import usePlatformSettings from "@hooks/usePlatformSettings"; -export const PlatformSelector: React.FC<{ onlyDisplayPlatform: boolean }> = ({ onlyDisplayPlatform = false }) => { +export const PlatformSelector: React.FC = () => { const [isOpen, toggleOpen] = useReducer((s) => !s, false) const ref = useRef(null) @@ -24,7 +23,7 @@ export const PlatformSelector: React.FC<{ onlyDisplayPlatform: boolean }> = ({ o * Contexts */ - const { paymentPlatform, setPaymentPlatform, currencyIndex, setCurrencyIndex } = usePlatformSettings(); + const { paymentPlatform, setPaymentPlatform } = usePlatformSettings(); /* * Handlers @@ -34,10 +33,9 @@ export const PlatformSelector: React.FC<{ onlyDisplayPlatform: boolean }> = ({ o toggleOpen(); }; - const handleSelectPlatform = (platform: PaymentPlatformType, currencyIndex: number) => { - if (setPaymentPlatform && setCurrencyIndex) { + const handleSelectPlatform = (platform: PaymentPlatformType) => { + if (setPaymentPlatform) { setPaymentPlatform(platform); - setCurrencyIndex(currencyIndex); toggleOpen(); } @@ -49,15 +47,9 @@ export const PlatformSelector: React.FC<{ onlyDisplayPlatform: boolean }> = ({ o return ( - - { !onlyDisplayPlatform - && - } + - { onlyDisplayPlatform - ? paymentPlatformInfo[paymentPlatform as PaymentPlatformType].platformName - : paymentPlatformInfo[paymentPlatform as PaymentPlatformType].platformCurrencies[currencyIndex ?? 0] - } + {paymentPlatformInfo[paymentPlatform as PaymentPlatformType].platformName} @@ -83,31 +75,14 @@ export const PlatformSelector: React.FC<{ onlyDisplayPlatform: boolean }> = ({ o - { onlyDisplayPlatform - ? paymentPlatforms.map((platform, index) => ( - handleSelectPlatform(platform, 0)} - /> - )) - : paymentPlatforms.map((platform, index) => - paymentPlatformInfo[platform].platformCurrencies.map((currency, currIndex) => ( - handleSelectPlatform(platform, currIndex)} - /> - )) - )} + {paymentPlatforms.map((platform, index) => ( + handleSelectPlatform(platform)} + /> + ))}
@@ -131,15 +106,15 @@ const Wrapper = styled.div` align-items: center; `; -const PlatformNameAndChevronContainer = styled.div<{ onlyDisplayPlatform: boolean }>` +const PlatformNameAndChevronContainer = styled.div` display: flex; flex-direction: row; align-items: center; border-radius: 24px; background: ${colors.selectorColor}; border: 1px solid rgba(255, 255, 255, 0.2); - padding: ${({ onlyDisplayPlatform }) => onlyDisplayPlatform ? '6px 8px 6px 14px': '4px 6px 4px 4px'}; - gap: ${({ onlyDisplayPlatform }) => onlyDisplayPlatform ? '4px' : '6px'}; + padding: 6px 8px 6px 14px; + gap: 4px; cursor: pointer; &:hover { @@ -222,4 +197,4 @@ const TableFooter = styled.div` font-size: 14px; text-align: left; line-height: 1.5; -`; +`; \ No newline at end of file diff --git a/client/src/helpers/strings/wise.ts b/client/src/helpers/strings/wise.ts index 0a7f8f534..1d1ff6213 100644 --- a/client/src/helpers/strings/wise.ts +++ b/client/src/helpers/strings/wise.ts @@ -41,7 +41,7 @@ const strings: PlatformStrings = { // New Deposit NEW_DEPOSIT_INSTRUCTIONS: ` Depositing requires registering a valid Wise multi currency id. Provide the USDC liquidity to deposit and - desired USDC/EUR conversion rate. You will receive EUR payments from users who claim your deposit. + desired USDC to fiat conversion rate. You will receive fiat payments from users who claim your deposit. `, NEW_DEPOSIT_ADDITIONAL_REGISTRATION_TOOLTIP: ` This is a second registration step required for depositors only to connect a Wise multi currency id @@ -60,7 +60,7 @@ const strings: PlatformStrings = { You can withdraw unclaimed USDC or USDC not locked for orders at any time. `, NEW_DEPOSIT_RECEIVE_TOOLTIP: ` - This is the amount of EUR you will receive if your entire deposit is claimed. + This is the amount of fiat currency you will receive if your entire deposit is claimed. `, // Instruction Drawer @@ -72,10 +72,10 @@ const strings: PlatformStrings = { `, INSTRUCTION_DRAWER_STEP_THREE: ` Click 'Send' and complete the payment on any UPI ID linked to your Wise bank account. Ensure you have email notifications from Wise InstaAlerts enabled - `, + `, // TODO: update this INSTRUCTION_DRAWER_STEP_FOUR: ` Continue through to validate email proof of transaction. Submit transaction containing proof to receive the requested USDC - `, + `, // TODO: update this // Payment Requirements PAYMENT_REQUIREMENT_STEP_ONE: ` diff --git a/client/src/helpers/types/paymentPlatform.ts b/client/src/helpers/types/paymentPlatform.ts index 1dcd19a5c..e703b7a64 100644 --- a/client/src/helpers/types/paymentPlatform.ts +++ b/client/src/helpers/types/paymentPlatform.ts @@ -35,7 +35,6 @@ interface PaymentPlatformData { platformId: PaymentPlatformType; platformName: string; platformCurrencies: string[]; - currencySymbols: string[]; flagSvgs: string[]; platformCurrencyIcons: string[]; } @@ -45,7 +44,6 @@ export const paymentPlatformInfo: Record