import React, { useEffect, useState, useRef } from 'react'; import { StyleSheet, View, ScrollView, Dimensions, Pressable } from 'react-native'; import { useForm, Controller } from 'react-hook-form'; import { useTheme } from 'styled-components/native'; import { TouchableOpacity } from 'react-native-gesture-handler'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import Recaptcha from 'react-native-recaptcha-that-works' import ReCaptcha from '@fatnlazycat/react-native-recaptcha-v3' import { LoginForm as LoginFormController, useConfig, useLanguage, ToastType, useToast, useApi } from 'ordering-components/native'; import { LoginWith, TabsContainer, WelcomeTextContainer, LogoWrapper, RecaptchaButton, } from './styles'; import { OText, OButton, OInput, OIcon, OModal } from '../shared'; import { LoginParams } from '../../types'; import { LANDSCAPE, PORTRAIT, useDeviceOrientation } from '../../../../../src/hooks/DeviceOrientation'; import { _setStoreData } from '../../../../../src/providers/StoreUtil' import { Otp } from './Otp' import Alert from '../../../../../src/providers/AlertProvider' import { PhoneInputNumber } from '../PhoneInputNumber' const LoginFormUI = (props: LoginParams) => { const { loginButtonText, formState, handleButtonLoginClick, useRootPoint, handleReCaptcha, enableReCaptcha, checkPhoneCodeState, useLoginByCellphone, useLoginByEmail, loginTab, otpType, setOtpType, generateOtpCode, useLoginOtpEmail, useLoginOtpCellphone, } = props; const theme = useTheme() const [{ configs }, { refreshConfigs }] = useConfig() const [ordering, { setOrdering }] = useApi(); const [, { showToast }] = useToast(); const [, t] = useLanguage(); const [recaptchaConfig, setRecaptchaConfig] = useState({}) const [recaptchaVerified, setRecaptchaVerified] = useState(false) const recaptchaRef = useRef({}); const { control, handleSubmit, formState: { errors }, clearErrors } = useForm(); const [orientationState] = useDeviceOrientation(); const [formsStateValues, setFormsStateValues] = useState({ isSubmitted: false }) const scrollRefTab = useRef() as React.MutableRefObject; const inputRef = useRef(null); const [windowWidth, setWindowWidth] = useState( parseInt(parseFloat(String(Dimensions.get('window').width)).toFixed(0)), ); const [projectName, setProjectName] = useState(''); const [isLoadingVerifyModal, setIsLoadingVerifyModal] = useState(false); const [willVerifyOtpState, setWillVerifyOtpState] = useState(false) const [alertState, setAlertState] = useState({ open: false, title: '', content: [] }) const [phoneInputData, setPhoneInputData] = useState({ error: '', phone: { country_phone_code: null, cellphone: null, }, }); const isOtpEmail = loginTab === 'otp' && otpType === 'email' const isOtpCellphone = loginTab === 'otp' && otpType === 'cellphone' const isDeviceLoginEnabled = configs?.device_code_login_enabled?.value === '1' const mainLogin = (values) => { if (loginTab === 'otp') { if (phoneInputData.error && (loginTab !== 'otp' || (otpType === 'cellphone' && loginTab === 'otp'))) { showToast(ToastType.Error, t('INVALID_PHONE_NUMBER', 'Invalid phone number')); return } if (loginTab === 'otp') { generateOtpCode({ ...values, ...phoneInputData.phone }) } setWillVerifyOtpState(true) } else { if (phoneInputData.error) { showToast(ToastType.Error, phoneInputData.error); return; } handleButtonLoginClick({ ...values, ...phoneInputData.phone, }); } } const onSubmit = (values: any) => { if (phoneInputData.error) { showToast(ToastType.Error, phoneInputData.error); return; } if (values?.project_name) { _setStoreData('project_name', values?.project_name) setFormsStateValues({ ...formsStateValues, isSubmitted: true, values }) return } mainLogin(values) }; const handleChangeInputEmail = (value: string, onChange: any) => { onChange(value.toLowerCase().replace(/[&,()%";:รง?<>{}\\[\]\s]/g, '')); }; const handleOpenRecaptcha = () => { setRecaptchaVerified(false) if (!recaptchaConfig?.siteKey) { showToast(ToastType.Error, t('NO_RECAPTCHA_SITE_KEY', 'The config doesn\'t have recaptcha site key')); return } if (!recaptchaConfig?.baseUrl) { showToast(ToastType.Error, t('NO_RECAPTCHA_BASE_URL', 'The config doesn\'t have recaptcha base url')); return } recaptchaRef.current.open() } const onRecaptchaVerify = (token: any) => { setRecaptchaVerified(true) handleReCaptcha && handleReCaptcha({ code: token, version: recaptchaConfig?.version }) } const styles = StyleSheet.create({ logo: { height: 80, width: 250, marginTop: 10 }, welcomeTextB: { marginBottom: 5 }, inputStyle: { borderRadius: 4, marginBottom: 12, borderWidth: 1, borderColor: theme.colors.disabled, minHeight: 44, maxHeight: 44 }, forgotStyle: { textAlign: 'center', fontWeight: '600', color: theme.colors.skyBlue, marginTop: orientationState?.dimensions?.height * 0.03, }, btn: { borderRadius: 7.6, height: 44, }, btnTab: { flex: 1, minWidth: 88, alignItems: 'center', }, btnTabText: { fontFamily: 'Poppins', fontStyle: 'normal', fontSize: 16, marginBottom: 10, paddingLeft: 8, paddingRight: 8, }, btnFlag: { width: 79, borderWidth: 1, borderRadius: 7.6, marginRight: 9, borderColor: theme.colors.inputSignup, }, borderStyleBase: { width: 30, height: 45 }, borderStyleHighLighted: { borderColor: "#03DAC6", }, underlineStyleBase: { width: 45, height: 60, borderWidth: 1, fontSize: 16 }, underlineStyleHighLighted: { borderColor: theme.colors.primary, color: theme.colors.primary, fontSize: 16 }, }); useEffect(() => { if (!formState.loading && formState.result?.error) { if (formState.result?.result?.[0] === 'ERROR_AUTH_VERIFICATION_CODE') { setRecaptchaVerified(false) setRecaptchaConfig({ version: 'v2', siteKey: configs?.security_recaptcha_site_key?.value || null, baseUrl: configs?.security_recaptcha_base_url?.value || null }) showToast(ToastType.Info, t('TRY_AGAIN', 'Please try again')) setFormsStateValues({ ...formsStateValues, isSubmitted: false, }) return } formState.result?.result && showToast( ToastType.Error, typeof formState.result?.result === 'string' ? formState.result?.result : formState.result?.result[0] ) setFormsStateValues({ ...formsStateValues, isSubmitted: false, }) } }, [formState]); useEffect(() => { if (ordering.project === null || !formsStateValues.isSubmitted || !useRootPoint) return const values: any = formsStateValues.values if (values?.project_name) { delete values.project_name } mainLogin(values) setFormsStateValues({ ...formsStateValues, isSubmitted: false, }) }, [ordering, formsStateValues.isSubmitted]) useEffect(() => { if (Object.keys(errors)?.length > 0) { // Convert all errors in one string to show in toast provider const list = Object.values(errors); let stringError = ''; list.map((item: any, i: number) => { stringError += (i + 1) === list.length ? `- ${item.message}` : `- ${item.message}\n` }); showToast(ToastType.Error, stringError); } }, [errors]); useEffect(() => { if (configs && Object.keys(configs).length > 0 && enableReCaptcha) { if (configs?.security_recaptcha_type?.value === 'v3' && configs?.security_recaptcha_score_v3?.value > 0 && configs?.security_recaptcha_site_key_v3?.value ) { setRecaptchaConfig({ version: 'v3', siteKey: configs?.security_recaptcha_site_key_v3?.value || null, baseUrl: configs?.security_recaptcha_base_url?.value || null }) return } if (configs?.security_recaptcha_site_key?.value) { setRecaptchaConfig({ version: 'v2', siteKey: configs?.security_recaptcha_site_key?.value || null, baseUrl: configs?.security_recaptcha_base_url?.value || null }) } } }, [configs, enableReCaptcha]) const handleChangeTab = (val: string) => { setPhoneInputData({ ...phoneInputData, error: '' }); clearErrors([val]); props.handleChangeTab(val); if (loginTab === 'email') { scrollRefTab.current?.scrollToEnd({ animated: true }); } if (loginTab === 'cellphone') { scrollRefTab.current?.scrollTo({ animated: true }); } }; const handleChangeOtpType = (type: string) => { handleChangeTab('otp', type) setOtpType(type) } const handleLoginOtp = (code: string) => { handleButtonLoginClick({ code }) setWillVerifyOtpState(false) } const closeAlert = () => { setAlertState({ open: false, title: '', content: [] }) } useEffect(() => { if (checkPhoneCodeState?.result?.error) { setAlertState({ open: true, content: t(checkPhoneCodeState?.result?.error, checkPhoneCodeState?.result?.error), title: '' }) } }, [checkPhoneCodeState]) useEffect(() => { const projectInputTimeout = setTimeout(() => { if (projectName && useRootPoint) { setOrdering({ ...ordering, project: projectName }) } }, 1500) return () => clearTimeout(projectInputTimeout); }, [projectName, isDeviceLoginEnabled]) useEffect(() => { if (ordering?.project) { refreshConfigs() } }, [ordering?.project]) const logo = ( ); const welcome = ( {t('WELCOME_TEXT_A', 'Hi There!')} {t('WELCOME_TEXT_B', 'Login To start')} {t('WELCOME_TEXT_C', 'You just need to login once.')} ); const note = ( useRootPoint && ( {t('IF_NOT_HAVE_ACCOUNT', 'If you don\'t have and account, please contact Ordering')}  {t('SUPPORT_DEPARTMENT', 'support department')} ) ) return ( {orientationState?.orientation === PORTRAIT && ( {logo} )} {welcome} {orientationState?.orientation === LANDSCAPE && ( {note} )} {orientationState?.orientation === LANDSCAPE && ( {logo} )} {!isDeviceLoginEnabled && (Number(useLoginByEmail) + Number(useLoginOtpEmail) + Number(useLoginOtpCellphone) > 1) && ( {useLoginByEmail && ( handleChangeTab('email')}> {t('BY_EMAIL', 'by Email')} )} {useLoginByCellphone && ( handleChangeTab('cellphone')}> {t('BY_PHONE', 'by Phone')} )} {useLoginOtpEmail && ( handleChangeOtpType('email')}> {t('BY_OTP_EMAIL', 'By Otp Email')} )} {useLoginOtpCellphone && ( handleChangeOtpType('cellphone')}> {t('BY_OTP_PHONE', 'By Otp Phone')} )} )} {useRootPoint && ( ( { setProjectName(e?.target?.value) onChange(e?.target?.value); setFormsStateValues({ ...formsStateValues, isSubmitted: false, }) }} /> )} /> )} {isDeviceLoginEnabled && ( ( onChange(val)} /> )} name="device_code" rules={{ required: t( 'VALIDATION_ERROR_DEVICE_CODE_REQUIRED', 'The field DEVICE_CODE is required', ).replace('_attribute_', t('DEVICE_CODE', 'Device Code')), }} defaultValue="" /> )} {!isDeviceLoginEnabled && ((useLoginByEmail && loginTab === 'email') || (loginTab === 'otp' && otpType === 'email')) && ( ( { handleChangeInputEmail(e, onChange); }} /> )} name="email" rules={{ required: t( 'VALIDATION_ERROR_EMAIL_REQUIRED', 'The field Email is required', ).replace('_attribute_', t('EMAIL', 'Email')), pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, message: t( 'INVALID_ERROR_EMAIL', 'Invalid email address', ).replace('_attribute_', t('EMAIL', 'Email')), }, }} defaultValue="" /> )} {!isDeviceLoginEnabled && ((useLoginByCellphone && loginTab === 'cellphone') || (loginTab === 'otp' && otpType === 'cellphone')) && ( setPhoneInputData(val)} onSubmitEditing={() => null} textInputProps={{ returnKeyType: 'next', onSubmitEditing: () => inputRef?.current?.focus?.(), }} /> )} {!isDeviceLoginEnabled && loginTab !== 'otp' && ( ( onChange(val)} inputStyle={{ textAlign: 'center' }} /> )} name="password" rules={{ required: t( 'VALIDATION_ERROR_PASSWORD_REQUIRED', 'The field Password is required', ).replace('_attribute_', t('PASSWORD', 'Password')), }} defaultValue="" forwardRef={inputRef} /> )} {(recaptchaConfig?.version) && ( <> {recaptchaConfig?.version === 'v3' ? ( ) : ( <> {recaptchaVerified ? ( ) : ( )} {t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')} setRecaptchaVerified(false)} /> ) } )} {orientationState?.orientation === PORTRAIT && ( {note} )} setWillVerifyOtpState(false)} entireModal title={t('ENTER_VERIFICATION_CODE', 'Enter verification code')} > ); }; export const LoginForm = (props: any) => { const loginProps = { ...props, UIComponent: LoginFormUI, isRecaptchaEnable: true }; return ; };