import { Box, Button, Checkbox, Input, Overlay, OverlayBackdrop, OverlayCenter, Spinner, Text, color, } from 'folds'; import React, { ChangeEventHandler, useCallback, useMemo, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { AuthDict, AuthType, IAuthData, MatrixError, RegisterRequest, UIAFlow, createClient, } from 'matrix-js-sdk'; import { PasswordInput } from '../../../components/password-input'; import { getLoginTermUrl, getUIAFlowForStages, hasStageInFlows, requiredStageInFlows, } from '../../../utils/matrix-uia'; import { useUIACompleted, useUIAFlow, useUIAParams } from '../../../hooks/useUIAFlows'; import { AsyncState, AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { useAutoDiscoveryInfo } from '../../../hooks/useAutoDiscoveryInfo'; import { RegisterError, RegisterResult, register, useRegisterComplete } from './registerUtil'; import { FieldError } from '../FiledError'; import { AutoDummyStageDialog, AutoTermsStageDialog, EmailStageDialog, ReCaptchaStageDialog, RegistrationTokenStageDialog, } from '../../../components/uia-stages'; import { useRegisterEmail } from '../../../hooks/useRegisterEmail'; import { ConfirmPasswordMatch } from '../../../components/ConfirmPasswordMatch'; import { UIAFlowOverlay } from '../../../components/UIAFlowOverlay'; import { RequestEmailTokenCallback, RequestEmailTokenResponse } from '../../../hooks/types'; export const SUPPORTED_REGISTER_STAGES = [ AuthType.RegistrationToken, AuthType.Terms, AuthType.Recaptcha, AuthType.Email, AuthType.Dummy, ]; type RegisterFormInputs = { usernameInput: HTMLInputElement; passwordInput: HTMLInputElement; confirmPasswordInput: HTMLInputElement; tokenInput?: HTMLInputElement; emailInput?: HTMLInputElement; termsInput?: HTMLInputElement; }; type FormData = { username: string; password: string; token?: string; email?: string; terms?: boolean; clientSecret: string; }; const pickStages = (uiaFlows: UIAFlow[], formData: FormData): string[] => { const pickedStages: string[] = []; if (formData.token) pickedStages.push(AuthType.RegistrationToken); if (formData.email) pickedStages.push(AuthType.Email); if (formData.terms) pickedStages.push(AuthType.Terms); if (hasStageInFlows(uiaFlows, AuthType.Recaptcha)) { pickedStages.push(AuthType.Recaptcha); } return pickedStages; }; type RegisterUIAFlowProps = { formData: FormData; flow: UIAFlow; authData: IAuthData; registerEmailState: AsyncState; registerEmail: RequestEmailTokenCallback; onRegister: (registerReqData: RegisterRequest) => void; }; function RegisterUIAFlow({ formData, flow, authData, registerEmailState, registerEmail, onRegister, }: RegisterUIAFlowProps) { const completed = useUIACompleted(authData); const { getStageToComplete } = useUIAFlow(authData, flow); const stageToComplete = getStageToComplete(); const handleAuthDict = useCallback( (authDict: AuthDict) => { const { password, username } = formData; onRegister({ auth: authDict, password, username, initial_device_display_name: 'Vojo Web', }); }, [onRegister, formData] ); const handleCancel = useCallback(() => { window.location.reload(); }, []); if (!stageToComplete) return null; return ( {stageToComplete.type === AuthType.RegistrationToken && ( )} {stageToComplete.type === AuthType.Terms && ( )} {stageToComplete.type === AuthType.Recaptcha && ( )} {stageToComplete.type === AuthType.Email && ( )} {stageToComplete.type === AuthType.Dummy && ( )} ); } type PasswordRegisterFormProps = { authData: IAuthData; uiaFlows: UIAFlow[]; defaultUsername?: string; defaultEmail?: string; defaultRegisterToken?: string; }; export function PasswordRegisterForm({ authData, uiaFlows, defaultUsername, defaultEmail, defaultRegisterToken, }: PasswordRegisterFormProps) { const { t } = useTranslation(); const serverDiscovery = useAutoDiscoveryInfo(); const baseUrl = serverDiscovery['m.homeserver'].base_url; const mx = useMemo(() => createClient({ baseUrl }), [baseUrl]); const params = useUIAParams(authData); const termUrl = getLoginTermUrl(params); const [formData, setFormData] = useState(); const [ongoingFlow, setOngoingFlow] = useState(); const [registerEmailState, registerEmail] = useRegisterEmail(mx); const [registerState, handleRegister] = useAsyncCallback< RegisterResult, MatrixError, [RegisterRequest] >(useCallback(async (registerReqData) => register(mx, registerReqData), [mx])); const [ongoingAuthData, customRegisterResp] = registerState.status === AsyncStatus.Success ? registerState.data : []; const registerError = registerState.status === AsyncStatus.Error ? registerState.error : undefined; useRegisterComplete(customRegisterResp); const handleSubmit: ChangeEventHandler = (evt) => { evt.preventDefault(); const { usernameInput, passwordInput, confirmPasswordInput, emailInput, tokenInput, termsInput, } = evt.target as HTMLFormElement & RegisterFormInputs; const token = tokenInput?.value.trim(); const username = usernameInput.value.trim(); const password = passwordInput.value; const confirmPassword = confirmPasswordInput.value; if (password !== confirmPassword) { return; } const email = emailInput?.value.trim(); const terms = termsInput?.value === 'on'; if (!username) { usernameInput.focus(); return; } const fData: FormData = { username, password, token, email, terms, clientSecret: mx.generateClientSecret(), }; const pickedStages = pickStages(uiaFlows, fData); const pickedFlow = getUIAFlowForStages(uiaFlows, pickedStages); setOngoingFlow(pickedFlow); setFormData(fData); handleRegister({ username, password, auth: { session: authData.session, }, initial_device_display_name: 'Vojo Web', }); }; return ( <> {t('Auth.register_username_label')} {registerError?.errcode === RegisterError.UserTaken && ( )} {registerError?.errcode === RegisterError.UserInvalid && ( )} {registerError?.errcode === RegisterError.UserExclusive && ( )} {(match, doMatch, passRef, confPassRef) => ( <> {t('Auth.register_password_label')} {registerError?.errcode === RegisterError.PasswordWeak && ( )} {registerError?.errcode === RegisterError.PasswordShort && ( )} {t('Auth.register_confirm_password_label')} )} {hasStageInFlows(uiaFlows, AuthType.RegistrationToken) && ( {requiredStageInFlows(uiaFlows, AuthType.RegistrationToken) ? t('Auth.register_token_label') : t('Auth.register_token_optional_label')} )} {hasStageInFlows(uiaFlows, AuthType.Email) && ( {requiredStageInFlows(uiaFlows, AuthType.Email) ? t('Auth.register_email_label') : t('Auth.register_email_optional_label')} )} {hasStageInFlows(uiaFlows, AuthType.Terms) && termUrl && ( }} /> )} {registerError?.errcode === RegisterError.RateLimited && ( )} {registerError?.errcode === RegisterError.Forbidden && ( )} {registerError?.errcode === RegisterError.InvalidRequest && ( )} {registerError?.errcode === RegisterError.Unknown && ( )} {registerState.status === AsyncStatus.Success && formData && ongoingFlow && ongoingAuthData && ( )} {registerState.status === AsyncStatus.Loading && ( }> )} ); }