Path: blob/main/components/dashboard/src/start/VerifyModal.tsx
2500 views
/**1* Copyright (c) 2022 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import { useState } from "react";7import Alert, { AlertType } from "../components/Alert";8import Modal, { ModalBody, ModalFooter, ModalHeader } from "../components/Modal";9import PhoneInput from "react-intl-tel-input";10import "react-intl-tel-input/dist/main.css";11import "./phone-input.css";12import { Button } from "@podkit/buttons/Button";13import { LinkButton } from "../components/LinkButton";14import { verificationClient } from "../service/public-api";15import { InputField } from "../components/forms/InputField";16import { TextInputField } from "../components/forms/TextInputField";17import { Link } from "react-router-dom";1819interface VerifyModalState {20phoneNumber?: string;21phoneNumberValid?: boolean;22sent?: Date;23sending?: boolean;24message?: {25type: AlertType;26text: string;27};28token?: string;29verified?: boolean;30}3132export function VerifyModal() {33const [state, setState] = useState<VerifyModalState>({});34const [verificationId, setVerificationId] = useState("");3536if (!state.sent) {37const sendCode = async () => {38try {39setState({40...state,41message: undefined,42sending: true,43});44const resp = await verificationClient.sendPhoneNumberVerificationToken({45phoneNumber: state.phoneNumber || "",46});47setVerificationId(resp.verificationId);48setState({49...state,50sending: false,51sent: new Date(),52});53} catch (err) {54setState({55sent: undefined,56sending: false,57message: {58type: "error",59text: err.toString(),60},61});62}63};64return (65<Modal66onClose={() => {}}67closeable={false}68onSubmit={sendCode}69title="User Validation Required"70buttons={71<div className="space-x-4">72<Link to="/billing">73{/* secondary button */}74<Button type="button" variant="secondary">75Subscribe to paid plan76</Button>77</Link>78<Button type="submit" disabled={!state.phoneNumberValid || state.sending}>79{"Send Code via Voice call"}80</Button>81</div>82}83visible={true}84>85<Alert type="warning" className="mt-2">86To use Gitpod for free you'll need to validate your account with your phone number. This is required87to discourage and reduce abuse on Gitpod infrastructure.88</Alert>89<Alert type="info" className="mt-4">90Alternatively, you can verify by subscribing to our paid plan.91</Alert>92<div className="text-gray-600 dark:text-gray-400 mt-2">93Enter a mobile phone number you would like to use to verify your account. If you encounter issues,94please retry later or use a different number.95</div>96{state.message ? (97<Alert type={state.message.type} className="mt-4 py-3">98{state.message.text}99</Alert>100) : (101<></>102)}103104<InputField label="Mobile Phone Number">105{/* HACK: Below we are adding a dummy dom element that is not visible, to reference the classes so they are not removed by purgeCSS. */}106<input type="tel" className="hidden intl-tel-input country-list" />107<PhoneInput108autoFocus={true}109containerClassName={"allow-dropdown w-full intl-tel-input"}110inputClassName={"w-full"}111allowDropdown={true}112defaultCountry={""}113autoHideDialCode={false}114onPhoneNumberChange={(isValid, phoneNumberRaw, countryData) => {115let phoneNumber = phoneNumberRaw;116if (!phoneNumber.startsWith("+") && !phoneNumber.startsWith("00")) {117phoneNumber = "+" + countryData.dialCode + phoneNumber;118}119setState({120...state,121phoneNumber,122phoneNumberValid: isValid,123});124}}125/>126</InputField>127</Modal>128);129} else if (!state.verified) {130const isTokenFilled = () => {131return state.token && /\d{6}/.test(state.token);132};133const verifyToken = async () => {134try {135const resp = await verificationClient.verifyPhoneNumberVerificationToken({136verificationId,137token: state.token,138phoneNumber: state.phoneNumber,139});140const verified = resp.verified;141if (verified) {142setState({143...state,144verified: true,145message: undefined,146});147} else {148setState({149...state,150message: {151type: "error",152text: `Invalid verification code.`,153},154});155}156} catch (err) {157setState({158sent: undefined,159sending: false,160message: {161type: "error",162text: err.toString(),163},164});165}166};167168const reset = () => {169setState({170...state,171sent: undefined,172message: undefined,173token: undefined,174});175};176return (177<Modal178onClose={() => {}}179closeable={false}180onSubmit={verifyToken}181title="User Validation Required"182buttons={183<div className="space-x-4">184<Link to="/billing">185{/* secondary button */}186<Button type="button" variant="secondary">187Subscribe to paid plan188</Button>189</Link>190<Button type="submit" disabled={!isTokenFilled()}>191Validate Account192</Button>193</div>194}195visible={true}196>197<Alert type="warning" className="mt-2">198To use Gitpod for free you'll need to validate your account with your phone number. This is required199to discourage and reduce abuse on Gitpod infrastructure.200</Alert>201<div className="pt-4">202<LinkButton onClick={reset}>← Use a different phone number</LinkButton>203</div>204<div className="text-gray-600 dark:text-gray-400 pt-4">205Enter the verification code we sent to {state.phoneNumber}.<br />206If you encounter issues, please retry later or use a different number.207</div>208{state.message ? (209<Alert type={state.message.type} className="mt-4 py-3">210{state.message.text}211</Alert>212) : (213<></>214)}215<TextInputField216label="Verification Code"217placeholder={"Enter code sent via phone call"}218type="text"219value={state.token}220autoFocus221onChange={(val) => {222setState({223...state,224token: val,225});226}}227/>228</Modal>229);230} else {231const continueStartWorkspace = () => {232window.location.reload();233};234return (235<Modal onClose={continueStartWorkspace} closeable={false} onSubmit={continueStartWorkspace} visible={true}>236<ModalHeader>User Validation Successful</ModalHeader>237<ModalBody>238<Alert type="success" className="mt-2">239Your account has been successfully verified.240</Alert>241</ModalBody>242<ModalFooter>243<Button type="submit">Continue</Button>244</ModalFooter>245</Modal>246);247}248}249250251