1 import { useEffect, useState } from 'react';
3 import { c } from 'ttag';
5 import { Button } from '@proton/atoms';
6 import type { ModalStateProps } from '@proton/components';
18 } from '@proton/components';
19 import { PLANS } from '@proton/payments';
20 import { getApiError } from '@proton/shared/lib/api/helpers/apiErrorHelper';
21 import { queryCheckEmailAvailability } from '@proton/shared/lib/api/user';
22 import { getAppHref } from '@proton/shared/lib/apps/helper';
23 import { APPS, DRIVE_APP_NAME, SSO_PATHS } from '@proton/shared/lib/constants';
24 import { DRIVE_SIGNIN } from '@proton/shared/lib/drive/urls';
25 import { API_CUSTOM_ERROR_CODES } from '@proton/shared/lib/errors';
26 import { replaceUrl } from '@proton/shared/lib/helpers/browser';
27 import { emailValidator } from '@proton/shared/lib/helpers/formValidators';
28 import { getUrlWithReturnUrl } from '@proton/shared/lib/helpers/url';
30 import usePublicToken from '../../../hooks/drive/usePublicToken';
31 import { RedirectionReason, drivePublicRedirectionReasonKey } from '../../../hooks/util/useRedirectToPublicPage';
32 import { Actions, countActionWithTelemetry, traceTelemetry } from '../../../utils/telemetry';
33 import { deleteStoredUrlPassword, saveUrlPasswordForRedirection } from '../../../utils/url/password';
36 customPassword?: string;
38 export const SignupFlowModal = ({ customPassword, onClose, ...modalProps }: Props & ModalStateProps) => {
39 const [email, setEmail] = useState('');
40 const [error, setError] = useState('');
42 const { token, urlPassword } = usePublicToken();
45 deleteStoredUrlPassword();
46 countActionWithTelemetry(Actions.ViewSignUpFlowModal);
49 const handleChangeEmail = (e: React.ChangeEvent<HTMLInputElement>) => {
50 setEmail(e.target.value);
51 setError(emailValidator(e.target.value));
55 * Sign-up flow: Redirect to /shared-with-me, then auto-add bookmarks in MainContainer.tsx
56 * Sign-in flow: Auto-add bookmarks in MainContainer.tsx and redirect back to public page
58 const handleSubmit = async () => {
60 countActionWithTelemetry(Actions.SubmitSignUpFlowModal);
61 const { Code } = await api({
62 ...queryCheckEmailAvailability(email),
63 silence: [API_CUSTOM_ERROR_CODES.ALREADY_USED, API_CUSTOM_ERROR_CODES.NOT_ALLOWED],
65 // Email is verified and available to use
66 // We redirect to DRIVE_SIGNUP
67 // If user pass proton domain we redirect him to signin page in all case
69 saveUrlPasswordForRedirection(urlPassword + (customPassword ?? ''));
71 countActionWithTelemetry(Actions.SignUpFlowModal),
72 traceTelemetry(Actions.SignUpFlowAndRedirectCompleted).start(),
74 const returnUrlSearchParams = new URLSearchParams();
75 returnUrlSearchParams.append('token', token);
76 const returnUrl = `/shared-with-me?`.concat(returnUrlSearchParams.toString());
78 const url = new URL(getAppHref(SSO_PATHS.SIGNUP, APPS.PROTONACCOUNT));
79 // This autofill the sign-up email input
80 url.searchParams.append('email', email);
81 url.searchParams.append('plan', PLANS.FREE);
82 url.searchParams.append('product', 'drive');
84 getUrlWithReturnUrl(url.toString(), {
91 const { code, message } = getApiError(err);
92 // Email is already in use or if user pass proton domain we redirect him to, we redirect to SIGN_IN
93 if (API_CUSTOM_ERROR_CODES.ALREADY_USED === code || API_CUSTOM_ERROR_CODES.NOT_ALLOWED === code) {
94 saveUrlPasswordForRedirection(urlPassword + (customPassword ?? ''));
95 await countActionWithTelemetry(Actions.SignInFlowModal);
96 const returnUrlSearchParams = new URLSearchParams();
97 returnUrlSearchParams.append('token', token);
98 returnUrlSearchParams.append(drivePublicRedirectionReasonKey, RedirectionReason.SIGNIN);
99 // Always return to public page in case of signin. This will be done in MainContainer.tsx on page loading
100 const returnUrl = `/?`.concat(returnUrlSearchParams.toString());
101 const url = new URL(DRIVE_SIGNIN);
102 // This autofill the sign-in email input
103 url.searchParams.append('username', email);
104 url.searchParams.append('product', 'drive');
106 getUrlWithReturnUrl(url.toString(), {
113 // Other errors we show the error message
114 setError(message || c('Error').t`Email is not valid`);
121 onSubmit={handleSubmit}
122 enableCloseWhenClickOutside
124 countActionWithTelemetry(Actions.DismissSignUpFlowModal);
129 data-testid="download-page-sign-in"
133 <div className="flex flex-column mb-1">
134 <DriveLogo variant="glyph-only" size={12} />
135 {c('Title').t`Save it for later in ${DRIVE_APP_NAME}`}
138 subline={c('Info').t`Keep your files secure with end-to-end encryption.`}
140 <ModalTwoContent className="mt-4">
142 data-testid="public-share-signup-modal-email"
143 id="public-share-signup-modal-email"
144 label={c('Label').t`Log in or sign up `}
145 onChange={handleChangeEmail}
146 placeholder={c('Placeholder').t`Email`}
151 <span className="flex items-center gap-1">
152 <Icon className="color-primary" name="gift" />
153 {c('Info').t`Free 5GB encrypted storage to get started`}
156 <ModalTwoFooter className="flex flex-column items-center">
157 <Button color="norm" size="large" type="submit" fullWidth={true}>{c('Action').t`Continue`}</Button>
163 export const useSignupFlowModal = () => {
164 return useModalTwoStatic(SignupFlowModal);