ordering-ui-react-native 0.15.50 → 0.15.53
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
|
import React, { useEffect } from 'react';
|
|
2
2
|
import { Platform, Text, StyleSheet } from 'react-native';
|
|
3
|
-
import { useApi, useSession, useLanguage } from 'ordering-components/native';
|
|
3
|
+
import { useApi, useSession, useLanguage, useConfig } from 'ordering-components/native';
|
|
4
4
|
import { appleAuthAndroid, appleAuth } from '@invertase/react-native-apple-authentication';
|
|
5
5
|
import uuid from 'react-native-uuid';
|
|
6
6
|
import Icon from 'react-native-vector-icons/FontAwesome5';
|
|
@@ -16,12 +16,12 @@ export const AppleLogin = (props: any) => {
|
|
|
16
16
|
} = props
|
|
17
17
|
|
|
18
18
|
const [ordering] = useApi();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
const [{ auth }] = useSession();
|
|
20
|
+
const [, t] = useLanguage();
|
|
21
|
+
const [{ configs }] = useConfig()
|
|
22
|
+
const buttonText = auth
|
|
23
|
+
? t('CONTINUE_WITH_APPLE', 'Logout with Apple')
|
|
24
|
+
: t('CONTINUE_WITH_FACEBOOK', 'Continue with Apple');
|
|
25
25
|
|
|
26
26
|
const performAppleLogin = async (code: string) => {
|
|
27
27
|
try {
|
|
@@ -32,9 +32,10 @@ export const AppleLogin = (props: any) => {
|
|
|
32
32
|
code: code
|
|
33
33
|
})
|
|
34
34
|
})
|
|
35
|
-
|
|
35
|
+
const { result, error } = await response.json()
|
|
36
|
+
if (!error) {
|
|
36
37
|
if (handleSuccessAppleLogin) {
|
|
37
|
-
handleSuccessAppleLogin(
|
|
38
|
+
handleSuccessAppleLogin(result)
|
|
38
39
|
handleLoading && handleLoading(false)
|
|
39
40
|
}
|
|
40
41
|
} else {
|
|
@@ -52,54 +53,58 @@ export const AppleLogin = (props: any) => {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
const onIOSButtonPress = async () => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
56
|
+
try {
|
|
57
|
+
const appleAuthRequestResponse = await appleAuth.performRequest({
|
|
58
|
+
requestedOperation: appleAuth.Operation.LOGIN,
|
|
59
|
+
requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// get current authentication state for user
|
|
63
|
+
// /!\ This method must be tested on a real device. On the iOS simulator it always throws an error.
|
|
64
|
+
const credentialState = await appleAuth.getCredentialStateForUser(appleAuthRequestResponse.user);
|
|
65
|
+
|
|
66
|
+
// use credentialState response to ensure the user is authenticated
|
|
67
|
+
if (credentialState === appleAuth.State.AUTHORIZED) {
|
|
68
|
+
// user is authenticated
|
|
69
|
+
if (appleAuthRequestResponse.authorizationCode) {
|
|
70
|
+
performAppleLogin(appleAuthRequestResponse.authorizationCode)
|
|
71
|
+
}
|
|
70
72
|
}
|
|
73
|
+
} catch (err: any) {
|
|
74
|
+
handleLoading && handleLoading(false)
|
|
75
|
+
handleErrors && handleErrors(err.message)
|
|
71
76
|
}
|
|
72
|
-
|
|
73
77
|
}
|
|
74
|
-
|
|
75
78
|
const onAndroidButtonPress = async () => {
|
|
76
|
-
// Generate secure, random values for state and nonce
|
|
77
|
-
const rawNonce: any = uuid.v4();
|
|
78
|
-
const state: any = uuid.v4();
|
|
79
|
-
|
|
80
|
-
// Configure the request
|
|
81
|
-
appleAuthAndroid.configure({
|
|
82
|
-
clientId: 'com.example.client-android',
|
|
83
|
-
// Return URL added to your Apple dev console. We intercept this redirect, but it must still match
|
|
84
|
-
// the URL you provided to Apple. It can be an empty route on your backend as it's never called.
|
|
85
|
-
redirectUri: 'https://example.com/auth/callback',
|
|
86
|
-
responseType: appleAuthAndroid.ResponseType.ALL,
|
|
87
|
-
scope: appleAuthAndroid.Scope.ALL,
|
|
88
|
-
// Random nonce value that will be SHA256 hashed before sending to Apple.
|
|
89
|
-
nonce: rawNonce,
|
|
90
|
-
state,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// Open the browser window for user sign in
|
|
94
|
-
const response = await appleAuthAndroid.signIn();
|
|
95
|
-
|
|
96
79
|
try {
|
|
80
|
+
// Generate secure, random values for state and nonce
|
|
81
|
+
const rawNonce: any = uuid.v4();
|
|
82
|
+
const state: any = uuid.v4();
|
|
83
|
+
|
|
84
|
+
// Configure the request
|
|
85
|
+
appleAuthAndroid.configure({
|
|
86
|
+
// The Service ID you registered with Apple
|
|
87
|
+
clientId: configs?.apple_login_client_id?.value,
|
|
88
|
+
// Return URL added to your Apple dev console. We intercept this redirect, but it must still match
|
|
89
|
+
// the URL you provided to Apple. It can be an empty route on your backend as it's never called.
|
|
90
|
+
redirectUri: 'https://example.com/auth/callback',
|
|
91
|
+
responseType: appleAuthAndroid.ResponseType.ALL,
|
|
92
|
+
scope: appleAuthAndroid.Scope.ALL,
|
|
93
|
+
// Random nonce value that will be SHA256 hashed before sending to Apple.
|
|
94
|
+
nonce: rawNonce,
|
|
95
|
+
state,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Open the browser window for user sign in
|
|
99
|
+
const response = await appleAuthAndroid.signIn();
|
|
97
100
|
if (response.code) {
|
|
98
101
|
performAppleLogin(response.code)
|
|
99
102
|
}
|
|
100
103
|
} catch (err: any) {
|
|
101
|
-
|
|
104
|
+
handleLoading && handleLoading(false)
|
|
105
|
+
handleErrors && handleErrors(err.message)
|
|
102
106
|
}
|
|
107
|
+
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
useEffect(() => {
|
|
@@ -115,35 +120,36 @@ export const AppleLogin = (props: any) => {
|
|
|
115
120
|
if (Platform.OS === 'android') return appleAuthAndroid.isSupported;
|
|
116
121
|
return false;
|
|
117
122
|
}
|
|
123
|
+
|
|
118
124
|
return (
|
|
119
125
|
<Container>
|
|
120
|
-
{canShowButton() &&
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
126
|
+
{canShowButton() &&
|
|
127
|
+
<AppleButton
|
|
128
|
+
onPress={() => Platform.OS == 'android' ? onAndroidButtonPress() : onIOSButtonPress()}
|
|
129
|
+
>
|
|
130
|
+
<Icon
|
|
131
|
+
name="apple"
|
|
132
|
+
size={20}
|
|
133
|
+
color={'black'}
|
|
134
|
+
style={style.fbBtn}
|
|
135
|
+
/>
|
|
136
|
+
<Text style={style.textBtn}>
|
|
137
|
+
{buttonText}
|
|
138
|
+
</Text>
|
|
139
|
+
</AppleButton>
|
|
134
140
|
}
|
|
135
141
|
</Container>
|
|
136
142
|
);
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
const style = StyleSheet.create({
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
})
|
|
146
|
+
fbBtn: {
|
|
147
|
+
position: 'absolute',
|
|
148
|
+
left: 0,
|
|
149
|
+
marginHorizontal: 16
|
|
150
|
+
},
|
|
151
|
+
textBtn: {
|
|
152
|
+
fontSize: 14,
|
|
153
|
+
color: '#000000'
|
|
154
|
+
}
|
|
155
|
+
})
|
|
@@ -3,14 +3,20 @@ import { TouchableOpacity } from 'react-native';
|
|
|
3
3
|
import { LogoutAction } from 'ordering-components/native';
|
|
4
4
|
import { useTheme } from 'styled-components/native';
|
|
5
5
|
import { OIcon, OText } from '../shared';
|
|
6
|
+
import { _retrieveStoreData } from '../../providers/StoreUtil';
|
|
6
7
|
|
|
7
8
|
const LogoutButtonUI = (props: any) => {
|
|
8
9
|
const { handleLogoutClick, text, color, iconSize } = props
|
|
9
10
|
const theme = useTheme();
|
|
10
11
|
|
|
12
|
+
const handleClick = async () => {
|
|
13
|
+
const data = await _retrieveStoreData('notification_state');
|
|
14
|
+
handleLogoutClick(data)
|
|
15
|
+
};
|
|
16
|
+
|
|
11
17
|
return (
|
|
12
18
|
<TouchableOpacity
|
|
13
|
-
onPress={() =>
|
|
19
|
+
onPress={() => handleClick()}
|
|
14
20
|
style={{ flexDirection: 'row', alignItems: 'center' }}
|
|
15
21
|
>
|
|
16
22
|
<OIcon
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { View, Pressable, StyleSheet, Linking, Platform } from 'react-native';
|
|
2
|
+
import { View, Pressable, StyleSheet, Linking, Platform, TouchableOpacity } from 'react-native';
|
|
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
6
|
import CheckBox from '@react-native-community/checkbox';
|
|
7
7
|
import { PhoneInputNumber } from '../PhoneInputNumber';
|
|
8
8
|
import { FacebookLogin } from '../FacebookLogin';
|
|
9
|
+
import Recaptcha from 'react-native-recaptcha-that-works'
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
SignupForm as SignUpController,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
LoginWith as SignupWith,
|
|
24
25
|
OTab,
|
|
25
26
|
OTabs,
|
|
27
|
+
RecaptchaButton
|
|
26
28
|
} from '../LoginForm/styles';
|
|
27
29
|
|
|
28
30
|
import NavBar from '../NavBar';
|
|
@@ -63,7 +65,9 @@ const SignupFormUI = (props: SignupParams) => {
|
|
|
63
65
|
handleSendVerifyCode,
|
|
64
66
|
handleCheckPhoneCode,
|
|
65
67
|
notificationState,
|
|
66
|
-
handleChangePromotions
|
|
68
|
+
handleChangePromotions,
|
|
69
|
+
enableReCaptcha,
|
|
70
|
+
handleReCaptcha
|
|
67
71
|
} = props;
|
|
68
72
|
|
|
69
73
|
const theme = useTheme();
|
|
@@ -117,6 +121,8 @@ const SignupFormUI = (props: SignupParams) => {
|
|
|
117
121
|
cellphone: null,
|
|
118
122
|
},
|
|
119
123
|
});
|
|
124
|
+
const [recaptchaConfig, setRecaptchaConfig] = useState<any>({})
|
|
125
|
+
const [recaptchaVerified, setRecaptchaVerified] = useState(false)
|
|
120
126
|
|
|
121
127
|
const nameRef = useRef<any>(null);
|
|
122
128
|
const lastnameRef = useRef<any>(null);
|
|
@@ -125,6 +131,7 @@ const SignupFormUI = (props: SignupParams) => {
|
|
|
125
131
|
const emailRef = useRef<any>(null);
|
|
126
132
|
const phoneRef = useRef<any>(null);
|
|
127
133
|
const passwordRef = useRef<any>(null);
|
|
134
|
+
const recaptchaRef = useRef<any>({});
|
|
128
135
|
|
|
129
136
|
const showInputPhoneNumber = (validationFields?.fields?.checkout?.cellphone?.enabled ?? false) || configs?.verification_phone_required?.value === '1'
|
|
130
137
|
|
|
@@ -286,6 +293,33 @@ const SignupFormUI = (props: SignupParams) => {
|
|
|
286
293
|
}
|
|
287
294
|
}
|
|
288
295
|
|
|
296
|
+
const handleOpenRecaptcha = () => {
|
|
297
|
+
setRecaptchaVerified(false)
|
|
298
|
+
if (!recaptchaConfig?.siteKey) {
|
|
299
|
+
showToast(ToastType.Error, t('NO_RECAPTCHA_SITE_KEY', 'The config doesn\'t have recaptcha site key'));
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
if (!recaptchaConfig?.baseUrl) {
|
|
303
|
+
showToast(ToastType.Error, t('NO_RECAPTCHA_BASE_URL', 'The config doesn\'t have recaptcha base url'));
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
recaptchaRef.current.open()
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const onRecaptchaVerify = (token: any) => {
|
|
310
|
+
setRecaptchaVerified(true)
|
|
311
|
+
handleReCaptcha(token)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
useEffect(() => {
|
|
315
|
+
if (configs && Object.keys(configs).length > 0 && enableReCaptcha) {
|
|
316
|
+
setRecaptchaConfig({
|
|
317
|
+
siteKey: configs?.security_recaptcha_site_key?.value || null,
|
|
318
|
+
baseUrl: configs?.security_recaptcha_base_url?.value || null
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
}, [configs, enableReCaptcha])
|
|
322
|
+
|
|
289
323
|
useEffect(() => {
|
|
290
324
|
if (!formState.loading && formState.result?.error) {
|
|
291
325
|
formState.result?.result &&
|
|
@@ -467,6 +501,39 @@ const SignupFormUI = (props: SignupParams) => {
|
|
|
467
501
|
</View>
|
|
468
502
|
)}
|
|
469
503
|
|
|
504
|
+
{enableReCaptcha && (
|
|
505
|
+
<>
|
|
506
|
+
<TouchableOpacity
|
|
507
|
+
onPress={handleOpenRecaptcha}
|
|
508
|
+
style={{ marginHorizontal: 4, marginBottom: 10 }}
|
|
509
|
+
>
|
|
510
|
+
<RecaptchaButton>
|
|
511
|
+
{recaptchaVerified ? (
|
|
512
|
+
<MaterialCommunityIcons
|
|
513
|
+
name="checkbox-marked"
|
|
514
|
+
size={23}
|
|
515
|
+
color={theme.colors.primary}
|
|
516
|
+
/>
|
|
517
|
+
) : (
|
|
518
|
+
<MaterialCommunityIcons
|
|
519
|
+
name="checkbox-blank-outline"
|
|
520
|
+
size={23}
|
|
521
|
+
color={theme.colors.disabled}
|
|
522
|
+
/>
|
|
523
|
+
)}
|
|
524
|
+
<OText size={14} mLeft={8}>{t('VERIFY_ReCAPTCHA', 'Verify reCAPTCHA')}</OText>
|
|
525
|
+
</RecaptchaButton>
|
|
526
|
+
</TouchableOpacity>
|
|
527
|
+
<Recaptcha
|
|
528
|
+
ref={recaptchaRef}
|
|
529
|
+
siteKey={recaptchaConfig?.siteKey}
|
|
530
|
+
baseUrl={recaptchaConfig?.baseUrl}
|
|
531
|
+
onVerify={onRecaptchaVerify}
|
|
532
|
+
onExpire={() => setRecaptchaVerified(false)}
|
|
533
|
+
/>
|
|
534
|
+
</>
|
|
535
|
+
)}
|
|
536
|
+
|
|
470
537
|
<View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 20 }}>
|
|
471
538
|
<Controller
|
|
472
539
|
control={control}
|
|
@@ -736,6 +803,7 @@ const SignupFormUI = (props: SignupParams) => {
|
|
|
736
803
|
export const SignupForm = (props: any) => {
|
|
737
804
|
const signupProps = {
|
|
738
805
|
...props,
|
|
806
|
+
isRecaptchaEnable: true,
|
|
739
807
|
UIComponent: SignupFormUI,
|
|
740
808
|
};
|
|
741
809
|
return <SignUpController {...signupProps} />;
|