ordering-ui-react-native 0.25.1 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,31 +9,31 @@ import ReCaptcha from '@fatnlazycat/react-native-recaptcha-v3'
9
9
  import ReactNativeHapticFeedback from "react-native-haptic-feedback";
10
10
 
11
11
  import {
12
- LoginForm as LoginFormController,
13
- useLanguage,
14
- useConfig,
15
- useSession,
16
- ToastType,
17
- useToast,
12
+ LoginForm as LoginFormController,
13
+ useLanguage,
14
+ useConfig,
15
+ useSession,
16
+ ToastType,
17
+ useToast,
18
18
  } from 'ordering-components/native';
19
19
  import { useTheme } from 'styled-components/native';
20
20
  import { FacebookLogin } from '../FacebookLogin';
21
21
  import { VerifyPhone } from '../../../../../src/components/VerifyPhone';
22
22
  import { OModal } from '../../../../../src/components/shared';
23
23
  import {
24
- Container,
25
- ButtonsWrapper,
26
- LoginWith,
27
- FormSide,
28
- FormInput,
29
- OTabs,
30
- OTab,
31
- SocialButtons,
32
- OrSeparator,
33
- LineSeparator,
34
- SkeletonWrapper,
35
- TabBtn,
36
- RecaptchaButton
24
+ Container,
25
+ ButtonsWrapper,
26
+ LoginWith,
27
+ FormSide,
28
+ FormInput,
29
+ OTabs,
30
+ OTab,
31
+ SocialButtons,
32
+ OrSeparator,
33
+ LineSeparator,
34
+ SkeletonWrapper,
35
+ TabBtn,
36
+ RecaptchaButton
37
37
  } from './styles';
38
38
 
39
39
  import NavBar from '../NavBar';
@@ -48,846 +48,871 @@ import { TouchableOpacity } from 'react-native-gesture-handler';
48
48
  import Alert from '../../../../../src/providers/AlertProvider'
49
49
 
