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