ordering-ui-react-native 0.16.24 → 0.16.27

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ordering-ui-react-native",
3
- "version": "0.16.24",
3
+ "version": "0.16.27",
4
4
  "description": "Reusable components made in react native",
5
5
  "main": "src/index.tsx",
6
6
  "author": "ordering.inc",
@@ -112,7 +112,7 @@
112
112
  "react-native-tracking-transparency": "^0.1.1",
113
113
  "react-native-uuid": "^2.0.1",
114
114
  "react-native-vector-icons": "^7.1.0",
115
- "react-native-webview": "^11.6.4",
115
+ "react-native-webview": "^11.22.7",
116
116
  "react-native-youtube-iframe": "^2.2.2",
117
117
  "rn-placeholder": "^3.0.3",
118
118
  "styled-components": "^5.1.1",
@@ -146,16 +146,18 @@ export const BusinessControllerUI = (props: BusinessControllerParams) => {
146
146
  />
147
147
  </BusinessLogo>
148
148
  <BusinessState>
149
- {(isBusinessOpen || !!getBusinessOffer(business?.offers)) && (
149
+ {(!isBusinessOpen || !!getBusinessOffer(business?.offers)) && (
150
150
  <View style={styles.businessStateView}>
151
151
  {getBusinessOffer(business?.offers) && (
152
152
  <OText color={theme.colors.white} size={18} style={styles.businessStateText}>
153
153
  {getBusinessOffer(business?.offers) || parsePrice(0)}
154
154
  </OText>
155
155
  )}
156
- <OText color={theme.colors.white} size={18} style={styles.businessStateText}>
157
- {t('PREORDER', 'PREORDER')}
158
- </OText>
156
+ {!isBusinessOpen && (
157
+ <OText color={theme.colors.white} size={18} style={styles.businessStateText}>
158
+ {t('PREORDER', 'PREORDER')}
159
+ </OText>
160
+ )}
159
161
  </View>
160
162
  )}
161
163
  </BusinessState>
@@ -164,10 +166,10 @@ export const BusinessControllerUI = (props: BusinessControllerParams) => {
164
166
  <BusinessInfo>
165
167
  <View style={{ width: '70%', alignItems: 'flex-start' }}>
166
168
  <OText
167
- size={20}
168
- numberOfLines={1}
169
- ellipsizeMode='tail'
170
- >
169
+ size={20}
170
+ numberOfLines={1}
171
+ ellipsizeMode='tail'
172
+ >
171
173
  {business?.name}
172
174
  </OText>
173
175
  </View>
@@ -1,9 +1,11 @@
1
1
  import React, { useEffect, useState, useRef } from 'react';
2
2
  import { Pressable, StyleSheet, View, Keyboard } from 'react-native';
3
3
  import Spinner from 'react-native-loading-spinner-overlay';
4
+ import { TouchableOpacity } from 'react-native-gesture-handler';
4
5
  import { useForm, Controller } from 'react-hook-form';
5
6
  import { PhoneInputNumber } from '../PhoneInputNumber'
6
7
  import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
8
+ import Recaptcha from 'react-native-recaptcha-that-works'
7
9
 
8
10
  import {
9
11
  LoginForm as LoginFormController,
@@ -28,7 +30,8 @@ import {
28
30
  OTab,
29
31
  SocialButtons,
30
32
  OrSeparator,
31
- LineSeparator
33
+ LineSeparator,
34
+ RecaptchaButton
32
35
  } from './styles';
33
36
 
34
37
  import { _removeStoreData } from '../../providers/StoreUtil';
@@ -56,7 +59,9 @@ const LoginFormUI = (props: LoginParams) => {
56
59
  handleSendVerifyCode,
57
60
  handleCheckPhoneCode,
58
61
  onNavigationRedirect,
59
- notificationState
62
+ notificationState,
63
+ handleReCaptcha,
64
+ enableReCaptcha
60
65
  } = props
61
66
 
62
67
  const theme = useTheme()
@@ -70,6 +75,10 @@ const LoginFormUI = (props: LoginParams) => {
70
75
  marginBottom: 25,
71
76
  borderWidth: 1,
72
77
  borderColor: theme.colors.disabled
78
+ },
79
+ recaptchaIcon: {
80
+ width: 100,
81
+ height: 100,
73
82
  }
74
83
  });
75
84
 
@@ -89,7 +98,9 @@ const LoginFormUI = (props: LoginParams) => {
89
98
  cellphone: null
90
99
  }
91
100
  });
92
-
101
+ const [recaptchaConfig, setRecaptchaConfig] = useState<any>({})
102
+ const [recaptchaVerified, setRecaptchaVerified] = useState(false)
103
+ const recaptchaRef = useRef<any>({});
93
104
  const inputRef = useRef<any>({})
94
105
 
95
106
  const googleLoginEnabled = configs?.google_login_enabled?.value === '1' || !configs?.google_login_enabled?.enabled
@@ -152,6 +163,34 @@ const LoginFormUI = (props: LoginParams) => {
152
163
  onChange(value.toLowerCase().replace(/[&,()%";:ç?<>{}\\[\]\s]/g, ''))
153
164
  }
154
165
 
166
+ const handleOpenRecaptcha = () => {
167
+ setRecaptchaVerified(false)
168
+ if (!recaptchaConfig?.siteKey) {
169
+ showToast(ToastType.Error, t('NO_RECAPTCHA_SITE_KEY', 'The config doesn\'t have recaptcha site key'));
170
+ return
171
+ }
172
+ if (!recaptchaConfig?.baseUrl) {
173
+ showToast(ToastType.Error, t('NO_RECAPTCHA_BASE_URL', 'The config doesn\'t have recaptcha base url'));
174
+ return
175
+ }
176
+
177
+ recaptchaRef.current.open()
178
+ }
179
+
180
+ const onRecaptchaVerify = (token: any) => {
181
+ setRecaptchaVerified(true)
182
+ handleReCaptcha && handleReCaptcha(token)
183
+ }
184
+
185
+ useEffect(() => {
186
+ if (configs && Object.keys(configs).length > 0 && enableReCaptcha) {
187
+ setRecaptchaConfig({
188
+ siteKey: configs?.security_recaptcha_site_key?.value || null,
189
+ baseUrl: configs?.security_recaptcha_base_url?.value || null
190
+ })
191
+ }
192
+ }, [configs, enableReCaptcha])
193
+
155
194
  useEffect(() => {
156
195
  if (!formState.loading && formState.result?.error) {
157
196
  formState.result?.result && showToast(
@@ -317,6 +356,39 @@ const LoginFormUI = (props: LoginParams) => {
317
356
  rules={{ required: t('VALIDATION_ERROR_PASSWORD_REQUIRED', 'The field Password is required').replace('_attribute_', t('PASSWORD', 'Password')) }}
318
357
  defaultValue=""
319
358
  />
359
+
360
+ {enableReCaptcha && (
361
+ <>
362
+ <TouchableOpacity
363
+ onPress={handleOpenRecaptcha}
364
+ >
365
+ <RecaptchaButton>
366
+ {recaptchaVerified ? (
367
+ <MaterialCommunityIcons
368
+ name="checkbox-marked"
369
+ size={26}
370
+ color={theme.colors.primary}
371
+ />
372
+ ) : (
373
+ <MaterialCommunityIcons
374
+ name="checkbox-blank-outline"
375
+ size={26}
376
+ color={theme.colors.mediumGray}
377
+ />
378
+ )}
379
+ <OText size={14} mLeft={8}>{t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')}</OText>
380
+ </RecaptchaButton>
381
+ </TouchableOpacity>
382
+ <Recaptcha
383
+ ref={recaptchaRef}
384
+ siteKey={recaptchaConfig?.siteKey}
385
+ baseUrl={recaptchaConfig?.baseUrl}
386
+ onVerify={onRecaptchaVerify}
387
+ onExpire={() => setRecaptchaVerified(false)}
388
+ />
389
+ </>
390
+ )}
391
+
320
392
  <OButton
321
393
  onClick={handleSubmit(onSubmit)}
322
394
  text={loginButtonText}
@@ -369,13 +441,13 @@ const LoginFormUI = (props: LoginParams) => {
369
441
  }
370
442
 
371
443
  {configs && Object.keys(configs).length > 0 && anySocialButtonActivated && (
372
- <ButtonsWrapper>
373
- <OText size={18} mBottom={10} color={theme.colors.disabled}>
374
- {t('SELECT_AN_OPTION_TO_LOGIN', 'Select an option to login')}
375
- </OText>
376
- <SocialButtons>
377
- {(configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') &&
378
- configs?.facebook_id?.value && (
444
+ <ButtonsWrapper>
445
+ <OText size={18} mBottom={10} color={theme.colors.disabled}>
446
+ {t('SELECT_AN_OPTION_TO_LOGIN', 'Select an option to login')}
447
+ </OText>
448
+ <SocialButtons>
449
+ {(configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') &&
450
+ configs?.facebook_id?.value && (
379
451
  <FacebookLogin
380
452
  notificationState={notificationState}
381
453
  handleErrors={(err: any) => showToast(ToastType.Error, err)}
@@ -383,26 +455,26 @@ const LoginFormUI = (props: LoginParams) => {
383
455
  handleSuccessFacebookLogin={handleSuccessFacebook}
384
456
  />
385
457
  )}
386
- {(configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled && (
387
- <GoogleLogin
388
- notificationState={notificationState}
389
- webClientId={configs?.google_login_client_id?.value}
390
- handleErrors={(err: any) => showToast(ToastType.Error, err)}
391
- handleLoading={(val: boolean) => setIsLoadingSocialButton(val)}
392
- handleSuccessGoogleLogin={handleSuccessFacebook}
393
- />
394
- )}
395
- {(configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && (
396
- <AppleLogin
397
- notificationState={notificationState}
398
- handleErrors={(err: any) => showToast(ToastType.Error, err)}
399
- handleLoading={(val: boolean) => setIsLoadingSocialButton(val)}
400
- handleSuccessApple={handleSuccessApple}
401
- />
402
- )}
403
- </SocialButtons>
404
- </ButtonsWrapper>
405
- )}
458
+ {(configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled && (
459
+ <GoogleLogin
460
+ notificationState={notificationState}
461
+ webClientId={configs?.google_login_client_id?.value}
462
+ handleErrors={(err: any) => showToast(ToastType.Error, err)}
463
+ handleLoading={(val: boolean) => setIsLoadingSocialButton(val)}
464
+ handleSuccessGoogleLogin={handleSuccessFacebook}
465
+ />
466
+ )}
467
+ {(configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && (
468
+ <AppleLogin
469
+ notificationState={notificationState}
470
+ handleErrors={(err: any) => showToast(ToastType.Error, err)}
471
+ handleLoading={(val: boolean) => setIsLoadingSocialButton(val)}
472
+ handleSuccessApple={handleSuccessApple}
473
+ />
474
+ )}
475
+ </SocialButtons>
476
+ </ButtonsWrapper>
477
+ )}
406
478
 
407
479
  {onNavigationRedirect && registerButtonText && (
408
480
  <ButtonsWrapper>
@@ -440,6 +512,7 @@ const LoginFormUI = (props: LoginParams) => {
440
512
  export const LoginForm = (props: any) => {
441
513
  const loginProps = {
442
514
  ...props,
515
+ isRecaptchaEnable: true,
443
516
  UIComponent: LoginFormUI,
444
517
  handleSuccessLogin: () => _removeStoreData('isGuestUser')
445
518
  };
@@ -67,3 +67,9 @@ export const LineSeparator = styled.View`
67
67
  export const SkeletonWrapper = styled.View`
68
68
  width: 90%;
69
69
  `
70
+ export const RecaptchaButton = styled.View`
71
+ flex-direction: row;
72
+ align-items: center;
73
+ margin-bottom: 10px;
74
+ justify-content: center;
75
+ `
@@ -3,6 +3,7 @@ import { View, Pressable, StyleSheet, Keyboard, Linking, Platform, TouchableOpac
3
3
  import { useForm, Controller } from 'react-hook-form';
4
4
  import Spinner from 'react-native-loading-spinner-overlay';
5
5
  import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
6
+ import Recaptcha from 'react-native-recaptcha-that-works'
6
7
 
7
8
  import { PhoneInputNumber } from '../PhoneInputNumber'
8
9
  import { FacebookLogin } from '../FacebookLogin'
@@ -23,7 +24,7 @@ import {
23
24
  SocialButtons
24
25
  } from './styles'
25
26
 
26
- import { LoginWith as SignupWith, OTab, OTabs } from '../LoginForm/styles'
27
+ import { LoginWith as SignupWith, OTab, OTabs, RecaptchaButton } from '../LoginForm/styles'
27
28
 
28
29
  import { _removeStoreData } from '../../providers/StoreUtil';
29
30
  import NavBar from '../NavBar'
@@ -58,7 +59,9 @@ const SignupFormUI = (props: SignupParams) => {
58
59
  setCheckPhoneCodeState,
59
60
  handleSendVerifyCode,
60
61
  handleCheckPhoneCode,
61
- notificationState
62
+ notificationState,
63
+ enableReCaptcha,
64
+ handleReCaptcha
62
65
  } = props
63
66
 
64
67
  const theme = useTheme()
@@ -107,6 +110,9 @@ const SignupFormUI = (props: SignupParams) => {
107
110
  }
108
111
  });
109
112
 
113
+ const [recaptchaConfig, setRecaptchaConfig] = useState<any>({})
114
+ const [recaptchaVerified, setRecaptchaVerified] = useState(false)
115
+
110
116
  const nameRef = useRef<any>(null)
111
117
  const lastnameRef = useRef<any>(null)
112
118
  const middleNameRef = useRef<any>(null)
@@ -114,6 +120,7 @@ const SignupFormUI = (props: SignupParams) => {
114
120
  const emailRef = useRef<any>(null)
115
121
  const phoneRef = useRef<any>(null)
116
122
  const passwordRef = useRef<any>(null)
123
+ const recaptchaRef = useRef<any>({});
117
124
 
118
125
  const googleLoginEnabled = configs?.google_login_enabled?.value === '1' || !configs?.google_login_enabled?.enabled
119
126
 
@@ -196,35 +203,6 @@ const SignupFormUI = (props: SignupParams) => {
196
203
  setPasswordSee(false);
197
204
  }
198
205
 
199
- const onSubmit = (values: any) => {
200
- Keyboard.dismiss()
201
- if (phoneInputData.error) {
202
- showToast(ToastType.Error, phoneInputData.error);
203
- return
204
- }
205
- if (
206
- !phoneInputData.phone.country_phone_code &&
207
- !phoneInputData.phone.cellphone &&
208
- validationFields?.fields?.checkout?.cellphone?.enabled &&
209
- validationFields?.fields?.checkout?.cellphone?.required
210
- ) {
211
- showToast(ToastType.Error, t('VALIDATION_ERROR_MOBILE_PHONE_REQUIRED', 'The field Mobile phone is required.'))
212
- return
213
- }
214
- if (signupTab === 'email' || !useSignupByCellphone) {
215
- handleButtonSignupClick && handleButtonSignupClick({
216
- ...values,
217
- ...phoneInputData.phone
218
- })
219
- if (!formState.loading && formState.result.result && !formState.result.error) {
220
- handleSuccessSignup && handleSuccessSignup(formState.result.result)
221
- }
222
- return
223
- }
224
- setFormValues(values)
225
- handleVerifyCodeClick(values)
226
- }
227
-
228
206
  const handleVerifyCodeClick = (values: any) => {
229
207
  const formData = values || formValues
230
208
  handleSendVerifyCode && handleSendVerifyCode({
@@ -265,6 +243,62 @@ const SignupFormUI = (props: SignupParams) => {
265
243
  }
266
244
  }
267
245
 
246
+ const handleOpenRecaptcha = () => {
247
+ setRecaptchaVerified(false)
248
+ if (!recaptchaConfig?.siteKey) {
249
+ showToast(ToastType.Error, t('NO_RECAPTCHA_SITE_KEY', 'The config doesn\'t have recaptcha site key'));
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
+ return
255
+ }
256
+ recaptchaRef.current.open()
257
+ }
258
+
259
+ const onRecaptchaVerify = (token: any) => {
260
+ setRecaptchaVerified(true)
261
+ handleReCaptcha && handleReCaptcha(token)
262
+ }
263
+
264
+ const onSubmit = (values: any) => {
265
+ Keyboard.dismiss()
266
+ if (phoneInputData.error) {
267
+ showToast(ToastType.Error, phoneInputData.error);
268
+ return
269
+ }
270
+ if (
271
+ !phoneInputData.phone.country_phone_code &&
272
+ !phoneInputData.phone.cellphone &&
273
+ validationFields?.fields?.checkout?.cellphone?.enabled &&
274
+ validationFields?.fields?.checkout?.cellphone?.required
275
+ ) {
276
+ showToast(ToastType.Error, t('VALIDATION_ERROR_MOBILE_PHONE_REQUIRED', 'The field Mobile phone is required.'))
277
+ return
278
+ }
279
+ if (signupTab === 'email' || !useSignupByCellphone) {
280
+ handleButtonSignupClick && handleButtonSignupClick({
281
+ ...values,
282
+ ...phoneInputData.phone
283
+ })
284
+ if (!formState.loading && formState.result.result && !formState.result.error) {
285
+ handleSuccessSignup && handleSuccessSignup(formState.result.result)
286
+ }
287
+ return
288
+ }
289
+ setFormValues(values)
290
+ handleVerifyCodeClick(values)
291
+ }
292
+
293
+ useEffect(() => {
294
+ if (configs && Object.keys(configs).length > 0 && enableReCaptcha) {
295
+ setRecaptchaConfig({
296
+ siteKey: configs?.security_recaptcha_site_key?.value || null,
297
+ baseUrl: configs?.security_recaptcha_base_url?.value || null
298
+ })
299
+ }
300
+ }, [configs, enableReCaptcha])
301
+
268
302
  useEffect(() => {
269
303
  if (!formState.loading && formState.result?.error) {
270
304
  formState.result?.result && showToast(
@@ -492,6 +526,38 @@ const SignupFormUI = (props: SignupParams) => {
492
526
  </View>
493
527
  )}
494
528
 
529
+ {enableReCaptcha && (
530
+ <>
531
+ <TouchableOpacity
532
+ onPress={handleOpenRecaptcha}
533
+ style={{ marginHorizontal: 4, marginBottom: 10 }}
534
+ >
535
+ <RecaptchaButton>
536
+ {recaptchaVerified ? (
537
+ <MaterialCommunityIcons
538
+ name="checkbox-marked"
539
+ size={23}
540
+ color={theme.colors.primary}
541
+ />
542
+ ) : (
543
+ <MaterialCommunityIcons
544
+ name="checkbox-blank-outline"
545
+ size={23}
546
+ color={theme.colors.disabled}
547
+ />
548
+ )}
549
+ <OText size={14} mLeft={8}>{t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')}</OText>
550
+ </RecaptchaButton>
551
+ </TouchableOpacity>
552
+ <Recaptcha
553
+ ref={recaptchaRef}
554
+ siteKey={recaptchaConfig?.siteKey}
555
+ baseUrl={recaptchaConfig?.baseUrl}
556
+ onVerify={onRecaptchaVerify}
557
+ onExpire={() => setRecaptchaVerified(false)}
558
+ />
559
+ </>
560
+ )}
495
561
  {signupTab === 'cellphone' && useSignupByEmail && useSignupByCellphone ? (
496
562
  <OButton
497
563
  onClick={handleSubmit(onSubmit)}
@@ -531,40 +597,40 @@ const SignupFormUI = (props: SignupParams) => {
531
597
  }
532
598
 
533
599
  {configs && Object.keys(configs).length > 0 && anySocialButtonActivated && (
534
- <ButtonsSection>
535
- <OText size={18} mBottom={10} color={theme.colors.disabled}>
536
- {t('SELECT_AN_OPTION_TO_LOGIN', 'Select an option to login')}
537
- </OText>
538
- <SocialButtons>
539
- {(configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') &&
540
- configs?.facebook_id?.value && (
541
- <FacebookLogin
542
- notificationState={notificationState}
543
- handleErrors={(err: any) => showToast(ToastType.Error, err)}
544
- handleLoading={(val: boolean) => setIsLoadingSocialButton(val)}
545
- handleSuccessFacebookLogin={handleSuccessFacebook}
546
- />
547
- )}
548
- {(configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled && (
549
- <GoogleLogin
550
- notificationState={notificationState}
551
- webClientId={configs?.google_login_client_id?.value}
552
- handleErrors={(err: any) => showToast(ToastType.Error, err)}
553
- handleLoading={(val: boolean) => setIsLoadingSocialButton(val)}
554
- handleSuccessGoogleLogin={handleSuccessFacebook}
555
- />
556
- )}
557
- {(configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && (
558
- <AppleLogin
600
+ <ButtonsSection>
601
+ <OText size={18} mBottom={10} color={theme.colors.disabled}>
602
+ {t('SELECT_AN_OPTION_TO_LOGIN', 'Select an option to login')}
603
+ </OText>
604
+ <SocialButtons>
605
+ {(configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') &&
606
+ configs?.facebook_id?.value && (
607
+ <FacebookLogin
559
608
  notificationState={notificationState}
560
609
  handleErrors={(err: any) => showToast(ToastType.Error, err)}
561
610
  handleLoading={(val: boolean) => setIsLoadingSocialButton(val)}
562
- handleSuccessApple={handleSuccessApple}
611
+ handleSuccessFacebookLogin={handleSuccessFacebook}
563
612
  />
564
613
  )}
565
- </SocialButtons>
566
- </ButtonsSection>
567
- )}
614
+ {(configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled && (
615
+ <GoogleLogin
616
+ notificationState={notificationState}
617
+ webClientId={configs?.google_login_client_id?.value}
618
+ handleErrors={(err: any) => showToast(ToastType.Error, err)}
619
+ handleLoading={(val: boolean) => setIsLoadingSocialButton(val)}
620
+ handleSuccessGoogleLogin={handleSuccessFacebook}
621
+ />
622
+ )}
623
+ {(configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && (
624
+ <AppleLogin
625
+ notificationState={notificationState}
626
+ handleErrors={(err: any) => showToast(ToastType.Error, err)}
627
+ handleLoading={(val: boolean) => setIsLoadingSocialButton(val)}
628
+ handleSuccessApple={handleSuccessApple}
629
+ />
630
+ )}
631
+ </SocialButtons>
632
+ </ButtonsSection>
633
+ )}
568
634
  </FormSide>
569
635
  <OModal
570
636
  open={isModalVisible}
@@ -581,13 +647,14 @@ const SignupFormUI = (props: SignupParams) => {
581
647
  />
582
648
  </OModal>
583
649
  <Spinner visible={formState.loading || isLoadingSocialButton} />
584
- </View >
650
+ </View>
585
651
  );
586
652
  };
587
653
 
588
654
  export const SignupForm = (props: any) => {
589
655
  const signupProps = {
590
656
  ...props,
657
+ isRecaptchaEnable: true,
591
658
  UIComponent: SignupFormUI,
592
659
  };
593
660
  return <SignUpController {...signupProps} />;
@@ -17,6 +17,8 @@ export interface LoginParams {
17
17
  handleSendVerifyCode?: any;
18
18
  handleCheckPhoneCode?: any;
19
19
  notificationState?: any;
20
+ handleReCaptcha?: (token: string) => void;
21
+ enableReCaptcha?: any;
20
22
  }
21
23
  export interface ProfileParams {
22
24
  navigation?: any;
@@ -99,6 +101,8 @@ export interface SignupParams {
99
101
  handleSendVerifyCode?: any;
100
102
  handleCheckPhoneCode?: any;
101
103
  notificationState?: any;
104
+ enableReCaptcha?: boolean;
105
+ handleReCaptcha?: (token: string) => void;
102
106
  }
103
107
 
104
108
  export interface PhoneInputParams {
@@ -248,8 +252,8 @@ export interface OrdersOptionParams {
248
252
  navigation?: any,
249
253
  loadOrders?: any,
250
254
  setOrderList?: any,
251
- setOrdersLength?: ({activeOrdersLength, previousOrdersLength} : {activeOrdersLength: number, previousOrdersLength: number}) => void,
252
- ordersLength: {activeOrdersLength: number, previousOrdersLength: number}
255
+ setOrdersLength?: ({ activeOrdersLength, previousOrdersLength }: { activeOrdersLength: number, previousOrdersLength: number }) => void,
256
+ ordersLength: { activeOrdersLength: number, previousOrdersLength: number }
253
257
  }
254
258
  export interface ActiveOrdersParams {
255
259
  orders?: any,
@@ -347,7 +351,7 @@ export interface MessagesParams {
347
351
  messages?: any,
348
352
  message?: string,
349
353
  image?: string,
350
- messagesToShow?: any ,
354
+ messagesToShow?: any,
351
355
  sendMessage?: any,
352
356
  handleSend?: () => {},
353
357
  setImage?: (image: string | null) => {},
@@ -416,7 +420,7 @@ export interface UpsellingProductsParams {
416
420
  setOpenUpselling?: any;
417
421
  onRedirect?: any;
418
422
  businessId?: number;
419
- cartProducts?: Array<any>;
423
+ cartProducts?: Array<any>;
420
424
  handleUpsellingPage: () => void;
421
425
  openUpselling: boolean;
422
426
  canOpenUpselling?: boolean;
@@ -424,16 +428,16 @@ export interface UpsellingProductsParams {
424
428
  }
425
429
 
426
430
  export interface GoogleMapsParams {
427
- location: {lat: number, lng: number}
428
- handleChangeAddressMap?: (address : any, details : any) => void
429
- setErrors?: (error : string) => void
431
+ location: { lat: number, lng: number }
432
+ handleChangeAddressMap?: (address: any, details: any) => void
433
+ setErrors?: (error: string) => void
430
434
  maxLimitLocation?: number
431
435
  readOnly?: boolean
432
436
  markerTitle?: string,
433
437
  saveLocation?: boolean,
434
438
  isSetInputs?: boolean,
435
439
  locations?: Array<any>,
436
- setSaveLocation?: (val : boolean) => void,
440
+ setSaveLocation?: (val: boolean) => void,
437
441
  handleToggleMap?: () => void
438
442
  }
439
443
 
@@ -481,7 +485,7 @@ export interface HelpAccountAndPaymentParams {
481
485
 
482
486
  export interface StripeMethodFormParams {
483
487
  cart: any;
484
- handleSource: ({id, card} : {id : string, card : any}) => void;
488
+ handleSource: ({ id, card }: { id: string, card: any }) => void;
485
489
  onCancel: () => void;
486
490
  setErrors: (error: string) => void;
487
491
  paymethod: string;
@@ -25,15 +25,17 @@ import {
25
25
  SortContainer,
26
26
  BrandContainer,
27
27
  BrandItem,
28
- PriceFilterWrapper
28
+ PriceFilterWrapper,
29
+ OptionTitle
29
30
  } from './styles'
30
31
  import FastImage from 'react-native-fast-image'
31
32
  import { convertHoursToMinutes } from '../../utils'
32
33
  import { Fade, Placeholder, PlaceholderLine } from 'rn-placeholder'
33
34
  import { BusinessSearchParams } from '../../types'
35
+ import { MyOrders } from '../MyOrders'
34
36
 
35
37
 
36
- export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
38
+ export const BusinessListingSearchUI = (props: BusinessSearchParams) => {
37
39
  const {
38
40
  navigation,
39
41
  businessesSearchList,
@@ -47,10 +49,11 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
47
49
  businessTypes,
48
50
  setFilters,
49
51
  brandList,
52
+ onNavigationRedirect,
50
53
  handleUpdateBusinessList,
51
54
  handleUpdateProducts
52
55
  } = props
53
-
56
+
54
57
  const screenHeight = Dimensions.get('window').height;
55
58
  const theme = useTheme()
56
59
  const [orderState] = useOrder()
@@ -224,6 +227,60 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
224
227
  )
225
228
  }
226
229
 
230
+ const BusinessControllerSkeletons = () => {
231
+ return (
232
+ <>
233
+ {[
234
+ ...Array(
235
+ paginationProps.nextPageItems
236
+ ? paginationProps.nextPageItems
237
+ : 3,
238
+ ).keys(),
239
+ ].map((item, i) => (
240
+ <Placeholder
241
+ Animation={Fade}
242
+ key={i}
243
+ style={{ width: 320, marginRight: 20, marginTop: 20 }}>
244
+ <View style={{ width: 320 }}>
245
+ <PlaceholderLine
246
+ height={155}
247
+ style={{ marginBottom: 20, borderRadius: 25 }}
248
+ />
249
+ <View style={{ paddingHorizontal: 10 }}>
250
+ <View
251
+ style={{
252
+ flexDirection: 'row',
253
+ justifyContent: 'space-between',
254
+ }}>
255
+ <PlaceholderLine
256
+ height={25}
257
+ width={40}
258
+ style={{ marginBottom: 10 }}
259
+ />
260
+ <PlaceholderLine
261
+ height={25}
262
+ width={20}
263
+ style={{ marginBottom: 10 }}
264
+ />
265
+ </View>
266
+ <PlaceholderLine
267
+ height={20}
268
+ width={30}
269
+ style={{ marginBottom: 10 }}
270
+ />
271
+ <PlaceholderLine
272
+ height={20}
273
+ width={80}
274
+ style={{ marginBottom: 0 }}
275
+ />
276
+ </View>
277
+ </View>
278
+ </Placeholder>
279
+ ))}
280
+ </>
281
+ )
282
+ }
283
+
227
284
  return (
228
285
  <ScrollView style={styles.container}>
229
286
  <WrapHeader style={{ paddingTop: top + 20, marginVertical: 2 }}>
@@ -259,6 +316,20 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
259
316
  </View>
260
317
  )
261
318
  }
319
+ {businessesSearchList.businesses?.length > 0 && (
320
+ <MyOrders
321
+ hideOrders
322
+ businessesSearchList={businessesSearchList}
323
+ onNavigationRedirect={onNavigationRedirect}
324
+ BusinessControllerSkeletons={BusinessControllerSkeletons}
325
+ />
326
+ )}
327
+
328
+ <OptionTitle isBusinessesSearchList={!!businessesSearchList}>
329
+ <OText size={16} lineHeight={24} weight={'500'} color={theme.colors.textNormal} mBottom={10}>
330
+ {t('BUSINESSES', 'Businesses')}
331
+ </OText>
332
+ </OptionTitle>
262
333
  <ScrollView horizontal>
263
334
  {businessesSearchList.businesses?.length > 0 && businessesSearchList.businesses.map((business: any, i: number) => (
264
335
  <BusinessController
@@ -283,55 +354,7 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
283
354
  </LoadMoreBusinessContainer>
284
355
  )}
285
356
  {businessesSearchList.loading && (
286
- <>
287
- {[
288
- ...Array(
289
- paginationProps.nextPageItems
290
- ? paginationProps.nextPageItems
291
- : 3,
292
- ).keys(),
293
- ].map((item, i) => (
294
- <Placeholder
295
- Animation={Fade}
296
- key={i}
297
- style={{ width: 320, marginRight: 20, marginTop: 20 }}>
298
- <View style={{ width: 320 }}>
299
- <PlaceholderLine
300
- height={155}
301
- style={{ marginBottom: 20, borderRadius: 25 }}
302
- />
303
- <View style={{ paddingHorizontal: 10 }}>
304
- <View
305
- style={{
306
- flexDirection: 'row',
307
- justifyContent: 'space-between',
308
- }}>
309
- <PlaceholderLine
310
- height={25}
311
- width={40}
312
- style={{ marginBottom: 10 }}
313
- />
314
- <PlaceholderLine
315
- height={25}
316
- width={20}
317
- style={{ marginBottom: 10 }}
318
- />
319
- </View>
320
- <PlaceholderLine
321
- height={20}
322
- width={30}
323
- style={{ marginBottom: 10 }}
324
- />
325
- <PlaceholderLine
326
- height={20}
327
- width={80}
328
- style={{ marginBottom: 0 }}
329
- />
330
- </View>
331
- </View>
332
- </Placeholder>
333
- ))}
334
- </>
357
+ <BusinessControllerSkeletons />
335
358
  )}
336
359
  </ScrollView>
337
360
  <ProductsList>
@@ -456,7 +479,7 @@ export const BusinessListingSearchUI = (props : BusinessSearchParams) => {
456
479
  onPress={() => handleChangeFilters('orderBy', item?.value)}
457
480
  style={{ marginBottom: 7 }}
458
481
  >
459
- <OText
482
+ <OText
460
483
  weight={filters?.orderBy?.includes(item?.value) ? 'bold' : '500'}
461
484
  mBottom={filters?.orderBy?.includes(item?.value) ? 5 : 0}
462
485
  >
@@ -1,4 +1,4 @@
1
- import styled from 'styled-components/native'
1
+ import styled, { css } from 'styled-components/native'
2
2
 
3
3
  export const WrapHeader = styled.View`
4
4
  width: 100%;
@@ -87,3 +87,10 @@ export const BrandItem = styled.TouchableOpacity`
87
87
  export const PriceFilterWrapper = styled.View`
88
88
  margin-bottom: 20px;
89
89
  `
90
+
91
+ export const OptionTitle = styled.View`
92
+ margin-top: 24px;
93
+ ${(props: any) => props.titleContent && css`
94
+ margin-left: ${() => props.isBusinessesSearchList ? '0' : '40px'};
95
+ `}
96
+ `
@@ -216,7 +216,7 @@ const PaymentOptionsUI = (props: any) => {
216
216
 
217
217
  {paymethodSelected?.gateway === 'cash' && (
218
218
  <PaymentOptionCash
219
- orderTotal={cart.total}
219
+ orderTotal={cart.balance ?? cart.total}
220
220
  defaultValue={paymethodSelected?.data?.cash}
221
221
  onChangeData={handlePaymethodDataChange}
222
222
  setErrorCash={props.setErrorCash}
@@ -9,13 +9,20 @@ import {
9
9
  import { useTheme } from 'styled-components/native';
10
10
  import { SingleProductCardParams } from '../../types';
11
11
  import { CardContainer, CardInfo, SoldOut, QuantityContainer, PricesContainer, RibbonBox, LogoWrapper } from './styles';
12
- import { StyleSheet, View, TouchableOpacity } from 'react-native';
13
- import { OText, OIcon } from '../shared';
12
+ import { StyleSheet, View, TouchableOpacity, Image } from 'react-native';
13
+ import { OText } from '../shared';
14
14
  import FastImage from 'react-native-fast-image'
15
15
  import IconAntDesign from 'react-native-vector-icons/AntDesign'
16
16
  import { shape } from '../../utils';
17
17
 
18
- const SinguleProductCardUI = (props: SingleProductCardParams) => {
18
+ function SingleProductCardPropsAreEqual(prevProps: any, nextProps: any) {
19
+ return JSON.stringify(prevProps.product) === JSON.stringify(nextProps.product) &&
20
+ prevProps.isSoldOut === nextProps.isSoldOut &&
21
+ prevProps.productAddedToCartLength === nextProps.productAddedToCartLength &&
22
+ prevProps.categoryState === nextProps.categoryState
23
+ }
24
+
25
+ const SinguleProductCardUI = React.memo((props: SingleProductCardParams) => {
19
26
  const {
20
27
  product,
21
28
  isSoldOut,
@@ -188,9 +195,13 @@ const SinguleProductCardUI = (props: SingleProductCardParams) => {
188
195
  resizeMode={FastImage.resizeMode.cover}
189
196
  />
190
197
  ) : (
191
- <OIcon
192
- src={theme?.images?.dummies?.product}
198
+ <FastImage
193
199
  style={styles.productStyle}
200
+ source={{
201
+ uri: Image.resolveAssetSource(theme.images.dummies.product).uri,
202
+ priority: FastImage.priority.normal,
203
+ }}
204
+ resizeMode={FastImage.resizeMode.cover}
194
205
  />
195
206
  )}
196
207
  </LogoWrapper>
@@ -204,7 +215,7 @@ const SinguleProductCardUI = (props: SingleProductCardParams) => {
204
215
  )}
205
216
  </CardContainer>
206
217
  );
207
- };
218
+ }, SingleProductCardPropsAreEqual);
208
219
 
209
220
  export const SingleProductCard = (props: SingleProductCardParams) => {
210
221
  const singleProductCardProps = {
@@ -605,6 +605,7 @@ export interface BusinessSearchParams {
605
605
  setFilters: (filters: any) => void,
606
606
  lazySearch?: boolean,
607
607
  brandList?: any;
608
+ onNavigationRedirect?: any,
608
609
  handleUpdateProducts: any,
609
610
  handleUpdateBusinessList?: any;
610
611
  }