50
50
  const LoginFormUI = (props: LoginParams) => {
51
- const {
52
- loginTab,
53
- formState,
54
- navigation,
55
- useLoginByEmail,
56
- useLoginByCellphone,
57
- useLoginOtp,
58
- loginButtonText,
59
- forgotButtonText,
60
- verifyPhoneState,
61
- checkPhoneCodeState,
62
- registerButtonText,
63
- setCheckPhoneCodeState,
64
- handleButtonLoginClick,
65
- handleSendVerifyCode,
66
- handleCheckPhoneCode,
67
- onNavigationRedirect,
68
- notificationState,
69
- handleReCaptcha,
70
- enableReCaptcha,
71
- otpType,
72
- setOtpType,
73
- generateOtpCode,
74
- useLoginOtpEmail,
75
- useLoginOtpCellphone,
76
- isGuest,
77
- setCellphoneStartZero
78
- } = props;
79
-
80
- const [, { showToast }] = useToast();
81
- const [, t] = useLanguage();
82
- const [{ configs }] = useConfig();
83
- const [, { login }] = useSession();
84
- const { control, handleSubmit, errors, reset, register, setValue } = useForm();
85
- const [passwordSee, setPasswordSee] = useState(false);
86
- const [isLoadingVerifyModal, setIsLoadingVerifyModal] = useState(false);
87
- const [isModalVisible, setIsModalVisible] = useState(false);
88
- const [isFBLoading, setIsFBLoading] = useState(false);
89
- const [willVerifyOtpState, setWillVerifyOtpState] = useState(false)
90
- const [phoneInputData, setPhoneInputData] = useState({
91
- error: '',
92
- phone: {
93
- country_phone_code: null,
94
- cellphone: null,
95
- },
96
- });
97
- const [recaptchaConfig, setRecaptchaConfig] = useState<any>({})
98
- const [recaptchaVerified, setRecaptchaVerified] = useState(false)
99
- const [alertState, setAlertState] = useState({ open: false, title: '', content: [] })
100
- const [tabLayouts, setTabLayouts] = useState<any>({})
101
- const [otpError, setOtpError] = useState(null)
102
- const tabsRef = useRef<any>(null)
103
- const enabledPoweredByOrdering = configs?.powered_by_ordering_module?.value
104
- const theme = useTheme();
105
- const isOtpEmail = loginTab === 'otp' && otpType === 'email'
106
- const isOtpCellphone = loginTab === 'otp' && otpType === 'cellphone'
107
-
108
- const [isCheckingCode, setCheckingCode] = useState(false)
109
-
110
- const googleLoginEnabled = configs?.google_login_enabled?.value === '1'
111
- const facebookLoginEnabled = configs?.facebook_login_enabled?.value === '1'
112
- const appleLoginEnabled = Platform.OS === 'ios' && (configs?.apple_login_enabled?.value === '1')
113
-
114
- const loginStyle = StyleSheet.create({
115
- btnOutline: {
116
- backgroundColor: '#FFF',
117
- color: theme.colors.primary,
118
- borderRadius: 7.6,
119
- },
120
- inputStyle: {
121
- marginBottom: 28,
122
- borderWidth: 1,
123
- // borderColor: theme.colors.border,
124
- borderRadius: 7.6,
125
- },
126
- line: {
127
- height: 1,
128
- backgroundColor: theme.colors.border,
129
- flexGrow: 1,
130
- marginBottom: 7,
131
- },
132
- recaptchaIcon: {
133
- width: 100,
134
- height: 100,
135
- },
136
- borderStyleBase: {
137
- width: 30,
138
- height: 45
139
- },
140
-
141
- borderStyleHighLighted: {
142
- borderColor: "#03DAC6",
143
- },
144
-
145
- underlineStyleBase: {
146
- width: 45,
147
- height: 60,
148
- borderWidth: 1,
149
- fontSize: 16
150
- },
151
-
152
- underlineStyleHighLighted: {
153
- borderColor: theme.colors.primary,
154
- color: theme.colors.primary,
155
- fontSize: 16
156
- },
157
- });
158
-
159
- const emailRef = useRef<any>({});
160
- const passwordRef = useRef<any>({});
161
- const recaptchaRef = useRef<any>({});
162
-
163
- const handleChangeTab = (val: string, otpType?: string) => {
164
- props.handleChangeTab(val);
165
- setPasswordSee(false);
166
- handleCategoryScroll(otpType ? `${val}_${otpType}` : val)
167
- };
168
-
169
- const vibrateApp = (impact?: string) => {
170
- const options = {
171
- enableVibrateFallback: true,
172
- ignoreAndroidSystemSettings: false
173
- };
174
- ReactNativeHapticFeedback.trigger(impact || "impactLight", options);
175
- }
176
-
177
- const onSubmit = (values?: any) => {
178
- Keyboard.dismiss();
179
- if (loginTab === 'otp') {
180
- if (phoneInputData.error && (loginTab !== 'otp' || (otpType === 'cellphone' && loginTab === 'otp'))) {
181
- showToast(ToastType.Error, t('INVALID_PHONE_NUMBER', 'Invalid phone number'));
182
- vibrateApp()
183
- return
184
- }
185
- if (otpType === 'cellphone' && phoneInputData?.error && !phoneInputData?.phone?.cellphone) {
186
- showToast(ToastType.Error, t('PHONE_NUMBER_REQUIRED', 'Phone number is required'));
187
- return
188
- }
189
- if (loginTab === 'otp') {
190
- generateOtpCode({
191
- ...values,
192
- ...phoneInputData.phone
193
- })
194
- }
195
- setWillVerifyOtpState(true)
196
- } else {
197
- if (phoneInputData.error) {
198
- showToast(ToastType.Error, phoneInputData.error);
199
- vibrateApp()
200
- return;
201
- }
202
- handleButtonLoginClick({
203
- ...values,
204
- ...phoneInputData.phone,
205
- });
206
- }
207
-
208
- };
209
- const handleVerifyCodeClick = () => {
210
- if (phoneInputData.error) {
211
- showToast(ToastType.Error, phoneInputData.error);
212
- vibrateApp()
213
- return;
214
- }
215
- if (
216
- !phoneInputData.error &&
217
- !phoneInputData.phone.country_phone_code &&
218
- !phoneInputData.phone.cellphone
219
- ) {
220
- showToast(
221
- ToastType.Error,
222
- t(
223
- 'VALIDATION_ERROR_MOBILE_PHONE_REQUIRED',
224
- 'The field Mobile phone is required.',
225
- ),
226
- );
227
- vibrateApp()
228
- return;
229
- }
230
- handleSendVerifyCode && handleSendVerifyCode(phoneInputData.phone);
231
- setIsLoadingVerifyModal(true);
232
- };
233
-
234
- const handleSuccessFacebook = (user: any) => {
235
- login({
236
- user,
237
- token: user.session.access_token,
238
- });
239
- };
240
-
241
- const handleChangeInputEmail = (value: string, onChange: any) => {
242
- onChange(value.toLowerCase().trim().replace(/[&,()%";:ç?<>{}\\[\]\s]/g, ''));
243
- };
244
-
245
- const handleOpenRecaptcha = () => {
246
- setRecaptchaVerified(false)
247
- if (!recaptchaConfig?.siteKey) {
248
- showToast(ToastType.Error, t('NO_RECAPTCHA_SITE_KEY', 'The config doesn\'t have recaptcha site key'));
249
- vibrateApp()
250
- return
251
- }
252
- if (!recaptchaConfig?.baseUrl) {
253
- showToast(ToastType.Error, t('NO_RECAPTCHA_BASE_URL', 'The config doesn\'t have recaptcha base url'));
254
- vibrateApp()
255
- return
256
- }
257
-
258
- recaptchaRef.current.open()
259
- }
260
-
261
- const onRecaptchaVerify = (token: any) => {
262
- setRecaptchaVerified(true)
263
- handleReCaptcha({ code: token, version: recaptchaConfig?.version })
264
- }
265
-
266
- const handleChangeOtpType = (type: string) => {
267
- handleChangeTab('otp', type)
268
- setOtpType(type)
269
- }
270
-
271
- const handleLoginOtp = async (code: string) => {
272
- if (!code) return
273
- const logged = await handleButtonLoginClick({ code })
274
- setCheckingCode(false)
275
- if (logged) {
276
- setWillVerifyOtpState(false)
277
- } else {
278
- setOtpError(t('OTP_CODE_INCORRECT', 'Otp code incorrect'))
279
- }
280
- }
281
-
282
- const closeAlert = () => {
283
- setAlertState({
284
- open: false,
285
- title: '',
286
- content: []
287
- })
288
- }
289
-
290
- const handleCategoryScroll = (opc: string) => {
291
- tabsRef.current.scrollTo({
292
- x: tabLayouts?.[opc]?.x - 40,
293
- animated: true
294
- })
295
- }
296
-
297
- const handleOnLayout = (event: any, opc: string) => {
298
- const _tabLayouts = { ...tabLayouts }
299
- const categoryKey = opc
300
- _tabLayouts[categoryKey] = event.nativeEvent.layout
301
- setTabLayouts(_tabLayouts)
302
- }
303
-
304
- const handleChangePhoneNumber = (number: any, rawNumber: any) => {
305
- setPhoneInputData(number)
306
- setCellphoneStartZero && setCellphoneStartZero(rawNumber?.number && rawNumber?.countryCallingCode ? rawNumber?.number : null)
307
- }
308
-
309
- useEffect(() => {
310
- if (configs && Object.keys(configs).length > 0 && enableReCaptcha) {
311
- if (configs?.security_recaptcha_type?.value === 'v3' &&
312
- configs?.security_recaptcha_score_v3?.value > 0 &&
313
- configs?.security_recaptcha_site_key_v3?.value
314
- ) {
315
- setRecaptchaConfig({
316
- version: 'v3',
317
- siteKey: configs?.security_recaptcha_site_key_v3?.value || null,
318
- baseUrl: configs?.security_recaptcha_base_url?.value || null
319
- })
320
- return
321
- }
322
- if (configs?.security_recaptcha_site_key?.value) {
323
- setRecaptchaConfig({
324
- version: 'v2',
325
- siteKey: configs?.security_recaptcha_site_key?.value || null,
326
- baseUrl: configs?.security_recaptcha_base_url?.value || null
327
- })
328
- }
329
- }
330
- }, [configs, enableReCaptcha])
331
-
332
- useEffect(() => {
333
- if (!formState.loading && formState.result?.error) {
334
- if (formState.result?.result?.[0] === 'ERROR_AUTH_VERIFICATION_CODE') {
335
- setRecaptchaVerified(false)
336
- setRecaptchaConfig({
337
- version: 'v2',
338
- siteKey: configs?.security_recaptcha_site_key?.value || null,
339
- baseUrl: configs?.security_recaptcha_base_url?.value || null
340
- })
341
- showToast(ToastType.Info, t('TRY_AGAIN', 'Please try again'))
342
- vibrateApp()
343
- return
344
- }
345
- formState.result?.result &&
346
- showToast(
347
- ToastType.Error,
348
- typeof formState.result?.result === 'string'
349
- ? formState.result?.result
350
- : formState.result?.result[0],
351
- );
352
- formState.result?.result && vibrateApp()
353
- }
354
- }, [formState]);
355
-
356
- useEffect(() => {
357
- if (verifyPhoneState && !verifyPhoneState?.loading) {
358
- if (verifyPhoneState.result?.error) {
359
- const message =
360
- typeof verifyPhoneState?.result?.result === 'string'
361
- ? verifyPhoneState?.result?.result
362
- : verifyPhoneState?.result?.result[0];
363
- verifyPhoneState.result?.result && showToast(ToastType.Error, message);
364
- verifyPhoneState.result?.result && vibrateApp();
365
- setIsLoadingVerifyModal(false);
366
- return;
367
- }
368
-
369
- const okResult = verifyPhoneState.result?.result === 'OK';
370
- if (okResult) {
371
- !isModalVisible && setIsModalVisible(true);
372
- setIsLoadingVerifyModal(false);
373
- }
374
- }
375
- }, [verifyPhoneState]);
376
-
377
- useEffect(() => {
378
- if (phoneInputData?.phone?.cellphone) setValue('cellphone', phoneInputData?.phone?.cellphone, '')
379
- else setValue('cellphone', '')
380
- }, [phoneInputData?.phone?.cellphone])
381
-
382
- useEffect(() => {
383
- register('cellphone', {
384
- required: loginTab === 'cellphone'
385
- ? t('VALIDATION_ERROR_MOBILE_PHONE_REQUIRED', 'The field Mobile phone is required').replace('_attribute_', t('CELLPHONE', 'Cellphone'))
386
- : null
387
- })
388
- }, [register])
389
-
390
- useEffect(() => {
391
- reset()
392
- }, [loginTab])
393
-
394
- useEffect(() => {
395
- if (checkPhoneCodeState?.result?.error) {
396
- setAlertState({
397
- open: true,
398
- content: t(checkPhoneCodeState?.result?.error, checkPhoneCodeState?.result?.error),
399
- title: ''
400
- })
401
- }
402
- }, [checkPhoneCodeState])
403
-
404
- useEffect(() => {
405
- if (!!Object.values(errors)?.length) vibrateApp()
406
- }, [errors])
407
-
408
- return (
409
- <Container>
410
- {isGuest ? (
411
- <OText style={{ textAlign: 'center', marginBottom: 10 }} size={18}>{t('LOGIN', 'Login')}</OText>
412
- ) : (
413
- <NavBar
414
- title={t('LOGIN', 'Login')}
415
- titleAlign={'center'}
416
- onActionLeft={() => navigation?.canGoBack() && navigation.goBack()}
417
- showCall={false}
418
- btnStyle={{ paddingLeft: 0 }}
419
- titleWrapStyle={{ paddingHorizontal: 0 }}
420
- titleStyle={{ marginRight: 0, marginLeft: 0 }}
421
- />
422
- )}
423
- <FormSide>
424
- {(Number(useLoginByEmail) + Number(useLoginByCellphone) + Number(useLoginOtpEmail) + Number(useLoginOtpCellphone) > 1) && (
425
- <LoginWith>
426
- <OTabs
427
- horizontal
428
- showsHorizontalScrollIndicator={false}
429
- ref={tabsRef}
430
- >
431
- {useLoginByEmail && (
432
- <TabBtn
433
- onPress={() => handleChangeTab('email')}
434
- onLayout={(event: any) => handleOnLayout(event, 'email')}
435
- >
436
- <OTab
437
- style={{
438
- borderBottomColor:
439
- loginTab === 'email'
440
- ? theme.colors.textNormal
441
- : theme.colors.border,
442
- }}>
443
- <OText
444
- size={14}
445
- color={
446
- loginTab === 'email'
447
- ? theme.colors.textNormal
448
- : theme.colors.disabled
449
- }
450
- weight={loginTab === 'email' ? 'bold' : 'normal'}>
451
- {t('LOGIN_BY_EMAIL', 'by Email')}
452
- </OText>
453
- </OTab>
454
- </TabBtn>
455
- )}
456
- {useLoginByCellphone && (
457
- <TabBtn
458
- onPress={() => handleChangeTab('cellphone')}
459
- onLayout={(event: any) => handleOnLayout(event, 'cellphone')}
460
- >
461
- <OTab
462
- style={{
463
- borderBottomColor:
464
- loginTab === 'cellphone'
465
- ? theme.colors.textNormal
466
- : theme.colors.border,
467
- }}>
468
- <OText
469
- size={14}
470
- color={
471
- loginTab === 'cellphone'
472
- ? theme.colors.textNormal
473
- : theme.colors.disabled
474
- }
475
- weight={loginTab === 'cellphone' ? 'bold' : 'normal'}>
476
- {t('LOGIN_BY_PHONE', 'by Phone')}
477
- </OText>
478
- </OTab>
479
- </TabBtn>
480
- )}
481
- {useLoginOtpEmail && (
482
- <TabBtn
483
- onPress={() => handleChangeOtpType('email')}
484
- onLayout={(event: any) => handleOnLayout(event, 'otp_email')}
485
- >
486
- <OTab
487
- style={{
488
- borderBottomColor:
489
- isOtpEmail
490
- ? theme.colors.textNormal
491
- : theme.colors.border,
492
- }}>
493
- <OText
494
- size={14}
495
- color={
496
- isOtpEmail
497
- ? theme.colors.textNormal
498
- : theme.colors.disabled
499
- }
500
- weight={isOtpEmail ? 'bold' : 'normal'}>
501
- {t('BY_OTP_EMAIL', 'By Otp Email')}
502
- </OText>
503
- </OTab>
504
- </TabBtn>
505
- )}
506
- {useLoginOtpCellphone && (
507
- <TabBtn
508
- onPress={() => handleChangeOtpType('cellphone')}
509
- onLayout={(event: any) => handleOnLayout(event, 'otp_cellphone')}
510
- >
511
- <OTab
512
- style={{
513
- borderBottomColor:
514
- isOtpCellphone
515
- ? theme.colors.textNormal
516
- : theme.colors.border,
517
- }}>
518
- <OText
519
- size={14}
520
- color={
521
- isOtpCellphone
522
- ? theme.colors.textNormal
523
- : theme.colors.disabled
524
- }
525
- weight={isOtpCellphone ? 'bold' : 'normal'}>
526
- {t('BY_OTP_PHONE', 'By Otp Phone')}
527
- </OText>
528
- </OTab>
529
- </TabBtn>
530
- )}
531
- </OTabs>
532
- </LoginWith>
533
- )}
534
-
535
- {(useLoginByCellphone || useLoginByEmail || useLoginOtp) && (
536
- <FormInput>
537
- {((useLoginByEmail && loginTab === 'email') || (loginTab === 'otp' && otpType === 'email')) && (
538
- <>
539
- {errors?.email && (
540
- <OText
541
- size={14}
542
- color={theme.colors.danger5}
543
- weight={'normal'}>
544
- {errors?.email?.message}{errors?.email?.type === 'required' && '*'}
545
- </OText>
546
- )}
547
- <Controller
548
- control={control}
549
- render={({ onChange, value }: any) => (
550
- <OInput
551
- placeholder={t('EMAIL', 'Email')}
552
- style={loginStyle.inputStyle}
553
- icon={theme.images.general.email}
554
- onChange={(e: any) => {
555
- handleChangeInputEmail(e, onChange);
556
- }}
557
- value={value}
558
- autoCapitalize="none"
559
- autoCorrect={false}
560
- type="email-address"
561
- autoCompleteType="email"
562
- returnKeyType="next"
563
- onSubmitEditing={() => passwordRef.current?.focus()}
564
- blurOnSubmit={false}
565
- forwardRef={emailRef}
566
- borderColor={errors?.email ? theme.colors.danger5 : theme.colors.border}
567
- />
568
- )}
569
- name="email"
570
- rules={{
571
- required: {
572
- value: true,
573
- message: t(
574
- 'VALIDATION_ERROR_EMAIL_REQUIRED',
575
- 'The field Email is required',
576
- ).replace('_attribute_', t('EMAIL', 'Email'))
577
- },
578
- pattern: {
579
- value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
580
- message: t(
581
- 'INVALID_ERROR_EMAIL',
582
- 'Invalid email address',
583
- ).replace('_attribute_', t('EMAIL', 'Email')),
584
- }
585
- }}
586
- defaultValue=""
587
- />
588
- </>
589
-
590
- )}
591
- {((useLoginByCellphone && loginTab === 'cellphone') || (loginTab === 'otp' && otpType === 'cellphone')) && (
592
- <View style={{ marginBottom: 28 }}>
593
- <PhoneInputNumber
594
- data={phoneInputData}
595
- handleData={handleChangePhoneNumber}
596
- textInputProps={{
597
- returnKeyType: 'next',
598
- onSubmitEditing: () => passwordRef?.current?.focus?.(),
599
- }}
600
- isStartValidation={errors?.cellphone}
601
- />
602
- </View>
603
- )}
604
- {errors?.password && (
605
- <OText
606
- size={14}
607
- color={theme.colors.danger5}
608
- weight={'normal'}>
609
- {errors?.password?.message}{errors?.password?.type === 'required' && '*'}
610
- </OText>
611
- )}
612
- {loginTab !== 'otp' && (
613
-
614
- <Controller
615
- control={control}
616
- render={({ onChange, value }: any) => (
617
- <OInput
618
- isSecured={!passwordSee ? true : false}
619
- placeholder={t('PASSWORD', 'Password')}
620
- style={{ ...loginStyle.inputStyle, marginBottom: 14 }}
621
- icon={theme.images.general.lock}
622
- iconCustomRight={
623
- !passwordSee ? (
624
- <MaterialCommunityIcons
625
- name="eye-outline"
626
- size={24}
627
- onPress={() => setPasswordSee(!passwordSee)}
628
- color={theme.colors.disabled}
629
- />
630
- ) : (
631
- <MaterialCommunityIcons
632
- name="eye-off-outline"
633
- size={24}
634
- onPress={() => setPasswordSee(!passwordSee)}
635
- color={theme.colors.disabled}
636
- />
637
- )
638
- }
639
- autoCapitalize='none'
640
- value={value}
641
- forwardRef={passwordRef}
642
- onChange={(val: any) => onChange(val)}
643
- returnKeyType="done"
644
- onSubmitEditing={handleSubmit(onSubmit)}
645
- blurOnSubmit
646
- borderColor={errors?.password ? theme.colors.danger5 : theme.colors.border}
647
- />
648
- )}
649
- name="password"
650
- rules={{
651
- required: {
652
- value: true,
653
- message: t(
654
- 'VALIDATION_ERROR_PASSWORD_REQUIRED',
655
- 'The field Password is required',
656
- ).replace('_attribute_', t('PASSWORD', 'Password'))
657
- }
658
- }}
659
- defaultValue=""
660
- />
661
- )}
662
- {onNavigationRedirect && forgotButtonText && loginTab !== 'otp' && (
663
- <TouchableOpacity onPress={() => onNavigationRedirect('Forgot')}>
664
- <OText size={14} mBottom={18}>
665
- {forgotButtonText}
666
- </OText>
667
- </TouchableOpacity>
668
- )}
669
-
670
- {(enableReCaptcha && recaptchaConfig?.version) && (
671
- <>
672
- {recaptchaConfig?.version === 'v3' ? (
673
- <ReCaptcha
674
- url={recaptchaConfig?.baseUrl}
675
- siteKey={recaptchaConfig?.siteKey}
676
- containerStyle={{ height: 40 }}
677
- onExecute={onRecaptchaVerify}
678
- reCaptchaType={1}
679
- />
680
- ) : (
681
- <>
682
- <TouchableOpacity
683
- onPress={handleOpenRecaptcha}
684
- >
685
- <RecaptchaButton>
686
- {recaptchaVerified ? (
687
- <MaterialCommunityIcons
688
- name="checkbox-marked"
689
- size={26}
690
- color={theme.colors.primary}
691
- />
692
- ) : (
693
- <MaterialCommunityIcons
694
- name="checkbox-blank-outline"
695
- size={26}
696
- color={theme.colors.mediumGray}
697
- />
698
- )}
699
- <OText size={14} mLeft={8}>{t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')}</OText>
700
- </RecaptchaButton>
701
- </TouchableOpacity>
702
- <Recaptcha
703
- ref={recaptchaRef}
704
- siteKey={recaptchaConfig?.siteKey}
705
- baseUrl={recaptchaConfig?.baseUrl}
706
- onVerify={onRecaptchaVerify}
707
- onExpire={() => setRecaptchaVerified(false)}
708
- />
709
- </>)
710
- }
711
- </>
712
- )}
713
- <OButton
714
- onClick={handleSubmit(onSubmit)}
715
- text={loginTab !== 'otp' ? loginButtonText : t('GET_VERIFY_CODE', 'Get verify code')}
716
- imgRightSrc={null}
717
- isLoading={formState.loading}
718
- style={{ borderRadius: 7.6, marginTop: 10, marginBottom: 25 }}
719
- />
720
- {onNavigationRedirect && registerButtonText && (
721
- <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'center' }}>
722
- <OText size={14}>
723
- {t('NEW_ON_PLATFORM', 'New on Ordering?')}
724
- </OText>
725
- <TouchableOpacity onPress={() => onNavigationRedirect('Signup')}>
726
- <OText size={14} mLeft={5} color={theme.colors.primary}>
727
- {t('CREATE_ACCOUNT', 'Create account')}
728
- </OText>
729
- </TouchableOpacity>
730
- </View>
731
- )}
732
- </FormInput>
733
- )}
734
-
735
- {useLoginByCellphone &&
736
- loginTab === 'cellphone' &&
737
- configs && Object.keys(configs).length > 0 &&
738
- (configs?.twilio_service_enabled?.value === 'true' ||
739
- configs?.twilio_service_enabled?.value === '1') &&
740
- configs?.twilio_module?.value && (
741
- <>
742
- <OrSeparator>
743
- <LineSeparator />
744
- <OText size={18} mRight={20} mLeft={20}>
745
- {t('OR', 'Or')}
746
- </OText>
747
- <LineSeparator />
748
- </OrSeparator>
749
-
750
- <ButtonsWrapper mBottom={20}>
751
- <OButton
752
- onClick={handleVerifyCodeClick}
753
- text={t('GET_VERIFY_CODE', 'Get Verify Code')}
754
- style={loginStyle.btnOutline}
755
- imgRightSrc={null}
756
- isLoading={isLoadingVerifyModal}
757
- indicatorColor={theme.colors.primary}
758
- />
759
- </ButtonsWrapper>
760
- </>
761
- )}
762
-
763
- {configs && Object.keys(configs).length > 0 ? (
764
- (((configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') && configs?.facebook_id?.value && facebookLoginEnabled) ||
765
- ((configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled) ||
766
- ((configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && appleLoginEnabled)) && !isGuest &&
767
- (
768
- <>
769
- <View
770
- style={{
771
- flexDirection: 'row',
772
- width: '100%',
773
- justifyContent: 'space-between',
774
- alignItems: 'center',
775
- marginVertical: 15
776
- }}>
777
- <View style={loginStyle.line} />
778
- <OText
779
- size={14}
780
- mBottom={10}
781
- style={{ paddingHorizontal: 19 }}
782
- color={theme.colors.disabled}>
783
- {t('OR', 'or')}
784
- </OText>
785
- <View style={loginStyle.line} />
786
- </View>
787
- <ButtonsWrapper>
788
- <SocialButtons>
789
- {(configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') &&
790
- configs?.facebook_id?.value &&
791
- facebookLoginEnabled && (
792
- <FacebookLogin
793
- notificationState={notificationState}
794
- handleErrors={(err: any) => { showToast(ToastType.Error, err), vibrateApp() }}
795
- handleLoading={(val: boolean) => setIsFBLoading(val)}
796
- handleSuccessFacebookLogin={handleSuccessFacebook}
797
- />
798
- )}
799
- {(configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled && (
800
- <GoogleLogin
801
- notificationState={notificationState}
802
- webClientId={configs?.google_login_client_id?.value}
803
- handleErrors={(err: any) => { showToast(ToastType.Error, err), vibrateApp() }}
804
- handleLoading={(val: boolean) => setIsFBLoading(val)}
805
- handleSuccessGoogleLogin={handleSuccessFacebook}
806
- />
807
- )}
808
- {(configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && appleLoginEnabled && (
809
- <AppleLogin
810
- notificationState={notificationState}
811
- handleErrors={(err: any) => { showToast(ToastType.Error, err), vibrateApp() }}
812
- handleLoading={(val: boolean) => setIsFBLoading(val)}
813
- handleSuccessAppleLogin={handleSuccessFacebook}
814
- />
815
- )}
816
- </SocialButtons>
817
- </ButtonsWrapper>
818
- </>
819
- )
820
- ) : (
821
- <SkeletonWrapper>
822
- <Placeholder Animation={Fade}>
823
- <PlaceholderLine
824
- height={20}
825
- style={{ marginBottom: 15, marginTop: 10 }}
826
- />
827
- <PlaceholderLine
828
- height={50}
829
- style={{ borderRadius: 25, marginBottom: 25 }}
830
- />
831
- </Placeholder>
832
- </SkeletonWrapper>
833
- )}
834
-
835
- {enabledPoweredByOrdering && (
836
- <OText>
837
- Powered By Ordering.co
838
- </OText>
839
- )}
840
- </FormSide>
841
- <OModal
842
- open={isModalVisible}
843
- onClose={() => setIsModalVisible(false)}
844
- entireModal
845
- title={t('VERIFY_PHONE', 'Verify Phone')}
846
- >
847
- <VerifyPhone
848
- phone={phoneInputData.phone}
849
- verifyPhoneState={verifyPhoneState}
850
- checkPhoneCodeState={checkPhoneCodeState}
851
- handleCheckPhoneCode={handleCheckPhoneCode}
852
- setCheckPhoneCodeState={setCheckPhoneCodeState}
853
- handleVerifyCodeClick={handleVerifyCodeClick}
854
- onClose={() => setIsModalVisible(false)}
855
- />
856
- </OModal>
857
- <Modal
858
- visible={willVerifyOtpState}
859
- onDismiss={() => setWillVerifyOtpState(false)}
860
- animationType='slide'
861
- >
862
- <Otp
863
- isCheckingCode={isCheckingCode}
864
- setCheckingCode={setCheckingCode}
865
- willVerifyOtpState={willVerifyOtpState}
866
- otpError={otpError}
867
- setOtpError={setOtpError}
868
- setWillVerifyOtpState={setWillVerifyOtpState}
869
- handleLoginOtp={handleLoginOtp}
870
- onSubmit={onSubmit}
871
- setAlertState={setAlertState}
872
- />
873
- </Modal>
874
- <Alert
875
- open={alertState.open}
876
- content={alertState.content}
877
- title={alertState.title || ''}
878
- onAccept={closeAlert}
879
- onClose={closeAlert}
880
- />
881
- <Spinner visible={isFBLoading} />
882
- </Container>
883
- );
51
+ const {
52
+ loginTab,
53
+ formState,
54
+ navigation,
55
+ useLoginByEmail,
56
+ useLoginByCellphone,
57
+ useLoginOtp,
58
+ loginButtonText,
59
+ forgotButtonText,
60
+ verifyPhoneState,
61
+ checkPhoneCodeState,
62
+ registerButtonText,
63
+ setCheckPhoneCodeState,
64
+ handleButtonLoginClick,
65
+ handleSendVerifyCode,
66
+ handleCheckPhoneCode,
67
+ onNavigationRedirect,
68
+ notificationState,
69
+ handleReCaptcha,
70
+ enableReCaptcha,
71
+ otpType,
72
+ setOtpType,
73
+ generateOtpCode,
74
+ useLoginOtpEmail,
75
+ useLoginOtpCellphone,
76
+ useLoginOtpWhatsapp,
77
+ isGuest,
78
+ setCellphoneStartZero
79
+ } = props;
80
+
81
+ const [, { showToast }] = useToast();
82
+ const [, t] = useLanguage();
83
+ const [{ configs }] = useConfig();
84
+ const [, { login }] = useSession();
85
+ const { control, handleSubmit, errors, reset, register, setValue } = useForm();
86
+ const [passwordSee, setPasswordSee] = useState(false);
87
+ const [isLoadingVerifyModal, setIsLoadingVerifyModal] = useState(false);
88
+ const [isModalVisible, setIsModalVisible] = useState(false);
89
+ const [isFBLoading, setIsFBLoading] = useState(false);
90
+ const [willVerifyOtpState, setWillVerifyOtpState] = useState(false)
91
+ const [phoneInputData, setPhoneInputData] = useState({
92
+ error: '',
93
+ phone: {
94
+ country_phone_code: null,
95
+ cellphone: null,
96
+ },
97
+ });
98
+ const [recaptchaConfig, setRecaptchaConfig] = useState<any>({})
99
+ const [recaptchaVerified, setRecaptchaVerified] = useState(false)
100
+ const [alertState, setAlertState] = useState({ open: false, title: '', content: [] })
101
+ const [tabLayouts, setTabLayouts] = useState<any>({})
102
+ const [otpError, setOtpError] = useState(null)
103
+ const tabsRef = useRef<any>(null)
104
+ const enabledPoweredByOrdering = configs?.powered_by_ordering_module?.value
105
+ const theme = useTheme();
106
+ const isOtpEmail = loginTab === 'otp' && otpType === 'email'
107
+ const isOtpCellphone = loginTab === 'otp' && otpType === 'cellphone'
108
+
109
+ const [isCheckingCode, setCheckingCode] = useState(false)
110
+
111
+ const googleLoginEnabled = configs?.google_login_enabled?.value === '1'
112
+ const facebookLoginEnabled = configs?.facebook_login_enabled?.value === '1'
113
+ const appleLoginEnabled = Platform.OS === 'ios' && (configs?.apple_login_enabled?.value === '1')
114
+
115
+ const loginStyle = StyleSheet.create({
116
+ btnOutline: {
117
+ backgroundColor: '#FFF',
118
+ color: theme.colors.primary,
119
+ borderRadius: 7.6,
120
+ },
121
+ inputStyle: {
122
+ marginBottom: 28,
123
+ borderWidth: 1,
124
+ // borderColor: theme.colors.border,
125
+ borderRadius: 7.6,
126
+ },
127
+ line: {
128
+ height: 1,
129
+ backgroundColor: theme.colors.border,
130
+ flexGrow: 1,
131
+ marginBottom: 7,
132
+ },
133
+ recaptchaIcon: {
134
+ width: 100,
135
+ height: 100,
136
+ },
137
+ borderStyleBase: {
138
+ width: 30,
139
+ height: 45
140
+ },
141
+
142
+ borderStyleHighLighted: {
143
+ borderColor: "#03DAC6",
144
+ },
145
+
146
+ underlineStyleBase: {
147
+ width: 45,
148
+ height: 60,
149
+ borderWidth: 1,
150
+ fontSize: 16
151
+ },
152
+
153
+ underlineStyleHighLighted: {
154
+ borderColor: theme.colors.primary,
155
+ color: theme.colors.primary,
156
+ fontSize: 16
157
+ },
158
+ });
159
+
160
+ const emailRef = useRef<any>({});
161
+ const passwordRef = useRef<any>({});
162
+ const recaptchaRef = useRef<any>({});
163
+ const otpChannelRef = useRef<number>(2);
164
+
165
+ const handleChangeTab = (val: string, otpType?: string) => {
166
+ props.handleChangeTab(val);
167
+ setPasswordSee(false);
168
+ handleCategoryScroll(otpType ? `${val}_${otpType}` : val)
169
+ };
170
+
171
+ const vibrateApp = (impact?: string) => {
172
+ const options = {
173
+ enableVibrateFallback: true,
174
+ ignoreAndroidSystemSettings: false
175
+ };
176
+ ReactNativeHapticFeedback.trigger(impact || "impactLight", options);
177
+ }
178
+
179
+ const onSubmit = (values?: any) => {
180
+ Keyboard.dismiss();
181
+ if (loginTab === 'otp') {
182
+ if (phoneInputData.error && (loginTab !== 'otp' || (otpType === 'cellphone' && loginTab === 'otp'))) {
183
+ showToast(ToastType.Error, t('INVALID_PHONE_NUMBER', 'Invalid phone number'));
184
+ vibrateApp()
185
+ return
186
+ }
187
+ if (otpType === 'cellphone' && phoneInputData?.error && !phoneInputData?.phone?.cellphone) {
188
+ showToast(ToastType.Error, t('PHONE_NUMBER_REQUIRED', 'Phone number is required'));
189
+ return
190
+ }
191
+ if (loginTab === 'otp') {
192
+ generateOtpCode({
193
+ ...values,
194
+ ...phoneInputData.phone
195
+ }, otpChannelRef.current)
196
+ }
197
+ setWillVerifyOtpState(true)
198
+ } else {
199
+ if (phoneInputData.error) {
200
+ showToast(ToastType.Error, phoneInputData.error);
201
+ vibrateApp()
202
+ return;
203
+ }
204
+ handleButtonLoginClick({
205
+ ...values,
206
+ ...phoneInputData.phone,
207
+ });
208
+ }
209
+
210
+ };
211
+ const handleVerifyCodeClick = () => {
212
+ if (phoneInputData.error) {
213
+ showToast(ToastType.Error, phoneInputData.error);
214
+ vibrateApp()
215
+ return;
216
+ }
217
+ if (
218
+ !phoneInputData.error &&
219
+ !phoneInputData.phone.country_phone_code &&
220
+ !phoneInputData.phone.cellphone
221
+ ) {
222
+ showToast(
223
+ ToastType.Error,
224
+ t(
225
+ 'VALIDATION_ERROR_MOBILE_PHONE_REQUIRED',
226
+ 'The field Mobile phone is required.',
227
+ ),
228
+ );
229
+ vibrateApp()
230
+ return;
231
+ }
232
+ handleSendVerifyCode && handleSendVerifyCode(phoneInputData.phone);
233
+ setIsLoadingVerifyModal(true);
234
+ };
235
+
236
+ const handleSuccessFacebook = (user: any) => {
237
+ login({
238
+ user,
239
+ token: user.session.access_token,
240
+ });
241
+ };
242
+
243
+ const handleChangeInputEmail = (value: string, onChange: any) => {
244
+ onChange(value.toLowerCase().trim().replace(/[&,()%";:ç?<>{}\\[\]\s]/g, ''));
245
+ };
246
+
247
+ const handleOpenRecaptcha = () => {
248
+ setRecaptchaVerified(false)
249
+ if (!recaptchaConfig?.siteKey) {
250
+ showToast(ToastType.Error, t('NO_RECAPTCHA_SITE_KEY', 'The config doesn\'t have recaptcha site key'));
251
+ vibrateApp()
252
+ return
253
+ }
254
+ if (!recaptchaConfig?.baseUrl) {
255
+ showToast(ToastType.Error, t('NO_RECAPTCHA_BASE_URL', 'The config doesn\'t have recaptcha base url'));
256
+ vibrateApp()
257
+ return
258
+ }
259
+
260
+ recaptchaRef.current.open()
261
+ }
262
+
263
+ const onRecaptchaVerify = (token: any) => {
264
+ setRecaptchaVerified(true)
265
+ handleReCaptcha({ code: token, version: recaptchaConfig?.version })
266
+ }
267
+
268
+ const handleChangeOtpType = (type: string) => {
269
+ handleChangeTab('otp', type)
270
+ setOtpType(type)
271
+ }
272
+
273
+ const handleLoginOtp = async (code: string) => {
274
+ if (!code) return
275
+ const logged = await handleButtonLoginClick({ code })
276
+ setCheckingCode(false)
277
+ if (logged) {
278
+ setWillVerifyOtpState(false)
279
+ } else {
280
+ setOtpError(t('OTP_CODE_INCORRECT', 'Otp code incorrect'))
281
+ }
282
+ }
283
+
284
+ const closeAlert = () => {
285
+ setAlertState({
286
+ open: false,
287
+ title: '',
288
+ content: []
289
+ })
290
+ }
291
+
292
+ const handleCategoryScroll = (opc: string) => {
293
+ tabsRef.current.scrollTo({
294
+ x: tabLayouts?.[opc]?.x - 40,
295
+ animated: true
296
+ })
297
+ }
298
+
299
+ const handleOnLayout = (event: any, opc: string) => {
300
+ const _tabLayouts = { ...tabLayouts }
301
+ const categoryKey = opc
302
+ _tabLayouts[categoryKey] = event.nativeEvent.layout
303
+ setTabLayouts(_tabLayouts)
304
+ }
305
+
306
+ const handleChangePhoneNumber = (number: any, rawNumber: any) => {
307
+ setPhoneInputData(number)
308
+ setCellphoneStartZero && setCellphoneStartZero(rawNumber?.number && rawNumber?.countryCallingCode ? rawNumber?.number : null)
309
+ }
310
+
311
+ useEffect(() => {
312
+ if (configs && Object.keys(configs).length > 0 && enableReCaptcha) {
313
+ if (configs?.security_recaptcha_type?.value === 'v3' &&
314
+ configs?.security_recaptcha_score_v3?.value > 0 &&
315
+ configs?.security_recaptcha_site_key_v3?.value
316
+ ) {
317
+ setRecaptchaConfig({
318
+ version: 'v3',
319
+ siteKey: configs?.security_recaptcha_site_key_v3?.value || null,
320
+ baseUrl: configs?.security_recaptcha_base_url?.value || null
321
+ })
322
+ return
323
+ }
324
+ if (configs?.security_recaptcha_site_key?.value) {
325
+ setRecaptchaConfig({
326
+ version: 'v2',
327
+ siteKey: configs?.security_recaptcha_site_key?.value || null,
328
+ baseUrl: configs?.security_recaptcha_base_url?.value || null
329
+ })
330
+ }
331
+ }
332
+ }, [configs, enableReCaptcha])
333
+
334
+ useEffect(() => {
335
+ if (!formState.loading && formState.result?.error) {
336
+ if (formState.result?.result?.[0] === 'ERROR_AUTH_VERIFICATION_CODE') {
337
+ setRecaptchaVerified(false)
338
+ setRecaptchaConfig({
339
+ version: 'v2',
340
+ siteKey: configs?.security_recaptcha_site_key?.value || null,
341
+ baseUrl: configs?.security_recaptcha_base_url?.value || null
342
+ })
343
+ showToast(ToastType.Info, t('TRY_AGAIN', 'Please try again'))
344
+ vibrateApp()
345
+ return
346
+ }
347
+ formState.result?.result &&
348
+ showToast(
349
+ ToastType.Error,
350
+ typeof formState.result?.result === 'string'
351
+ ? formState.result?.result
352
+ : formState.result?.result[0],
353
+ );
354
+ formState.result?.result && vibrateApp()
355
+ }
356
+ }, [formState]);
357
+
358
+ useEffect(() => {
359
+ if (verifyPhoneState && !verifyPhoneState?.loading) {
360
+ if (verifyPhoneState.result?.error) {
361
+ const message =
362
+ typeof verifyPhoneState?.result?.result === 'string'
363
+ ? verifyPhoneState?.result?.result
364
+ : verifyPhoneState?.result?.result[0];
365
+ verifyPhoneState.result?.result && showToast(ToastType.Error, message);
366
+ verifyPhoneState.result?.result && vibrateApp();
367
+ setIsLoadingVerifyModal(false);
368
+ return;
369
+ }
370
+
371
+ const okResult = verifyPhoneState.result?.result === 'OK';
372
+ if (okResult) {
373
+ !isModalVisible && setIsModalVisible(true);
374
+ setIsLoadingVerifyModal(false);
375
+ }
376
+ }
377
+ }, [verifyPhoneState]);
378
+
379
+ useEffect(() => {
380
+ if (phoneInputData?.phone?.cellphone) setValue('cellphone', phoneInputData?.phone?.cellphone, '')
381
+ else setValue('cellphone', '')
382
+ }, [phoneInputData?.phone?.cellphone])
383
+
384
+ useEffect(() => {
385
+ register('cellphone', {
386
+ required: loginTab === 'cellphone'
387
+ ? t('VALIDATION_ERROR_MOBILE_PHONE_REQUIRED', 'The field Mobile phone is required').replace('_attribute_', t('CELLPHONE', 'Cellphone'))
388
+ : null
389
+ })
390
+ }, [register])
391
+
392
+ useEffect(() => {
393
+ reset()
394
+ }, [loginTab])
395
+
396
+ useEffect(() => {
397
+ if (checkPhoneCodeState?.result?.error) {
398
+ setAlertState({
399
+ open: true,
400
+ content: t(checkPhoneCodeState?.result?.error, checkPhoneCodeState?.result?.error),
401
+ title: ''
402
+ })
403
+ }
404
+ }, [checkPhoneCodeState])
405
+
406
+ useEffect(() => {
407
+ if (!!Object.values(errors)?.length) vibrateApp()
408
+ }, [errors])
409
+
410
+ return (
411
+ <Container>
412
+ {isGuest ? (
413
+ <OText style={{ textAlign: 'center', marginBottom: 10 }} size={18}>{t('LOGIN', 'Login')}</OText>
414
+ ) : (
415
+ <NavBar
416
+ title={t('LOGIN', 'Login')}
417
+ titleAlign={'center'}
418
+ onActionLeft={() => navigation?.canGoBack() && navigation.goBack()}
419
+ showCall={false}
420
+ btnStyle={{ paddingLeft: 0 }}
421
+ titleWrapStyle={{ paddingHorizontal: 0 }}
422
+ titleStyle={{ marginRight: 0, marginLeft: 0 }}
423
+ />
424
+ )}
425
+ <FormSide>
426
+ {(Number(useLoginByEmail) + Number(useLoginByCellphone) + Number(useLoginOtpEmail) + Number(useLoginOtpCellphone) > 1) && (
427
+ <LoginWith>
428
+ <OTabs
429
+ horizontal
430
+ showsHorizontalScrollIndicator={false}
431
+ ref={tabsRef}
432
+ >
433
+ {useLoginByEmail && (
434
+ <TabBtn
435
+ onPress={() => handleChangeTab('email')}
436
+ onLayout={(event: any) => handleOnLayout(event, 'email')}
437
+ >
438
+ <OTab
439
+ style={{
440
+ borderBottomColor:
441
+ loginTab === 'email'
442
+ ? theme.colors.textNormal
443
+ : theme.colors.border,
444
+ }}>
445
+ <OText
446
+ size={14}
447
+ color={
448
+ loginTab === 'email'
449
+ ? theme.colors.textNormal
450
+ : theme.colors.disabled
451
+ }
452
+ weight={loginTab === 'email' ? 'bold' : 'normal'}>
453
+ {t('LOGIN_BY_EMAIL', 'by Email')}
454
+ </OText>
455
+ </OTab>
456
+ </TabBtn>
457
+ )}
458
+ {useLoginByCellphone && (
459
+ <TabBtn
460
+ onPress={() => handleChangeTab('cellphone')}
461
+ onLayout={(event: any) => handleOnLayout(event, 'cellphone')}
462
+ >
463
+ <OTab
464
+ style={{
465
+ borderBottomColor:
466
+ loginTab === 'cellphone'
467
+ ? theme.colors.textNormal
468
+ : theme.colors.border,
469
+ }}>
470
+ <OText
471
+ size={14}
472
+ color={
473
+ loginTab === 'cellphone'
474
+ ? theme.colors.textNormal
475
+ : theme.colors.disabled
476
+ }
477
+ weight={loginTab === 'cellphone' ? 'bold' : 'normal'}>
478
+ {t('LOGIN_BY_PHONE', 'by Phone')}
479
+ </OText>
480
+ </OTab>
481
+ </TabBtn>
482
+ )}
483
+ {useLoginOtpEmail && (
484
+ <TabBtn
485
+ onPress={() => handleChangeOtpType('email')}
486
+ onLayout={(event: any) => handleOnLayout(event, 'otp_email')}
487
+ >
488
+ <OTab
489
+ style={{
490
+ borderBottomColor:
491
+ isOtpEmail
492
+ ? theme.colors.textNormal
493
+ : theme.colors.border,
494
+ }}>
495
+ <OText
496
+ size={14}
497
+ color={
498
+ isOtpEmail
499
+ ? theme.colors.textNormal
500
+ : theme.colors.disabled
501
+ }
502
+ weight={isOtpEmail ? 'bold' : 'normal'}>
503
+ {t('BY_OTP_EMAIL', 'By Otp Email')}
504
+ </OText>
505
+ </OTab>
506
+ </TabBtn>
507
+ )}
508
+ {useLoginOtpCellphone && (
509
+ <TabBtn
510
+ onPress={() => handleChangeOtpType('cellphone')}
511
+ onLayout={(event: any) => handleOnLayout(event, 'otp_cellphone')}
512
+ >
513
+ <OTab
514
+ style={{
515
+ borderBottomColor:
516
+ isOtpCellphone
517
+ ? theme.colors.textNormal
518
+ : theme.colors.border,
519
+ }}>
520
+ <OText
521
+ size={14}
522
+ color={
523
+ isOtpCellphone
524
+ ? theme.colors.textNormal
525
+ : theme.colors.disabled
526
+ }
527
+ weight={isOtpCellphone ? 'bold' : 'normal'}>
528
+ {t('BY_OTP_PHONE', 'By Otp Phone')}
529
+ </OText>
530
+ </OTab>
531
+ </TabBtn>
532
+ )}
533
+ </OTabs>
534
+ </LoginWith>
535
+ )}
536
+
537
+ {(useLoginByCellphone || useLoginByEmail || useLoginOtp) && (
538
+ <FormInput>
539
+ {((useLoginByEmail && loginTab === 'email') || (loginTab === 'otp' && otpType === 'email')) && (
540
+ <>
541
+ {errors?.email && (
542
+ <OText
543
+ size={14}
544
+ color={theme.colors.danger5}
545
+ weight={'normal'}>
546
+ {errors?.email?.message}{errors?.email?.type === 'required' && '*'}
547
+ </OText>
548
+ )}
549
+ <Controller
550
+ control={control}
551
+ render={({ onChange, value }: any) => (
552
+ <OInput
553
+ placeholder={t('EMAIL', 'Email')}
554
+ style={loginStyle.inputStyle}
555
+ icon={theme.images.general.email}
556
+ onChange={(e: any) => {
557
+ handleChangeInputEmail(e, onChange);
558
+ }}
559
+ value={value}
560
+ autoCapitalize="none"
561
+ autoCorrect={false}
562
+ type="email-address"
563
+ autoCompleteType="email"
564
+ returnKeyType="next"
565
+ onSubmitEditing={() => passwordRef.current?.focus()}
566
+ blurOnSubmit={false}
567
+ forwardRef={emailRef}
568
+ borderColor={errors?.email ? theme.colors.danger5 : theme.colors.border}
569
+ />
570
+ )}
571
+ name="email"
572
+ rules={{
573
+ required: {
574
+ value: true,
575
+ message: t(
576
+ 'VALIDATION_ERROR_EMAIL_REQUIRED',
577
+ 'The field Email is required',
578
+ ).replace('_attribute_', t('EMAIL', 'Email'))
579
+ },
580
+ pattern: {
581
+ value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
582
+ message: t(
583
+ 'INVALID_ERROR_EMAIL',
584
+ 'Invalid email address',
585
+ ).replace('_attribute_', t('EMAIL', 'Email')),
586
+ }
587
+ }}
588
+ defaultValue=""
589
+ />
590
+ </>
591
+
592
+ )}
593
+ {((useLoginByCellphone && loginTab === 'cellphone') || (loginTab === 'otp' && otpType === 'cellphone')) && (
594
+ <View style={{ marginBottom: 28 }}>
595
+ <PhoneInputNumber
596
+ data={phoneInputData}
597
+ handleData={handleChangePhoneNumber}
598
+ textInputProps={{
599
+ returnKeyType: 'next',
600
+ onSubmitEditing: () => passwordRef?.current?.focus?.(),
601
+ }}
602
+ isStartValidation={errors?.cellphone}
603
+ />
604
+ </View>
605
+ )}
606
+ {errors?.password && (
607
+ <OText
608
+ size={14}
609
+ color={theme.colors.danger5}
610
+ weight={'normal'}>
611
+ {errors?.password?.message}{errors?.password?.type === 'required' && '*'}
612
+ </OText>
613
+ )}
614
+ {loginTab !== 'otp' && (
615
+
616
+ <Controller
617
+ control={control}
618
+ render={({ onChange, value }: any) => (
619
+ <OInput
620
+ isSecured={!passwordSee ? true : false}
621
+ placeholder={t('PASSWORD', 'Password')}
622
+ style={{ ...loginStyle.inputStyle, marginBottom: 14 }}
623
+ icon={theme.images.general.lock}
624
+ iconCustomRight={
625
+ !passwordSee ? (
626
+ <MaterialCommunityIcons
627
+ name="eye-outline"
628
+ size={24}
629
+ onPress={() => setPasswordSee(!passwordSee)}
630
+ color={theme.colors.disabled}
631
+ />
632
+ ) : (
633
+ <MaterialCommunityIcons
634
+ name="eye-off-outline"
635
+ size={24}
636
+ onPress={() => setPasswordSee(!passwordSee)}
637
+ color={theme.colors.disabled}
638
+ />
639
+ )
640
+ }
641
+ autoCapitalize='none'
642
+ value={value}
643
+ forwardRef={passwordRef}
644
+ onChange={(val: any) => onChange(val)}
645
+ returnKeyType="done"
646
+ onSubmitEditing={handleSubmit(onSubmit)}
647
+ blurOnSubmit
648
+ borderColor={errors?.password ? theme.colors.danger5 : theme.colors.border}
649
+ />
650
+ )}
651
+ name="password"
652
+ rules={{
653
+ required: {
654
+ value: true,
655
+ message: t(
656
+ 'VALIDATION_ERROR_PASSWORD_REQUIRED',
657
+ 'The field Password is required',
658
+ ).replace('_attribute_', t('PASSWORD', 'Password'))
659
+ }
660
+ }}
661
+ defaultValue=""
662
+ />
663
+ )}
664
+ {onNavigationRedirect && forgotButtonText && loginTab !== 'otp' && (
665
+ <TouchableOpacity onPress={() => onNavigationRedirect('Forgot')}>
666
+ <OText size={14} mBottom={18}>
667
+ {forgotButtonText}
668
+ </OText>
669
+ </TouchableOpacity>
670
+ )}
671
+
672
+ {(enableReCaptcha && recaptchaConfig?.version) && (
673
+ <>
674
+ {recaptchaConfig?.version === 'v3' ? (
675
+ <ReCaptcha
676
+ url={recaptchaConfig?.baseUrl}
677
+ siteKey={recaptchaConfig?.siteKey}
678
+ containerStyle={{ height: 40 }}
679
+ onExecute={onRecaptchaVerify}
680
+ reCaptchaType={1}
681
+ />
682
+ ) : (
683
+ <>
684
+ <TouchableOpacity
685
+ onPress={handleOpenRecaptcha}
686
+ >
687
+ <RecaptchaButton>
688
+ {recaptchaVerified ? (
689
+ <MaterialCommunityIcons
690
+ name="checkbox-marked"
691
+ size={26}
692
+ color={theme.colors.primary}
693
+ />
694
+ ) : (
695
+ <MaterialCommunityIcons
696
+ name="checkbox-blank-outline"
697
+ size={26}
698
+ color={theme.colors.mediumGray}
699
+ />
700
+ )}
701
+ <OText size={14} mLeft={8}>{t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')}</OText>
702
+ </RecaptchaButton>
703
+ </TouchableOpacity>
704
+ <Recaptcha
705
+ ref={recaptchaRef}
706
+ siteKey={recaptchaConfig?.siteKey}
707
+ baseUrl={recaptchaConfig?.baseUrl}
708
+ onVerify={onRecaptchaVerify}
709
+ onExpire={() => setRecaptchaVerified(false)}
710
+ />
711
+ </>)
712
+ }
713
+ </>
714
+ )}
715
+ {(loginTab === 'otp' && otpType === 'cellphone' && useLoginOtpWhatsapp)
716
+ ? (
717
+ <View style={{ flexDirection: 'row', gap: 10, marginTop: 10, marginBottom: 25 }}>
718
+ <OButton
719
+ onClick={() => { otpChannelRef.current = 2; handleSubmit(onSubmit)() }}
720
+ text={t('SEND_BY_SMS', 'Send by SMS')}
721
+ imgRightSrc={null}
722
+ isLoading={formState.loading}
723
+ parentStyle={{ flex: 1 }}
724
+ style={{ borderRadius: 7.6 }}
725
+ />
726
+ <OButton
727
+ onClick={() => { otpChannelRef.current = 4; handleSubmit(onSubmit)() }}
728
+ text={t('SEND_BY_WHATSAPP', 'Send by WhatsApp')}
729
+ imgRightSrc={null}
730
+ isLoading={formState.loading}
731
+ parentStyle={{ flex: 1 }}
732
+ style={{ borderRadius: 7.6 }}
733
+ />
734
+ </View>
735
+ )
736
+ : (
737
+ <OButton
738
+ onClick={handleSubmit(onSubmit)}
739
+ text={loginTab !== 'otp' ? loginButtonText : t('GET_VERIFY_CODE', 'Get verify code')}
740
+ imgRightSrc={null}
741
+ isLoading={formState.loading}
742
+ style={{ borderRadius: 7.6, marginTop: 10, marginBottom: 25 }}
743
+ />
744
+ )}
745
+ {onNavigationRedirect && registerButtonText && (
746
+ <View style={{ flex: 1, flexDirection: 'row', justifyContent: 'center' }}>
747
+ <OText size={14}>
748
+ {t('NEW_ON_PLATFORM', 'New on Ordering?')}
749
+ </OText>
750
+ <TouchableOpacity onPress={() => onNavigationRedirect('Signup')}>
751
+ <OText size={14} mLeft={5} color={theme.colors.primary}>
752
+ {t('CREATE_ACCOUNT', 'Create account')}
753
+ </OText>
754
+ </TouchableOpacity>
755
+ </View>
756
+ )}
757
+ </FormInput>
758
+ )}
759
+
760
+ {useLoginByCellphone &&
761
+ loginTab === 'cellphone' &&
762
+ configs && Object.keys(configs).length > 0 &&
763
+ (configs?.twilio_service_enabled?.value === 'true' ||
764
+ configs?.twilio_service_enabled?.value === '1') &&
765
+ configs?.twilio_module?.value && (
766
+ <>
767
+ <OrSeparator>
768
+ <LineSeparator />
769
+ <OText size={18} mRight={20} mLeft={20}>
770
+ {t('OR', 'Or')}
771
+ </OText>
772
+ <LineSeparator />
773
+ </OrSeparator>
774
+
775
+ <ButtonsWrapper mBottom={20}>
776
+ <OButton
777
+ onClick={handleVerifyCodeClick}
778
+ text={t('GET_VERIFY_CODE', 'Get Verify Code')}
779
+ style={loginStyle.btnOutline}
780
+ imgRightSrc={null}
781
+ isLoading={isLoadingVerifyModal}
782
+ indicatorColor={theme.colors.primary}
783
+ />
784
+ </ButtonsWrapper>
785
+ </>
786
+ )}
787
+
788
+ {configs && Object.keys(configs).length > 0 ? (
789
+ (((configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') && configs?.facebook_id?.value && facebookLoginEnabled) ||
790
+ ((configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled) ||
791
+ ((configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && appleLoginEnabled)) && !isGuest &&
792
+ (
793
+ <>
794
+ <View
795
+ style={{
796
+ flexDirection: 'row',
797
+ width: '100%',
798
+ justifyContent: 'space-between',
799
+ alignItems: 'center',
800
+ marginVertical: 15
801
+ }}>
802
+ <View style={loginStyle.line} />
803
+ <OText
804
+ size={14}
805
+ mBottom={10}
806
+ style={{ paddingHorizontal: 19 }}
807
+ color={theme.colors.disabled}>
808
+ {t('OR', 'or')}
809
+ </OText>
810
+ <View style={loginStyle.line} />
811
+ </View>
812
+ <ButtonsWrapper>
813
+ <SocialButtons>
814
+ {(configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') &&
815
+ configs?.facebook_id?.value &&
816
+ facebookLoginEnabled && (
817
+ <FacebookLogin
818
+ notificationState={notificationState}
819
+ handleErrors={(err: any) => { showToast(ToastType.Error, err), vibrateApp() }}
820
+ handleLoading={(val: boolean) => setIsFBLoading(val)}
821
+ handleSuccessFacebookLogin={handleSuccessFacebook}
822
+ />
823
+ )}
824
+ {(configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled && (
825
+ <GoogleLogin
826
+ notificationState={notificationState}
827
+ webClientId={configs?.google_login_client_id?.value}
828
+ handleErrors={(err: any) => { showToast(ToastType.Error, err), vibrateApp() }}
829
+ handleLoading={(val: boolean) => setIsFBLoading(val)}
830
+ handleSuccessGoogleLogin={handleSuccessFacebook}
831
+ />
832
+ )}
833
+ {(configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && appleLoginEnabled && (
834
+ <AppleLogin
835
+ notificationState={notificationState}
836
+ handleErrors={(err: any) => { showToast(ToastType.Error, err), vibrateApp() }}
837
+ handleLoading={(val: boolean) => setIsFBLoading(val)}
838
+ handleSuccessAppleLogin={handleSuccessFacebook}
839
+ />
840
+ )}
841
+ </SocialButtons>
842
+ </ButtonsWrapper>
843
+ </>
844
+ )
845
+ ) : (
846
+ <SkeletonWrapper>
847
+ <Placeholder Animation={Fade}>
848
+ <PlaceholderLine
849
+ height={20}
850
+ style={{ marginBottom: 15, marginTop: 10 }}
851
+ />
852
+ <PlaceholderLine
853
+ height={50}
854
+ style={{ borderRadius: 25, marginBottom: 25 }}
855
+ />
856
+ </Placeholder>
857
+ </SkeletonWrapper>
858
+ )}
859
+
860
+ {enabledPoweredByOrdering && (
861
+ <OText>
862
+ Powered By Ordering.co
863
+ </OText>
864
+ )}
865
+ </FormSide>
866
+ <OModal
867
+ open={isModalVisible}
868
+ onClose={() => setIsModalVisible(false)}
869
+ entireModal
870
+ title={t('VERIFY_PHONE', 'Verify Phone')}
871
+ >
872
+ <VerifyPhone
873
+ phone={phoneInputData.phone}
874
+ verifyPhoneState={verifyPhoneState}
875
+ checkPhoneCodeState={checkPhoneCodeState}
876
+ handleCheckPhoneCode={handleCheckPhoneCode}
877
+ setCheckPhoneCodeState={setCheckPhoneCodeState}
878
+ handleVerifyCodeClick={handleVerifyCodeClick}
879
+ onClose={() => setIsModalVisible(false)}
880
+ />
881
+ </OModal>
882
+ <Modal
883
+ visible={willVerifyOtpState}
884
+ onDismiss={() => setWillVerifyOtpState(false)}
885
+ animationType='slide'
886
+ >
887
+ <Otp
888
+ isCheckingCode={isCheckingCode}
889
+ setCheckingCode={setCheckingCode}
890
+ willVerifyOtpState={willVerifyOtpState}
891
+ otpError={otpError}
892
+ setOtpError={setOtpError}
893
+ setWillVerifyOtpState={setWillVerifyOtpState}
894
+ handleLoginOtp={handleLoginOtp}
895
+ onSubmit={onSubmit}
896
+ setAlertState={setAlertState}
897
+ />
898
+ </Modal>
899
+ <Alert
900
+ open={alertState.open}
901
+ content={alertState.content}
902
+ title={alertState.title || ''}
903
+ onAccept={closeAlert}
904
+ onClose={closeAlert}
905
+ />
906
+ <Spinner visible={isFBLoading} />
907
+ </Container>
908
+ );
884
909
  };
885
910
 
886
911
  export const LoginForm = (props: any) => {
887
- const loginProps = {
888
- ...props,
889
- isRecaptchaEnable: true,
890
- UIComponent: LoginFormUI,
891
- };
892
- return <LoginFormController {...loginProps} />;
912
+ const loginProps = {
913
+ ...props,
914
+ isRecaptchaEnable: true,
915
+ UIComponent: LoginFormUI,
916
+ };
917
+ return <LoginFormController {...loginProps} />;
893
918
  };