import React, { useEffect, useState, useRef } from 'react'; import { Pressable, StyleSheet, View, Keyboard } from 'react-native'; import Spinner from 'react-native-loading-spinner-overlay'; import { TouchableOpacity } from 'react-native-gesture-handler'; import { useForm, Controller } from 'react-hook-form'; import { PhoneInputNumber } from '../PhoneInputNumber' import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons' import Recaptcha from 'react-native-recaptcha-that-works' import { LoginForm as LoginFormController, useLanguage, useConfig, useSession, ToastType, useToast } from 'ordering-components/native'; import { FacebookLogin } from '../FacebookLogin'; import { VerifyPhone } from '../VerifyPhone'; import { GoogleLogin } from '../GoogleLogin' import { Container, ButtonsWrapper, LoginWith, FormSide, FormInput, OTabs, OTab, SocialButtons, OrSeparator, LineSeparator, RecaptchaButton } from './styles'; import { _removeStoreData } from '../../providers/StoreUtil'; import NavBar from '../NavBar' import { OText, OButton, OInput, OModal } from '../shared'; import { LoginParams } from '../../types'; import { useTheme } from 'styled-components/native'; import { AppleLogin } from '../AppleLogin' const LoginFormUI = (props: LoginParams) => { const { loginTab, formState, navigation, useLoginByEmail, useLoginByCellphone, loginButtonText, forgotButtonText, verifyPhoneState, checkPhoneCodeState, registerButtonText, setCheckPhoneCodeState, handleButtonLoginClick, handleSendVerifyCode, handleCheckPhoneCode, onNavigationRedirect, notificationState, handleReCaptcha, enableReCaptcha } = props const theme = useTheme() const loginStyle = StyleSheet.create({ btnOutline: { backgroundColor: '#FFF', color: theme.colors.primary }, inputStyle: { marginBottom: 25, borderWidth: 1, borderColor: theme.colors.disabled }, recaptchaIcon: { width: 100, height: 100, } }); const [, { showToast }] = useToast(); const [, t] = useLanguage() const [{ configs }] = useConfig() const [, { login }] = useSession() const { control, handleSubmit, errors } = useForm(); const [passwordSee, setPasswordSee] = useState(false); const [isLoadingVerifyModal, setIsLoadingVerifyModal] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false); const [isLoadingSocialButton, setIsLoadingSocialButton] = useState(false); const [phoneInputData, setPhoneInputData] = useState({ error: '', phone: { country_phone_code: null, cellphone: null } }); const [recaptchaConfig, setRecaptchaConfig] = useState({}) const [recaptchaVerified, setRecaptchaVerified] = useState(false) const recaptchaRef = useRef({}); const inputRef = useRef({}) const googleLoginEnabled = configs?.google_login_enabled?.value === '1' || !configs?.google_login_enabled?.enabled const anySocialButtonActivated = ((configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') && configs?.facebook_id?.value) || (configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) || (configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) const handleChangeTab = (val: string) => { props.handleChangeTab(val); setPasswordSee(false); } const onSubmit = (values: any) => { Keyboard.dismiss() if (phoneInputData.error) { showToast(ToastType.Error, phoneInputData.error); return } handleButtonLoginClick({ ...values, ...phoneInputData.phone }); } const handleVerifyCodeClick = () => { if (phoneInputData.error) { showToast(ToastType.Error, phoneInputData.error); return } if ( !phoneInputData.error && !phoneInputData.phone.country_phone_code && !phoneInputData.phone.cellphone ) { showToast(ToastType.Error, t('VALIDATION_ERROR_MOBILE_PHONE_REQUIRED', 'The field Mobile phone is required.')) return } handleSendVerifyCode && handleSendVerifyCode(phoneInputData.phone) setIsLoadingVerifyModal(true) } const handleSuccessFacebook = (user: any) => { _removeStoreData('isGuestUser') login({ user, token: user.session.access_token }) } const handleSuccessApple = (user: any) => { _removeStoreData('isGuestUser') login({ user, token: user?.session?.access_token }) } const handleChangeInputEmail = (value: string, onChange: any) => { onChange(value.toLowerCase().replace(/[&,()%";:รง?<>{}\\[\]\s]/g, '')) } const handleOpenRecaptcha = () => { setRecaptchaVerified(false) if (recaptchaVerified) { handleReCaptcha && handleReCaptcha('') return } 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(token) } useEffect(() => { if (configs && Object.keys(configs).length > 0 && enableReCaptcha) { setRecaptchaConfig({ siteKey: configs?.security_recaptcha_site_key?.value || null, baseUrl: configs?.security_recaptcha_base_url?.value || null }) } }, [configs, enableReCaptcha]) useEffect(() => { if (!formState.loading && formState.result?.error) { formState.result?.result && showToast( ToastType.Error, typeof formState.result?.result === 'string' ? formState.result?.result : formState.result?.result[0] ) } }, [formState]) useEffect(() => { if (verifyPhoneState && !verifyPhoneState?.loading) { if (verifyPhoneState.result?.error) { const message = typeof verifyPhoneState?.result?.result === 'string' ? verifyPhoneState?.result?.result : verifyPhoneState?.result?.result[0] verifyPhoneState.result?.result && showToast( ToastType.Error, message ) setIsLoadingVerifyModal(false) return } const okResult = verifyPhoneState.result?.result === 'OK' if (okResult) { !isModalVisible && setIsModalVisible(true) setIsLoadingVerifyModal(false) } } }, [verifyPhoneState]) 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 = '' if (phoneInputData.error) { list.unshift({ message: phoneInputData.error }) } if ( loginTab === 'cellphone' && !phoneInputData.error && !phoneInputData.phone.country_phone_code && !phoneInputData.phone.cellphone ) { list.unshift({ message: t('VALIDATION_ERROR_MOBILE_PHONE_REQUIRED', 'The field Mobile phone is required.') }) } list.map((item: any, i: number) => { stringError += (i + 1) === list.length ? `- ${item.message}` : `- ${item.message}\n` }) showToast(ToastType.Error, stringError) } }, [errors]) return ( navigation?.canGoBack() && navigation.goBack()} showCall={false} btnStyle={{ paddingLeft: 0 }} paddingTop={0} /> {useLoginByEmail && useLoginByCellphone && ( {useLoginByEmail && ( handleChangeTab('email')}> {t('LOGIN_BY_EMAIL', 'Login by Email')} )} {useLoginByCellphone && ( handleChangeTab('cellphone')}> {t('LOGIN_BY_PHONE', 'Login by Phone')} )} )} {(useLoginByCellphone || useLoginByEmail) && ( {useLoginByEmail && loginTab === 'email' && ( ( { handleChangeInputEmail(e, onChange) }} value={value} autoCapitalize='none' autoCorrect={false} type='email-address' autoCompleteType='email' returnKeyType='next' onSubmitEditing={() => inputRef.current?.focus()} blurOnSubmit={false} /> )} 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="" /> )} {useLoginByCellphone && loginTab === 'cellphone' && ( setPhoneInputData(val)} textInputProps={{ returnKeyType: 'next', onSubmitEditing: () => inputRef?.current?.focus?.(), }} /> )} ( setPasswordSee(!passwordSee)} /> : setPasswordSee(!passwordSee)} /> } value={value} autoCompleteType='password' forwardRef={inputRef} onChange={(val: any) => onChange(val)} returnKeyType='done' onSubmitEditing={handleSubmit(onSubmit)} blurOnSubmit /> )} name="password" rules={{ required: t('VALIDATION_ERROR_PASSWORD_REQUIRED', 'The field Password is required').replace('_attribute_', t('PASSWORD', 'Password')) }} defaultValue="" /> {enableReCaptcha && ( <> {recaptchaVerified ? ( ) : ( )} {t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')} setRecaptchaVerified(false)} footerComponent={ recaptchaRef.current.close()} style={{ borderRadius: 0 }} text={t('CLOSE', 'Close')} bgColor={theme.colors.primary} borderColor={theme.colors.primary} textStyle={{ color: 'white' }} imgRightSrc={null} />} /> )} )} {onNavigationRedirect && forgotButtonText && ( onNavigationRedirect('Forgot')}> {forgotButtonText} )} {useLoginByCellphone && loginTab === 'cellphone' && configs && Object.keys(configs).length > 0 && (configs?.twilio_service_enabled?.value === 'true' || configs?.twilio_service_enabled?.value === '1') && configs?.twilio_module?.value && ( <> {t('OR', 'Or')} ) } {configs && Object.keys(configs).length > 0 && anySocialButtonActivated && ( {t('SELECT_AN_OPTION_TO_LOGIN', 'Select an option to login')} {(configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') && configs?.facebook_id?.value && ( showToast(ToastType.Error, err)} handleLoading={(val: boolean) => setIsLoadingSocialButton(val)} handleSuccessFacebookLogin={handleSuccessFacebook} /> )} {(configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled && ( showToast(ToastType.Error, err)} handleLoading={(val: boolean) => setIsLoadingSocialButton(val)} handleSuccessGoogleLogin={handleSuccessFacebook} /> )} {(configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && ( showToast(ToastType.Error, err)} handleLoading={(val: boolean) => setIsLoadingSocialButton(val)} handleSuccessApple={handleSuccessApple} /> )} )} {onNavigationRedirect && registerButtonText && ( onNavigationRedirect('Signup')} text={registerButtonText} style={loginStyle.btnOutline} borderColor={theme.colors.primary} imgRightSrc={null} /> )} setIsModalVisible(false)} entireModal title={t('VERIFY_PHONE', 'Verify Phone')} > setIsModalVisible(false)} /> ); }; export const LoginForm = (props: any) => { const loginProps = { ...props, isRecaptchaEnable: true, UIComponent: LoginFormUI, handleSuccessLogin: () => _removeStoreData('isGuestUser') }; return ; };