From 4babd41dedc6df3f6c8600ea115792a87f6e85f9 Mon Sep 17 00:00:00 2001 From: J Caso Date: Mon, 28 Aug 2023 14:42:59 +0200 Subject: [PATCH] feat: confirm phone code with auto focus --- ui/summit-2023/package-lock.json | 57 ++-- ui/summit-2023/package.json | 7 +- .../components/VerifyWallet/VerifyWallet.scss | 106 ++++++++ .../components/VerifyWallet/VerifyWallet.tsx | 256 ++++++++++++++++++ .../src/components/VerifyWallet/index.ts | 1 + .../src/components/common/Header/Header.tsx | 4 +- .../src/components/common/Modal/Modal.tsx | 8 +- 7 files changed, 409 insertions(+), 30 deletions(-) create mode 100644 ui/summit-2023/src/components/VerifyWallet/VerifyWallet.scss create mode 100644 ui/summit-2023/src/components/VerifyWallet/VerifyWallet.tsx create mode 100644 ui/summit-2023/src/components/VerifyWallet/index.ts diff --git a/ui/summit-2023/package-lock.json b/ui/summit-2023/package-lock.json index 81b446826..799ab2ba5 100644 --- a/ui/summit-2023/package-lock.json +++ b/ui/summit-2023/package-lock.json @@ -20,9 +20,9 @@ "i18next": "^22.4.14", "json-canonicalize": "^1.0.6", "moment": "^2.29.4", + "mui-tel-input": "^4.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-hot-toast": "^2.4.1", "react-i18next": "^12.2.0", "react-redux": "^8.0.7", "react-router-dom": "^6.11.1", @@ -11613,14 +11613,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/goober": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", - "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", - "peerDependencies": { - "csstype": "^3.0.10" - } - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -15284,6 +15276,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.10.43", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.43.tgz", + "integrity": "sha512-M/iPACJGsTvEy8QmUY4K0SoIFB71X2j7y2JvUMYzUXUxCNmiU+NTfHdz7gt+dC48BVfBzZi2oO6s9TDGllCfxA==" + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -16173,6 +16170,33 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mui-tel-input": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mui-tel-input/-/mui-tel-input-4.0.0.tgz", + "integrity": "sha512-TbHwdZ1bbMY/bPpR9X9RBxD1BlezOX7JxjaxOtj92nDbJItQF/pk+9qYMnFbRkjnouXk3kkyn5pCTWnU6Di+Tw==", + "dependencies": { + "@types/node": "^20.4.7", + "libphonenumber-js": "^1.10.39" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material": "^5.0.0", + "@types/react": "^18.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/mui-tel-input/node_modules/@types/node": { + "version": "20.5.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.7.tgz", + "integrity": "sha512-dP7f3LdZIysZnmvP3ANJYTSwg+wLLl8p7RqniVlV7j+oXSXAbt9h0WIBFmJy5inWZoX9wZN6eXx+YXd9Rh3RBA==" + }, "node_modules/multicast-dns": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", @@ -18836,21 +18860,6 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, - "node_modules/react-hot-toast": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", - "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", - "dependencies": { - "goober": "^2.1.10" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, "node_modules/react-i18next": { "version": "12.3.1", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.3.1.tgz", diff --git a/ui/summit-2023/package.json b/ui/summit-2023/package.json index 4eb336280..54aaf6458 100644 --- a/ui/summit-2023/package.json +++ b/ui/summit-2023/package.json @@ -15,10 +15,13 @@ "@textea/json-viewer": "^3.0.0", "@types/uuid": "^9.0.2", "history": "^5.3.0", + "i18next": "^22.4.14", "json-canonicalize": "^1.0.6", "moment": "^2.29.4", + "mui-tel-input": "^4.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^12.2.0", "react-redux": "^8.0.7", "react-router-dom": "^6.11.1", "react-scripts": "5.0.1", @@ -32,9 +35,7 @@ "swiper": "^9.3.1", "typescript": "^4.9.5", "uuid": "^9.0.0", - "web-vitals": "^2.1.4", - "i18next": "^22.4.14", - "react-i18next": "^12.2.0" + "web-vitals": "^2.1.4" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", diff --git a/ui/summit-2023/src/components/VerifyWallet/VerifyWallet.scss b/ui/summit-2023/src/components/VerifyWallet/VerifyWallet.scss new file mode 100644 index 000000000..cd2d8a73e --- /dev/null +++ b/ui/summit-2023/src/components/VerifyWallet/VerifyWallet.scss @@ -0,0 +1,106 @@ +@import './src/common/styles/colors'; + +.description { + color: var(--color-medium-grey); + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 22px; +} + +.optionItem { + border-radius: 8px; + border: 1px solid var(--color-light-blue); + background: var(--color-ultra-light-grey); + margin-top: 12px; + padding: 16px !important; + cursor: pointer; +} + +.optionLabel { + color: var(--color-medium-grey); + font-size: 16px !important; + font-style: normal; + font-weight: 600 !important; + line-height: 22px; +} + +.MuiListItemAvatar-root { + min-width: 24px !important; + margin-right: 13px; +} + +.option-icon { + color: var(--color-ultra-dark-blue) !important; +} + +.phone-number-input.MuiTelInput-TextField { + width: 100% !important; +} + +.verify-number-button-cancel.MuiButtonBase-root { + display: flex; + width: 162px; + padding: 16px 24px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 8px; + background-color: transparent !important; + border: 1px solid var(--color-light-blue); + color: var(--color-medium-grey); + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: normal; + text-transform: none; +} + +.verify-number-button-continue.MuiButtonBase-root { + display: flex; + width: 162px; + padding: 16px 24px; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 8px; + background: var(--color-light-grey); + color: var(--color-light) !important; + font-size: 16px; + font-style: normal; + font-weight: 600; + line-height: normal; + text-transform: none; +} + +.verify-number-button-valid.MuiButtonBase-root { + background: var(--color-light-green) !important; + color: var(--color-ultra-dark-blue) !important; +} + +.confirm-phone-code-input { + width: 53px; + height: 58px; + flex-shrink: 0; + border-radius: 8px; + border: 1px solid var(--color-light-grey); + background: var(--color-ultra-light-grey); + text-align: center; + outline: none; + color: var(--color-medium-grey); + font-size: 18px; + font-style: normal; + font-weight: 600; + line-height: 22px; /* 122.222% */ +} + +.didnt-receive-label{ + color: var(--color-dark-blue); + text-align: center; + font-size: 16px; + font-style: normal; + font-weight: 600 !important; + line-height: 22px; /* 137.5% */ + text-decoration-line: underline; + margin-top: 28px; +} \ No newline at end of file diff --git a/ui/summit-2023/src/components/VerifyWallet/VerifyWallet.tsx b/ui/summit-2023/src/components/VerifyWallet/VerifyWallet.tsx new file mode 100644 index 000000000..8ab5b9430 --- /dev/null +++ b/ui/summit-2023/src/components/VerifyWallet/VerifyWallet.tsx @@ -0,0 +1,256 @@ +import React, { useRef, RefObject, useState } from 'react'; + +import { + Button, + Checkbox, + FormControlLabel, Grid, + List, + ListItem, + ListItemAvatar, + Typography, + useMediaQuery, + useTheme +} from '@mui/material'; +import CallIcon from '@mui/icons-material/Call'; +import { MuiTelInput, matchIsValidTel, MuiTelInputCountry } from 'mui-tel-input'; +import './VerifyWallet.scss'; +import discordLogo from '../../common/resources/images/discord-icon.svg'; + +// TODO: env. +const excludedCountries: MuiTelInputCountry[] | undefined = []; + +const VerifyWallet: React.FC = () => { + const [verifyOption, setVerifyOption] = useState(undefined); + const [defaultCountryCode, setDefaultCountryCode] = useState('ES'); + const [phone, setPhone] = useState(''); + const [codes, setCodes] = useState(Array(6).fill('')); + + const [phoneCodeIsSent, setPhoneCodeIsSent] = useState(false); + const [checkImNotARobot, setCheckImNotARobot] = useState(false); + const [isPhoneInputDisabled, setIsPhoneInputDisabled] = useState(false); + + const inputRefs = useRef<(HTMLInputElement | null)[]>([]); + + inputRefs.current = []; + + const handleSelectOption = (option: string) => { + setVerifyOption(option); + }; + + const handleChangePhone = (phoneNumber: string) => { + setPhone(phoneNumber); + }; + + const handleSendCode= () => { + if (matchIsValidTel(phone) && checkImNotARobot){ + // TODO + } + }; + const reset = () => { + setVerifyOption(undefined) + }; + + const renderSelectOption = () => { + return <> + + To verify your address please proceed with one of the options. + + + handleSelectOption('discord')} + > + + + + + Verify with Discord + + + handleSelectOption('sms')} + > + + + + + Verify with SMS + + + + + } + + const renderConfirmCode = () => { + + const handleInputChange = (event: React.ChangeEvent, index: number) => { + const value = event.target.value; + + const updatedCodes = [...codes]; + updatedCodes[index] = value; + setCodes(updatedCodes); + + if (value && /^[0-9]$/.test(value) && index < 5) { + inputRefs.current[index + 1]?.focus(); + } + else if (!value && index > 0) { + inputRefs.current[index]?.focus(); + } + }; + + return ( + <> + + Confirm the verification code that’s been sent to {phone} + +
+ { + [...Array(6)].map((_, index) => ( + inputRefs.current[index] = el} + type="text" + maxLength={1} + onChange={(e) => handleInputChange(e, index)} + onKeyDown={(e) => { + const target = e.target as HTMLInputElement; + if (e.key === 'Backspace' && target.value === '') { + if (index > 0) { + inputRefs.current[index - 1]?.focus(); + } + } + }} + className='confirm-phone-code-input' + /> + )) + } +
+ + I didn’t receive a code + + + + + + + + + + + ); + }; + + const renderVerifyPhoneNumber = () => { + return ( + <> + + To verify your address please confirm your phone number. + + + setCheckImNotARobot(checked)} + name="notRobot" + color="primary" + /> + } + label="I am not a robot" + /> + + + + + + + + + + ); + } + + const renderVerifyDiscord = () => { + return <> + + To verify your address you need to sign a secret message. You will get the secret from our friendly Discord bot. + + } + + const renderVerify = () => { + + if (verifyOption !== undefined){ + if ( verifyOption === 'sms') { + if (phoneCodeIsSent) { + return renderConfirmCode(); + } else { + return renderVerifyPhoneNumber(); + } + } else { + return renderVerifyDiscord(); + } + } else { + return renderSelectOption(); + } + } + + return renderVerify(); +}; + +export { + VerifyWallet +} \ No newline at end of file diff --git a/ui/summit-2023/src/components/VerifyWallet/index.ts b/ui/summit-2023/src/components/VerifyWallet/index.ts new file mode 100644 index 000000000..92fb96c75 --- /dev/null +++ b/ui/summit-2023/src/components/VerifyWallet/index.ts @@ -0,0 +1 @@ +export * from './VerifyWallet'; \ No newline at end of file diff --git a/ui/summit-2023/src/components/common/Header/Header.tsx b/ui/summit-2023/src/components/common/Header/Header.tsx index fac04e218..653f0a654 100644 --- a/ui/summit-2023/src/components/common/Header/Header.tsx +++ b/ui/summit-2023/src/components/common/Header/Header.tsx @@ -26,6 +26,7 @@ import { useCardano } from '@cardano-foundation/cardano-connect-with-wallet'; import { addressSlice, walletIcon } from '../../../utils/utils'; import Modal from '../Modal/Modal'; import ConnectWalletList from '../../ConnectWalletList/ConnectWalletList'; +import {VerifyWallet} from '../../VerifyWallet'; const Header: React.FC = () => { const [drawerOpen, setDrawerOpen] = useState(false); @@ -270,8 +271,9 @@ const Header: React.FC = () => { name='verify-wallet-modal' title='Verify your wallet' onClose={handleCloseVerify} + disableBackdropClick={true} > - verify screen + void; children: React.ReactNode; }; const Modal = (props: ModalProps) => { - const { name, id, isOpen, title, width, onClose } = props; + const { name, id, isOpen, title, width, disableBackdropClick, onClose } = props; return ( { + if (disableBackdropClick && reason === 'backdropClick') return; + onClose(); + }} aria-labelledby={name} maxWidth='sm' PaperComponent={StyledPaper}