import {RegisterProcess, ThemeMode} from '@caj/configs/appVar'; import {appVarActions} from '@caj/configs/appVarReducer'; import {defaultHeaderStyle} from '@caj/configs/colors'; import {SlideFromLeftView} from '@caj/helper/animations'; import {saveVarChanges} from '@caj/helper/appData'; import {apiBackendRequest, makeRequest} from '@caj/helper/request'; import { accountNameOptions, EMail, emailOptions, passwordOptions, userNameOptions, XToken, } from '@caj/helper/types'; import {RootScreenNavigationProp} from '@caj/Navigation'; import {RootState, store} from '@caj/redux/store'; import {useNavigation} from '@react-navigation/native'; import { createNativeStackNavigator, NativeStackNavigationProp, } from '@react-navigation/native-stack'; import { Box, Button, Center, Container, FormControl, Heading, HStack, Icon, IconButton, Input, Pressable, ScrollView, Spinner, Text, useColorModeValue, useTheme, useToast, VStack, WarningOutlineIcon, } from 'native-base'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; import {baseFontSize} from 'native-base/lib/typescript/theme/tools'; import {useEffect, useRef, useState} from 'react'; import {useDispatch, useSelector} from 'react-redux'; import reactStringReplace from 'react-string-replace'; import ConfirmationCodeField from './ConfirmationCodeField'; import NameDisplay from './NameDisplay'; import showToast from './Toast'; import {NativeSyntheticEvent, TextInputFocusEventData} from 'react-native'; const validateEmail = (email: EMail) => { return emailOptions.isAllowed(email); }; export default function NotLoggedIn() { const lang = useSelector((state: RootState) => state.appVariables.lang); const theme = useSelector( (state: RootState) => state.appVariables.preferences.theme, ); const toast = useToast(); const navigation = useNavigation(); return ( {lang.appName} {lang.appNameDesc} ); } export function RegisterScreenAnim(props: any) { return ( ); } export type LoginStackNavigatorParamList = { RegStepOne: undefined; RegStepTwo: undefined; RegStepFinal: undefined; }; const LoginStack = createNativeStackNavigator(); export type LoginScreenNavigationProp = NativeStackNavigationProp; function RegisterScreen() { const lang = useSelector((state: RootState) => state.appVariables.lang); const theme = useSelector( (state: RootState) => state.appVariables.preferences.theme, ); return ( ); } function Agreement() { const toast = useToast(); const lang = useSelector((state: RootState) => state.appVariables.lang); const textColor = useColorModeValue('blue.700', 'cyan.400'); let replacedText = reactStringReplace( lang.account.registration.info, '${TermsOfUse}', (match, i) => ( { showToast(toast, { title: lang.account.registration.termsOfUse, variant: 'solid', status: 'info', description: undefined, isClosable: true, rest: {colorScheme: 'primary'}, }); }}> {lang.account.registration.termsOfUse} ), ); replacedText = reactStringReplace( replacedText, '${privacyPolicy}', (match, i) => ( { showToast(toast, { title: lang.account.registration.privacyPolicy, variant: 'solid', status: 'info', description: undefined, isClosable: true, rest: {colorScheme: 'primary'}, }); }}> {lang.account.registration.privacyPolicy} ), ); return ( {replacedText} ); } function resendMail(email: EMail, toast: any): Promise { return new Promise((resolve, reject) => { makeRequest({ path: apiBackendRequest.REGISTER_RESEND_MAIL, requestHeader: {}, request: { Email: email, }, //response: { // XToken: undefined, //}, }) .then(resp => { console.log(1); let token = store.getState().appVariables.preferences.RegisterProcess.XToken; if (token !== undefined /*resp.response.XToken !== undefined*/) { showToast(toast, { title: store.getState().appVariables.lang.info, variant: 'solid', status: 'info', description: store.getState().appVariables.lang.account.registration.stepTwo .resend[2], isClosable: true, rest: {}, }); resolve(token); } else { reject(500); showToast(toast, { title: store.getState().appVariables.lang.error, variant: 'solid', status: 'error', description: 'XToken is undefined', isClosable: true, rest: {}, }); } }) .catch(resp => { let text = 'unknown error ' + resp.status; if (resp.status !== undefined) { const _text = store.getState().appVariables.lang.account.registration.stepTwo .resendError[resp.status as number]; if (_text !== undefined) text = _text; } showToast(toast, { title: store.getState().appVariables.lang.error, variant: 'solid', status: 'error', description: text, isClosable: true, rest: {}, }); reject(resp.status); }); }); } function StepOne() { const lang = useSelector((state: RootState) => state.appVariables.lang); const regPro = useSelector( (state: RootState) => state.appVariables.preferences.RegisterProcess, ); const dispatch = useDispatch(); const toast = useToast(); const navigation = useNavigation(); const initNoErrors = { wrongFormat: false, alreadyExists: false, noEntered: false, unknown: undefined, }; const [errors, setErrors] = useState(initNoErrors); const isError = errors.wrongFormat || errors.alreadyExists || errors.noEntered || errors.unknown !== undefined; const errorText = () => { if (errors.wrongFormat) { return lang.account.registration.stepOne.addressInvalid; } if (errors.alreadyExists) { return lang.account.registration.stepOne.addressExists; } if (errors.noEntered) { return lang.account.registration.stepOne.noMailEntered; } if (errors.unknown !== undefined) { return errors.unknown; } }; const [isLoading, setLoading] = useState(false); const [values, setValues] = useState({email: regPro.EMail}); useEffect(() => { if (regPro.isRegistering === 'stepTwo') { setLoading(true); setErrors(initNoErrors); setTimeout(nextStep, 500); } else if (regPro.isRegistering === 'stepFinal') { setLoading(true); setErrors(initNoErrors); setTimeout(nextStep, 500); } }, []); const nextStep = () => { setLoading(true); setErrors(initNoErrors); makeRequest({ path: apiBackendRequest.REGISTER_STEP_1, requestHeader: {}, request: { Email: values.email, }, response: { XToken: undefined, }, }) .then(resp => { let rp = {...regPro}; rp.isRegistering = 'stepTwo'; rp.EMail = values.email; rp.XToken = resp.response.XToken; dispatch(appVarActions.setRegisterProcess(rp)); saveVarChanges(); showToast(toast, { title: lang.account.registration.stepOne.success, variant: 'solid', status: 'success', description: undefined, isClosable: true, rest: {colorScheme: 'primary'}, }); navigation.navigate('Register', {screen: 'RegStepTwo'}); setLoading(false); }) .catch(resp => { if (resp.status === 401 || resp.status === 204) { if (regPro.XToken !== undefined) { let rp = {...regPro}; if (resp.status === 401) { rp.isRegistering = 'stepTwo'; rp.EMail = values.email; dispatch(appVarActions.setRegisterProcess(rp)); saveVarChanges(); navigation.navigate('Register', {screen: 'RegStepTwo'}); } else if (resp.status === 204) { rp.isRegistering = 'stepFinal'; rp.EMail = values.email; dispatch(appVarActions.setRegisterProcess(rp)); saveVarChanges(); navigation.navigate('Register', {screen: 'RegStepFinal'}); } setLoading(false); } else { resendMail(values.email, toast) .then(() => { setLoading(false); }) .catch(() => { setLoading(false); }); } } else { showToast(toast, { title: 'Error', variant: 'solid', status: 'error', description: resp.status, isClosable: true, rest: {colorScheme: 'primary'}, }); setLoading(false); } }); }; return ( {lang.account.registration.stepOne.title} { const mail = text.replaceAll(' ', ''); setValues({email: mail}); if (errors.noEntered && mail !== '') { let err = errors; err.noEntered = false; setErrors({...err}); } if (errors.wrongFormat && validateEmail(mail)) { let err = errors; err.wrongFormat = false; setErrors({...err}); } if (errors.alreadyExists) { let err = errors; err.alreadyExists = false; setErrors({...err}); } }} /> }> {errorText()} ); } function StepTwo() { const lang = useSelector((state: RootState) => state.appVariables.lang); const regPro = useSelector( (state: RootState) => state.appVariables.preferences.RegisterProcess, ); const dispatch = useDispatch(); const cellCount = 6; const {colors} = useTheme(); const toast = useToast(); const navigation = useNavigation(); const initNoErrors = { noEntered: false, }; const [errors, setErrors] = useState(initNoErrors); const [isLoading, setLoading] = useState(false); const [values, setValues] = useState({code: ''}); const headerText = () => { return reactStringReplace( lang.account.registration.stepTwo.title, '${EMail}', (match, i) => ( {regPro.EMail} ), ); }; const resendText = () => { return reactStringReplace( lang.account.registration.stepTwo.resend[0], '${resend}', (match, i) => ( { setLoading(true); resendMail( store.getState().appVariables.preferences.RegisterProcess.EMail, toast, ) .then(() => { setLoading(false); }) .catch(() => { setLoading(false); }); }}> {lang.account.registration.stepTwo.resend[1]} ), ); }; const validate = (text: string) => { setLoading(true); setTimeout(() => { makeRequest({ path: apiBackendRequest.REGISTER_STEP_2, requestHeader: {}, requestGET: {':verifyId': text, ':xToken': regPro.XToken || ''}, }) .then(resp => { let rp = {...regPro}; rp.isRegistering = 'stepFinal'; dispatch(appVarActions.setRegisterProcess(rp)); saveVarChanges(); showToast(toast, { title: lang.account.registration.stepTwo.success, variant: 'solid', status: 'success', description: undefined, isClosable: true, rest: {colorScheme: 'primary'}, }); navigation.navigate('Register', {screen: 'RegStepFinal'}); setLoading(false); }) .catch(resp => { let text = 'unknown error ' + resp.status; if (resp.status !== undefined) { const _text = lang.account.registration.stepTwo.verificationError[ resp.status as number ]; if (_text !== undefined) text = _text; } showToast(toast, { title: lang.error, variant: 'solid', status: 'error', description: text, isClosable: true, rest: {}, }); setLoading(false); if (resp.status === 422) { let rp = {...regPro}; rp.isRegistering = false; dispatch(appVarActions.setRegisterProcess(rp)); saveVarChanges(); navigation.navigate('Register', {screen: 'RegStepOne'}); } }); }, 500); }; return (
{headerText()} { setValues({code: text}); setErrors(initNoErrors); }} onFinish={(text: string) => validate(text)} /> }> {lang.account.registration.stepTwo.noCodeEntered}
{resendText()}
); } function StepFinal() { const lang = useSelector((state: RootState) => state.appVariables.lang); const regPro = useSelector( (state: RootState) => state.appVariables.preferences.RegisterProcess, ); const dispatch = useDispatch(); const {colors} = useTheme(); const toast = useToast(); const navigation = useNavigation(); const [isLoading, setLoading] = useState(false); const [showPassword, setShowPassword] = useState(false); interface inputElementType { label: string; input: string; autoCapitalize: 'none' | 'words'; errorIndex: 'none' | string; errorTextObject: any; isPassword: boolean | 'passwordRepeat'; minLength: number; maxLength: number; onTextChange: any; textChangeTimeout: number; isAllowed: any; } const accountNameRef = useRef(setTimeout(() => {})); const accountNameFetchRef = useRef(setTimeout(() => {})); const accountName = { label: lang.account.registration.stepFinal.accountName, input: '', errorIndex: 'none', errorTextObject: lang.account.registration.stepFinal.accountNameError, minLength: accountNameOptions.minLength, maxLength: accountNameOptions.maxLength, isAllowed: accountNameOptions.isAllowed, isPassword: false, onTextChange: (e: NativeSyntheticEvent) => { let text = e.nativeEvent.text; let self = accountName; clearTimeout(accountNameRef.current); let obj = {...valuesAccountName}; accountNameRef.current = setTimeout(() => { obj.input = text; if (text.length < self.minLength) obj.errorIndex = 'tooShort'; else if (text.length > self.maxLength) obj.errorIndex = 'tooLong'; else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid'; else obj.errorIndex = 'none'; setValuesAccountName(obj); }, 50); clearTimeout(accountNameFetchRef.current); accountNameFetchRef.current = setTimeout(() => { console.log(obj); if (obj.errorIndex === 'none') { makeRequest({ path: apiBackendRequest.REGISTER_STEP_FINAL_ACCOUNT_NAME_CHECK, request: {AccountName: obj.input}, }) .then(resp => { console.log('OK'); }) .catch(resp => { if (resp.status !== undefined) { obj.errorIndex = resp.status; setValuesAccountName(obj); } }); } }, 750); }, } as inputElementType; const [valuesAccountName, setValuesAccountName] = useState(accountName); const userNameRef = useRef(setTimeout(() => {})); const userName = { label: lang.account.registration.stepFinal.userName, input: '', errorIndex: 'none', errorTextObject: lang.account.registration.stepFinal.userNameError, minLength: userNameOptions.minLength, maxLength: userNameOptions.maxLength, isAllowed: userNameOptions.isAllowed, isPassword: false, onTextChange: (e: NativeSyntheticEvent) => { let text = e.nativeEvent.text; let self = userName; clearTimeout(userNameRef.current); userNameRef.current = setTimeout(() => { let obj = {...valuesUserName}; obj.input = text; if (text.length < self.minLength) obj.errorIndex = 'tooShort'; else if (text.length > self.maxLength) obj.errorIndex = 'tooLong'; else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid'; else obj.errorIndex = 'none'; setValuesUserName(obj); }, 50); }, } as inputElementType; const [valuesUserName, setValuesUserName] = useState(userName); const passwordRef = useRef(setTimeout(() => {})); const password = { label: lang.account.registration.stepFinal.password, input: '', errorIndex: 'none', errorTextObject: lang.account.registration.stepFinal.passwordError, minLength: passwordOptions.minLength, maxLength: passwordOptions.maxLength, isAllowed: passwordOptions.isAllowed, isPassword: true, onTextChange: (e: NativeSyntheticEvent) => { let text = e.nativeEvent.text; let self = password; clearTimeout(passwordRef.current); passwordRef.current = setTimeout(() => { let obj = {...valuesPassword}; obj.input = text; if (text.length < self.minLength) obj.errorIndex = 'tooShort'; else if (text.length > self.maxLength) obj.errorIndex = 'tooLong'; else if (self.isAllowed(text) === false) obj.errorIndex = 'invalid'; else obj.errorIndex = 'none'; setValuesPassword(obj); let objRe = {...valuesPasswordRe}; if (text !== valuesPasswordRe.input) objRe.errorIndex = 'noMatch'; else objRe.errorIndex = 'none'; setValuesPasswordRe(objRe); }, 50); }, } as inputElementType; const [valuesPassword, setValuesPassword] = useState(password); const passwordReRef = useRef(setTimeout(() => {})); const passwordRe = { label: lang.account.registration.stepFinal.passwordRepeat, input: '', errorIndex: 'none', errorTextObject: lang.account.registration.stepFinal.passwordError, minLength: passwordOptions.minLength, maxLength: passwordOptions.maxLength, isAllowed: passwordOptions.isAllowed, isPassword: 'passwordRepeat', onTextChange: (e: NativeSyntheticEvent) => { let text = e.nativeEvent.text; let self = passwordRe; clearTimeout(passwordReRef.current); passwordReRef.current = setTimeout(() => { let obj = {...valuesPasswordRe}; obj.input = text; console.log(text, valuesPassword.input); if (text !== valuesPassword.input) obj.errorIndex = 'noMatch'; else obj.errorIndex = 'none'; setValuesPasswordRe(obj); }, 50); }, } as inputElementType; const [valuesPasswordRe, setValuesPasswordRe] = useState(passwordRe); const inputElement = ( val: inputElementType, set: React.Dispatch>, valConst: inputElementType, autofocus?: boolean, ) => { const isPassword = val.isPassword === true || val.isPassword === 'passwordRepeat'; return ( {val.label} setShowPassword(!showPassword)} icon={ } borderRadius="full" /> ) : undefined } /> }> {val.errorTextObject[val.errorIndex] !== undefined ? val.errorTextObject[val.errorIndex] .replaceAll('$(minLength)', val.minLength) .replaceAll('$(maxLength)', val.maxLength) : val.errorTextObject[val.errorIndex] !== 'none' ? lang.error : null} ); }; return (
{lang.account.registration.stepFinal.displayName}
{inputElement(valuesUserName, setValuesUserName, userName)} {inputElement(valuesAccountName, setValuesAccountName, accountName)} {inputElement(valuesPassword, setValuesPassword, password)} {inputElement(valuesPasswordRe, setValuesPasswordRe, passwordRe)}
); }