@truworth/twc-auth 3.0.11 → 3.0.13

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/README.md CHANGED
@@ -37,11 +37,10 @@ Truworth Auth Module - A comprehensive authentication solution for React Native
37
37
  react-native-gesture-handler \
38
38
  react-native-linear-gradient \
39
39
  react-native-modalize \
40
- react-native-reanimated \
41
40
  react-native-screens \
42
41
  react-native-safe-area-context \
43
42
  react-native-svg \
44
- react-native-vector-icons \
43
+ @react-native-vector-icons/ionicons \
45
44
  react@^18.2.0 \
46
45
  react-native@^0.74.5
47
46
  ```
@@ -52,9 +51,7 @@ Truworth Auth Module - A comprehensive authentication solution for React Native
52
51
  npx pod-install
53
52
  ```
54
53
 
55
- 4. For React Native Reanimated, follow the [installation steps](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/installation/) if you haven't already.
56
-
57
- 5. For React Navigation, wrap your app with `NavigationContainer` and ensure you've followed the [getting started guide](https://reactnavigation.org/docs/getting-started/).
54
+ 4. For React Navigation, wrap your app with `NavigationContainer` and ensure you've followed the [getting started guide](https://reactnavigation.org/docs/getting-started/).
58
55
 
59
56
  #### React Native Dependencies
60
57
 
@@ -73,11 +70,10 @@ Truworth Auth Module - A comprehensive authentication solution for React Native
73
70
  | react-native-gesture-handler | >=2.16.0 |
74
71
  | react-native-linear-gradient | >=2.8.3 |
75
72
  | react-native-modalize | >=2.1.1 |
76
- | react-native-reanimated | >=3.13.0 |
77
73
  | react-native-safe-area-context | >=5.3.0 |
78
74
  | react-native-screens | >=3.31.1 |
79
75
  | react-native-svg | >=15.8.0 |
80
- | react-native-vector-icons | >=9.2.0 |
76
+ | @react-native-vector-icons/ionicons | >=13.0.0 |
81
77
 
82
78
  > Note: lottie-react-native, react-native-fast-image are optional and only needed if your app uses features that depend on them.
83
79
 
@@ -106,15 +102,15 @@ Truworth Auth Module - A comprehensive authentication solution for React Native
106
102
  2. Install peer dependencies:
107
103
  ```bash
108
104
  npm install \
109
- next@^15.0.4 \
110
- @react-google-maps/api@>=2.20.6 \
111
- @truworth/twc-web-design@>=1.9.0 \
112
- antd@>=5.6.3 \
113
- framer-motion@>=12.6.2 \
114
- lodash@>=4.17.21 \
115
- lucide-react@>=0.483.0 \
116
- react@>=18.2.0 \
117
- react-dom@>=18.2.0
105
+ 'next@^15.0.4' \
106
+ '@react-google-maps/api@>=2.20.6' \
107
+ '@truworth/twc-web-design@>=1.9.0' \
108
+ 'antd@>=5.6.3' \
109
+ 'framer-motion@>=12.6.2' \
110
+ 'lodash@>=4.17.21' \
111
+ 'lucide-react@>=0.483.0' \
112
+ 'react@>=18.2.0' \
113
+ 'react-dom@>=18.2.0'
118
114
  ```
119
115
 
120
116
  ## Running the Example Applications of the TWC-Auth Package
@@ -163,9 +159,7 @@ This repository includes example applications for both mobile (React Native) and
163
159
  Copy the example environment variables below into a new file named `.env` in the `apps/web` directory.
164
160
 
165
161
  ```shell
166
- NEXT_PUBLIC_ENVIRONMENT=development/production
167
-
168
- NODE_ENV=development/production
162
+ NEXT_PUBLIC_ENVIRONMENT=<ENVIRONMENT> # ENVIRONMENT can be "development" or "production"
169
163
 
170
164
  NEXT_PUBLIC_MEMBER_IMAGES_URL=<value>
171
165
 
@@ -2,11 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { View, Text } from 'react-native';
3
3
  import { Modal } from '@ui-kitten/components';
4
4
  import { Colors, RoundedButton } from '@truworth/twc-rn-common';
5
- import IonIcon from 'react-native-vector-icons/Ionicons';
5
+ import { Ionicons } from '@react-native-vector-icons/ionicons';
6
6
  const { primary, gray } = Colors;
7
7
  const ConfirmationModal = ({ title, visible, onClose, onProceed, onDismiss, primaryLabel, secondaryLabel, iconName = "information-circle-outline", iconColor = primary.primary_main, description }) => {
8
8
  return (_jsx(Modal, { visible: visible, onBackdropPress: onClose, backdropStyle: { backgroundColor: 'rgba(0, 0, 0, 0.25)' }, children: _jsxs(View, { style: { flex: 1, backgroundColor: 'white', padding: 32, marginHorizontal: 16, borderRadius: 12, marginTop: -50 }, children: [iconName &&
9
- _jsx(IonIcon, { name: iconName, size: 64, color: iconColor, style: { alignSelf: 'center', marginBottom: 12 } }), title &&
9
+ _jsx(Ionicons, { name: iconName, size: 64, color: iconColor, style: { alignSelf: 'center', marginBottom: 12 } }), title &&
10
10
  _jsx(Text, { style: { fontSize: 16, textAlign: 'center', fontWeight: '600', color: gray.gray_900, marginBottom: 24, lineHeight: 20 }, children: title }), description &&
11
11
  _jsx(Text, { style: { fontSize: 12, textAlign: 'center', fontWeight: '600', color: gray.gray_600, marginTop: -16, marginBottom: 24, lineHeight: 16 }, children: description }), primaryLabel &&
12
12
  _jsx(RoundedButton, { size: 'large', label: primaryLabel, onPress: () => { onClose(); onProceed?.(); } }), secondaryLabel &&
@@ -1 +1,2 @@
1
- export {};
1
+ import React from 'react';
2
+ import { Ionicons } from '@react-native-vector-icons/ionicons';
@@ -16,9 +16,12 @@ const VerifyMobileOTP = ({ validateOTP, timer, status, resendOTP, phone }) => {
16
16
  }
17
17
  }, [otp]);
18
18
  useEffect(() => {
19
- if (otpValue && otpValue.length == 6) {
19
+ if (otpValue && otpValue.length === 6) {
20
20
  setOtp(otpValue);
21
21
  }
22
+ else if (!otpValue) {
23
+ setOtp('');
24
+ }
22
25
  }, [otpValue]);
23
26
  return (_jsxs(Layout, { style: { flex: 1, backgroundColor: '#FFFFFF' }, children: [_jsx(Header, {}), _jsxs(CustomKeyboardAvoidingView, { behavior: Platform.OS === 'ios' ? 'padding' : undefined, keyboardVerticalOffset: 0, children: [_jsxs(ScrollView, { style: { paddingHorizontal: 20, paddingTop: 32, flex: 1 }, children: [_jsxs(Text, { textBreakStrategy: 'simple', style: {
24
27
  fontSize: 20, fontWeight: '600', color: gray.gray_900,
@@ -125,6 +125,7 @@ const AuthProvider = ({ children, router, basePath = '', LogoComponent, session,
125
125
  setClient(null);
126
126
  setRegistrationMethod(null);
127
127
  setProfile(null);
128
+ setOtpValue('');
128
129
  }, []);
129
130
  /**
130
131
  * Helper to normalize and format user profile data
@@ -152,6 +153,11 @@ const AuthProvider = ({ children, router, basePath = '', LogoComponent, session,
152
153
  const handleOtpReceived = (otp) => {
153
154
  setOtpValue(otp);
154
155
  };
156
+ /** Clears SMS auto-fill buffer so it does not repopulate OTP after navigating to the main app. */
157
+ const handleLoginSuccess = useCallback((result) => {
158
+ setOtpValue('');
159
+ onLogin(result);
160
+ }, [onLogin]);
155
161
  const AuthContextValues = {
156
162
  token,
157
163
  profile,
@@ -177,7 +183,7 @@ const AuthProvider = ({ children, router, basePath = '', LogoComponent, session,
177
183
  registrationMethod,
178
184
  socialLoginConfig,
179
185
  onLaunchAuthSession,
180
- onLogin,
186
+ onLogin: handleLoginSuccess,
181
187
  logout,
182
188
  refreshSession: onRefreshSession,
183
189
  onTokenChange: (token) => setToken(token),
@@ -1 +1,2 @@
1
- export {};
1
+ import { Ionicons } from "@react-native-vector-icons/ionicons";
2
+ import React from "react";
@@ -19,7 +19,7 @@ const useTimer = (duration) => {
19
19
  return timer;
20
20
  };
21
21
  const useInterval = (callback, delay) => {
22
- const savedCallback = useRef();
22
+ const savedCallback = useRef(undefined);
23
23
  // Remember the latest function.
24
24
  useEffect(() => {
25
25
  savedCallback.current = callback;
@@ -5,7 +5,6 @@ import { Colors, TextInputField } from '@truworth/twc-rn-common';
5
5
  import { useEnterEmail } from './hooks/internal/useEnterEmail';
6
6
  import { ScreenLayout } from '../../components/ScreenLayout/index.native';
7
7
  import { ConfirmationModal as LoginConflictModal } from '../../components/ConfirmationModal';
8
- import { RegistrationMethod } from '../../enums';
9
8
  const { utility } = Colors;
10
9
  const EnterEmail = ({ navigation }) => {
11
10
  const { email, isEmailValid, handleEmailChange, handleClearEmail, handleEmailExists, loading, loginConflictTitle, loginConflictDescription, isLoginConflictModalVisible, setIsLoginConflictModalVisible } = useEnterEmail();
@@ -25,7 +24,7 @@ const EnterEmail = ({ navigation }) => {
25
24
  handleEmailExists({ onValidate });
26
25
  },
27
26
  disabled: !isEmailValid,
28
- }, children: _jsx(TextInputField, { value: email, placeholder: 'Enter here...', onChangeValue: handleEmailChange, rightIcon2: email.length > 0 ? 'close' : '', onPressRightIcon2: handleClearEmail, autoFocus: true, autoCapitalize: 'none', keyboardType: 'email-address' }) }), isLoginConflictModalVisible &&
27
+ }, children: _jsx(TextInputField, { value: email, placeholder: 'Enter here...', onChangeValue: handleEmailChange, rightIcon2: email.length > 0 ? 'close' : undefined, onPressRightIcon2: handleClearEmail, autoFocus: true, autoCapitalize: 'none', keyboardType: 'email-address' }) }), isLoginConflictModalVisible &&
29
28
  _jsx(LoginConflictModal, { title: loginConflictTitle, description: loginConflictDescription, visible: isLoginConflictModalVisible, onProceed: () => setIsLoginConflictModalVisible(false), onClose: () => setIsLoginConflictModalVisible(false), iconColor: utility.warning_main, primaryLabel: 'Okay' })] }));
30
29
  };
31
30
  export default EnterEmail;
@@ -9,7 +9,7 @@ import { useExistingAccountsSheet } from './hooks/internal/useExistingAccountsSh
9
9
  import { useAuthPackageContext } from '../../../../hooks/internal/useAuthPackageContext';
10
10
  import { LoginMethodCode } from '../../../../enums';
11
11
  import moment from 'moment';
12
- import IonIcons from 'react-native-vector-icons/Ionicons';
12
+ import { Ionicons } from '@react-native-vector-icons/ionicons';
13
13
  const { primary, gray, utility } = Colors;
14
14
  const ExistingAccountsSheet = ({ visible, hide, phone, existingAccounts }) => {
15
15
  const { ref: sheetRef, open: openSheet, close: closeSheet } = useModalize();
@@ -64,10 +64,10 @@ const ExistingAccountsSheet = ({ visible, hide, phone, existingAccounts }) => {
64
64
  marginBottom: 8,
65
65
  flexDirection: 'row',
66
66
  alignItems: 'center',
67
- }, children: [_jsx(IonIcons, { size: 16, name: "information-circle", style: { marginRight: 8 }, color: utility.warning_main }), _jsxs(Text, { style: {
67
+ }, children: [_jsx(Ionicons, { size: 16, name: "information-circle", style: { marginRight: 8 }, color: utility.warning_main }), _jsxs(Text, { style: {
68
68
  flex: 1, fontSize: 12, fontWeight: '500',
69
69
  lineHeight: 18, color: gray.gray_500,
70
70
  }, children: ["Once you proceed, this phone number will be ", _jsx(Text, { style: { fontWeight: '600', color: gray.gray_900 }, children: "removed from other accounts." })] })] }), _jsx(RoundedButton, { label: 'Link and Continue', size: 'large', loading: loading, disabled: !selectedAccount, onPress: onContinue, containerStyle: { marginBottom: 8 } }), openChatSupport &&
71
- _jsxs(TouchableOpacity, { activeOpacity: 0.8, onPress: openChatSupport, style: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', padding: 8 }, children: [_jsx(IonIcons, { size: 12, name: 'headset', color: primary.primary_main }), _jsx(Text, { style: { fontSize: 14, fontWeight: '600', color: primary.primary_main, marginStart: 8 }, children: "Connect with Support" })] })] }) }));
71
+ _jsxs(TouchableOpacity, { activeOpacity: 0.8, onPress: openChatSupport, style: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', padding: 8 }, children: [_jsx(Ionicons, { size: 12, name: 'headset', color: primary.primary_main }), _jsx(Text, { style: { fontSize: 14, fontWeight: '600', color: primary.primary_main, marginStart: 8 }, children: "Connect with Support" })] })] }) }));
72
72
  };
73
73
  export { ExistingAccountsSheet };
@@ -4,7 +4,7 @@ import { Keyboard, Text, TextInput, TouchableOpacity, View } from 'react-native'
4
4
  import { Colors } from '@truworth/twc-rn-common';
5
5
  import { Layout } from '@ui-kitten/components';
6
6
  import { useEffect } from 'react';
7
- import IonIcons from 'react-native-vector-icons/Ionicons';
7
+ import { Ionicons } from '@react-native-vector-icons/ionicons';
8
8
  import { ScreenLayout } from '../../components/ScreenLayout';
9
9
  import { useEnterMobile } from './hooks/internal/useEnterMobile';
10
10
  import { ExistingAccountsSheet } from './components/ExistingAccountsSheet/index.native';
@@ -46,11 +46,11 @@ const EnterMobile = ({ navigation }) => {
46
46
  borderWidth: 1, borderRadius: 8,
47
47
  paddingHorizontal: 12, height: 56,
48
48
  backgroundColor: primary.white, marginTop: 4,
49
- }, children: [_jsxs(TouchableOpacity, { activeOpacity: 0.8, onPress: changeCountryCode, style: { flexDirection: 'row', alignItems: 'center' }, children: [_jsx(Text, { style: { fontSize: 14, color: primary.black, fontWeight: '500' }, children: `+${countryCode}` }), _jsx(IonIcons, { name: 'caret-down', size: 14, color: gray.gray_900 })] }), _jsx(TextInput, { style: {
49
+ }, children: [_jsxs(TouchableOpacity, { activeOpacity: 0.8, onPress: changeCountryCode, style: { flexDirection: 'row', alignItems: 'center' }, children: [_jsx(Text, { style: { fontSize: 14, color: primary.black, fontWeight: '500' }, children: `+${countryCode}` }), _jsx(Ionicons, { name: 'caret-down', size: 14, color: gray.gray_900 })] }), _jsx(TextInput, { style: {
50
50
  flex: 1, fontSize: 14, fontWeight: '500',
51
51
  color: primary.black, marginLeft: 2,
52
52
  }, onChangeText: text => handleEnterMobile(text), value: phone, keyboardType: "numeric", maxLength: countryCode === '91' ? 10 : 14, autoFocus: true }), phone.length > 0 &&
53
- _jsx(TouchableOpacity, { activeOpacity: 0.8, onPress: handleClearPhoneInput, children: _jsx(IonIcons, { name: 'close', size: 24, color: gray.gray_900 }) })] }) }), showExistingAccountsSheet &&
53
+ _jsx(TouchableOpacity, { activeOpacity: 0.8, onPress: handleClearPhoneInput, children: _jsx(Ionicons, { name: 'close', size: 24, color: gray.gray_900 }) })] }) }), showExistingAccountsSheet &&
54
54
  _jsx(ExistingAccountsSheet, { phone: phone, visible: showExistingAccountsSheet, existingAccounts: existingAccounts, hide: () => setShowExistingAccountsSheet(false) })] }));
55
55
  };
56
56
  export default EnterMobile;
@@ -1,7 +1,7 @@
1
- import { useState, useRef, useEffect } from "react";
1
+ import { useState, useRef, useEffect, useMemo } from "react";
2
2
  import { axiosClient } from "../../../../../api/axiosClient";
3
3
  import { showMessage } from "../../../../../helpers/show-message";
4
- const useSSOAuthenticationMethods = () => {
4
+ const useSSOAuthenticationMethods = ({ client }) => {
5
5
  const [loading, setLoading] = useState(false);
6
6
  const isMountedRef = useRef(true);
7
7
  useEffect(() => {
@@ -34,6 +34,30 @@ const useSSOAuthenticationMethods = () => {
34
34
  }
35
35
  });
36
36
  };
37
- return { loading, initiateSSOLogin };
37
+ const headingText = useMemo(() => {
38
+ if (client?.ssoEnabled && client?.mobileLoginEnabled) {
39
+ return `Choose how to sign in`;
40
+ }
41
+ if (client?.ssoEnabled) {
42
+ return `Sign in with your organization account`;
43
+ }
44
+ if (client?.mobileLoginEnabled) {
45
+ return `Sign in with your mobile number`;
46
+ }
47
+ return 'No Single Sign-On methods are available';
48
+ }, [client?.ssoEnabled, client?.mobileLoginEnabled]);
49
+ const subHeadingText = useMemo(() => {
50
+ if (!client?.ssoEnabled && !client?.mobileLoginEnabled) {
51
+ return 'Your organization has not configured any single sign-on methods yet. Please contact your administrator.';
52
+ }
53
+ return null;
54
+ }, [client?.ssoEnabled, client?.mobileLoginEnabled]);
55
+ return {
56
+ loading,
57
+ initiateSSOLogin,
58
+ headingText,
59
+ subHeadingText,
60
+ hasMultipleLoginOptions: client?.ssoEnabled && client?.mobileLoginEnabled,
61
+ };
38
62
  };
39
63
  export { useSSOAuthenticationMethods };
@@ -6,9 +6,9 @@ import { ScreenLayout } from "../../../components/ScreenLayout";
6
6
  import { CDN_IMAGES_URL } from "../../../constants/cdn-url";
7
7
  import { showMessage } from "../../../helpers/show-message";
8
8
  import React from "react";
9
- import { KeyRound, MailIcon, Smartphone } from "lucide-react";
9
+ import { KeyRound, Smartphone } from "lucide-react";
10
10
  const SSOAuthenticationMethods = ({ client, onPressBack, handleMobileLogin }) => {
11
- const { loading, initiateSSOLogin } = useSSOAuthenticationMethods();
11
+ const { loading, initiateSSOLogin, hasMultipleLoginOptions, headingText, subHeadingText } = useSSOAuthenticationMethods({ client });
12
12
  const { LogoComponent } = useAuthPackageContext();
13
13
  const renderLogo = () => {
14
14
  if (!LogoComponent) {
@@ -40,11 +40,13 @@ const SSOAuthenticationMethods = ({ client, onPressBack, handleMobileLogin }) =>
40
40
  };
41
41
  return (_jsx(ScreenLayout, { onPressBack: onPressBack, title: _jsxs(Flex, { direction: "column", children: [client?.image ?
42
42
  _jsx("img", { src: client?.image, width: 175, height: 117, alt: `${client?.name ?? 'Client'} logo` })
43
- : renderLogo(), _jsx(Typography, { type: "heading", size: "h5", className: "my-6", children: "Select how to sign-in" }), client?.ssoEnabled &&
44
- _jsxs(_Fragment, { children: [_jsx(Button, { label: "Login with SSO", isFullWidth: true, variant: "primary", leftIcon: _jsx(KeyRound, {}), onClick: () => initiateSSOLogin({
45
- clientId: client.id,
46
- onSSOLoginInitiated
47
- }), loading: loading }), _jsx(Typography, { type: "body", size: "large", className: "mt-6 text-gray-600", children: "Recommended for quick access" }), _jsxs(Flex, { align: "center", className: "my-4", children: [_jsx(Flex, { className: "flex-1 border-t border-gray-300" }), _jsx(Typography, { type: 'body', size: 'small', className: "text-gray-500 px-4", children: "OR" }), _jsx(Flex, { className: "flex-1 border-t border-gray-300" })] })] }), _jsx(Button, { label: "Login with Email & Password", isFullWidth: true, variant: "secondary", leftIcon: _jsx(MailIcon, {}), onClick: onPressBack }), client?.mobileLoginEnabled &&
48
- _jsx(Button, { label: "Login with Mobile Number", isFullWidth: true, className: "mt-6", leftIcon: _jsx(Smartphone, {}), variant: "text", onClick: handleMobileLogin })] }) }));
43
+ : renderLogo(), _jsx(Typography, { type: "heading", size: "h5", className: "mb-4 mt-6", children: headingText }), subHeadingText &&
44
+ _jsx(Typography, { type: "body", size: "large", className: "text-gray-600", children: subHeadingText }), client?.ssoEnabled &&
45
+ _jsx(Button, { label: "Login with SSO", isFullWidth: true, variant: "primary", leftIcon: _jsx(KeyRound, {}), onClick: () => initiateSSOLogin({
46
+ clientId: client.id,
47
+ onSSOLoginInitiated
48
+ }), loading: loading }), hasMultipleLoginOptions &&
49
+ _jsxs(_Fragment, { children: [_jsx(Typography, { type: "body", size: "large", className: "mt-6 text-gray-600", children: "Recommended for quick access" }), _jsxs(Flex, { align: "center", className: "my-4", children: [_jsx(Flex, { className: "flex-1 border-t border-gray-300" }), _jsx(Typography, { type: 'body', size: 'small', className: "text-gray-500 px-4", children: "OR" }), _jsx(Flex, { className: "flex-1 border-t border-gray-300" })] })] }), client?.mobileLoginEnabled &&
50
+ _jsx(Button, { label: "Login with Mobile Number", isFullWidth: true, className: "mt-6", leftIcon: _jsx(Smartphone, {}), variant: "secondary", onClick: handleMobileLogin })] }) }));
49
51
  };
50
52
  export default SSOAuthenticationMethods;
@@ -13,9 +13,9 @@ const { primary, gray } = Colors;
13
13
  const SSOAuthenticationMethods = ({ route }) => {
14
14
  const { client } = route.params;
15
15
  const [aspectRatio, setAspectRatio] = useState(1);
16
- const { loading, initiateSSOLogin } = useSSOAuthenticationMethods();
17
- const { LogoComponent } = useAuthPackageContext();
16
+ const { loading, initiateSSOLogin, hasMultipleLoginOptions, headingText, subHeadingText } = useSSOAuthenticationMethods({ client });
18
17
  const navigation = useNavigation();
18
+ const { LogoComponent } = useAuthPackageContext();
19
19
  useEffect(() => {
20
20
  if (client.image) {
21
21
  Image.getSize(client.image, (width, height) => {
@@ -52,8 +52,23 @@ const SSOAuthenticationMethods = ({ route }) => {
52
52
  };
53
53
  return (_jsx(ScreenLayout, { title: "", containerStyle: { flex: 1, backgroundColor: primary.white }, children: _jsxs(View, { style: { flex: 1, justifyContent: 'center' }, children: [client.image ?
54
54
  _jsx(FastImage, { source: { uri: client.image }, resizeMode: 'contain', style: { width: 180, aspectRatio: aspectRatio, alignSelf: 'center', marginTop: -150 } })
55
- : renderLogo(), _jsx(Text, { style: { textAlign: 'center', fontSize: 24, fontWeight: '600', color: gray.gray_700, marginTop: 30 }, children: "Select how to sign-in" }), client?.ssoEnabled &&
56
- _jsxs(_Fragment, { children: [_jsx(RoundedButton, { loading: loading, type: 'primary', label: 'Login with SSO', size: 'large', containerStyle: { marginTop: 40 }, onPress: () => initiateSSOLogin({ clientId: client.id, onSSOLoginInitiated }), leftIcon: 'key-outline' }), _jsx(Text, { style: { textAlign: 'center', fontSize: 14, color: gray.gray_700, marginTop: 20 }, children: "Recommended for quick access" }), _jsxs(View, { style: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginTop: 30, marginHorizontal: 30 }, children: [_jsx(View, { style: { flex: 1, height: 1, backgroundColor: gray.gray_200 } }), _jsx(View, { children: _jsx(Text, { style: { width: 50, textAlign: 'center', color: gray.gray_500, fontSize: 16 }, children: "OR" }) }), _jsx(View, { style: { flex: 1, height: 1, backgroundColor: gray.gray_200 } })] })] }), _jsx(RoundedButton, { type: 'secondary', label: 'Login with Email & Password', size: 'large', containerStyle: { marginTop: 30 }, onPress: () => navigation.popTo('EnterEmail'), leftIcon: 'mail-outline' }), client?.mobileLoginEnabled &&
57
- _jsx(RoundedButton, { type: 'text', label: 'Login with Mobile Number', size: 'large', containerStyle: { marginHorizontal: 16, marginTop: 30 }, onPress: () => navigation.navigate('EnterMobile'), leftIcon: 'phone-portrait-outline' })] }) }));
55
+ : renderLogo(), _jsx(Text, { style: { textAlign: 'center', fontSize: 18, fontWeight: '600', color: gray.gray_700, marginTop: 30, lineHeight: 32 }, children: headingText }), subHeadingText &&
56
+ _jsx(Text, { style: {
57
+ textAlign: 'center',
58
+ fontSize: 16,
59
+ lineHeight: 22,
60
+ color: gray.gray_600,
61
+ marginTop: 12,
62
+ marginHorizontal: 32,
63
+ }, children: subHeadingText }), client?.ssoEnabled &&
64
+ _jsx(RoundedButton, { loading: loading, type: 'primary', label: 'Login with SSO', size: 'large', containerStyle: { marginTop: 18 }, onPress: () => initiateSSOLogin({
65
+ clientId: client.id,
66
+ onSSOLoginInitiated,
67
+ }), leftIcon: 'key-outline' }), hasMultipleLoginOptions &&
68
+ _jsxs(_Fragment, { children: [_jsx(Text, { style: { textAlign: 'center', fontSize: 14, color: gray.gray_700, marginTop: 20, }, children: "Recommended for quick access" }), _jsxs(View, { style: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginTop: 30, marginHorizontal: 30 }, children: [_jsx(View, { style: { flex: 1, height: 1, backgroundColor: gray.gray_200, } }), _jsx(Text, { style: { width: 50, textAlign: 'center', color: gray.gray_500, fontSize: 16, }, children: "OR" }), _jsx(View, { style: { flex: 1, height: 1, backgroundColor: gray.gray_200, } })] })] }), client?.mobileLoginEnabled &&
69
+ _jsx(RoundedButton, { type: 'secondary', label: 'Login with Mobile Number', size: 'large', containerStyle: {
70
+ marginHorizontal: 16,
71
+ marginTop: 30
72
+ }, onPress: () => navigation.navigate('EnterMobile'), leftIcon: 'phone-portrait-outline' })] }) }));
58
73
  };
59
74
  export default SSOAuthenticationMethods;
@@ -5,7 +5,7 @@ import { useNavigation } from '@react-navigation/native';
5
5
  import { useModalize } from 'react-native-modalize';
6
6
  import { Layout } from '@ui-kitten/components';
7
7
  import DateTimePicker from '@react-native-community/datetimepicker';
8
- import IonIcon from 'react-native-vector-icons/Ionicons';
8
+ import { Ionicons } from '@react-native-vector-icons/ionicons';
9
9
  import moment from 'moment';
10
10
  import { BottomSheet, Chips, Colors, TextInputField, DateInputField, widthPercentageToDP as wp, isIphoneX, RoundedButton, CustomKeyboardAvoidingView } from '@truworth/twc-rn-common';
11
11
  import { ScreenLayout } from '../../components/ScreenLayout';
@@ -112,11 +112,11 @@ const MobileInputField = ({ phone, countryCode, onChangeCountryCode, onChangeMob
112
112
  borderColor: phone.length > 0
113
113
  ? error?.show ? utility.danger_main : gray.gray_500
114
114
  : gray.gray_300,
115
- }, children: [_jsxs(TouchableOpacity, { activeOpacity: 0.8, onPress: onChangeCountryCode, style: { flexDirection: 'row', alignItems: 'center' }, children: [_jsx(Text, { style: { fontSize: 14, color: primary.black, fontWeight: '500' }, children: `+${countryCode}` }), _jsx(IonIcon, { size: 14, name: 'caret-down', color: gray.gray_900 })] }), _jsx(TextInput, { style: {
115
+ }, children: [_jsxs(TouchableOpacity, { activeOpacity: 0.8, onPress: onChangeCountryCode, style: { flexDirection: 'row', alignItems: 'center' }, children: [_jsx(Text, { style: { fontSize: 14, color: primary.black, fontWeight: '500' }, children: `+${countryCode}` }), _jsx(Ionicons, { size: 14, name: 'caret-down', color: gray.gray_900 })] }), _jsx(TextInput, { style: {
116
116
  flex: 1, fontSize: 14, fontWeight: '500',
117
117
  color: primary.black, marginLeft: 2,
118
118
  }, onChangeText: text => onChangeMobile(text), value: phone, keyboardType: "numeric", maxLength: countryCode === '91' ? 10 : 14 }), phone.length > 0 &&
119
- _jsx(TouchableOpacity, { activeOpacity: 0.8, onPress: onCross, style: { position: 'absolute', right: 10 }, children: _jsx(IonIcon, { size: 24, name: 'close', color: gray.gray_900 }) })] }), _jsx(Text, { style: { fontSize: 12, fontWeight: '500', color: utility.danger_main, marginStart: 4, marginTop: 4 }, children: error?.show ? error?.message : '' })] }));
119
+ _jsx(TouchableOpacity, { activeOpacity: 0.8, onPress: onCross, style: { position: 'absolute', right: 10 }, children: _jsx(Ionicons, { size: 24, name: 'close', color: gray.gray_900 }) })] }), _jsx(Text, { style: { fontSize: 12, fontWeight: '500', color: utility.danger_main, marginStart: 4, marginTop: 4 }, children: error?.show ? error?.message : '' })] }));
120
120
  };
121
121
  const KnowMoreModal = ({ visible, hide }) => {
122
122
  const { ref: knowMoreRef, open: openKnowMoreSheet, close: closeKnowMoreSheet } = useModalize();
@@ -178,7 +178,7 @@ const LinkedAccountsSheet = ({ visible, hide, linkedAccounts, countryCode, phone
178
178
  justifyContent: 'center',
179
179
  padding: 8,
180
180
  marginTop: 8
181
- }, children: [_jsx(IonIcon, { name: 'headset', size: 12, color: primary.primary_main }), _jsx(Text, { style: {
181
+ }, children: [_jsx(Ionicons, { name: 'headset', size: 12, color: primary.primary_main }), _jsx(Text, { style: {
182
182
  fontSize: 14,
183
183
  fontWeight: '600',
184
184
  color: primary.primary_main,
@@ -9,7 +9,7 @@ import { axiosClient } from "../../../../api/axiosClient";
9
9
  const useVerifyResetPasswordOTP = ({ email }) => {
10
10
  const [resendOTPCounter, setResendOTPCounter] = useState(0);
11
11
  const [status, setStatus] = useState("idle");
12
- const resetStatusTimeoutRef = useRef();
12
+ const resetStatusTimeoutRef = useRef(undefined);
13
13
  const sessionTokenRef = useRef('');
14
14
  const scheduleStatusReset = (delay) => {
15
15
  if (resetStatusTimeoutRef.current) {
@@ -1,11 +1,13 @@
1
+ import React from 'react';
2
+ import { Ionicons } from '@react-native-vector-icons/ionicons';
1
3
  type ConfirmationModalProps = {
2
4
  title: string;
3
5
  visible: boolean;
4
6
  primaryLabel: string;
5
7
  secondaryLabel?: string;
6
- iconName?: string;
8
+ iconName?: React.ComponentProps<typeof Ionicons>['name'];
7
9
  /** @deprecated Use iconClassName instead for Tailwind classes */
8
- iconColor?: string;
10
+ iconColor?: React.ComponentProps<typeof Ionicons>['color'];
9
11
  /** Tailwind classes for icon styling, e.g. "text-primary" or "text-utility-warning-main" */
10
12
  iconClassName?: string;
11
13
  onClose: () => void;
@@ -1,6 +1,8 @@
1
+ import { Ionicons } from "@react-native-vector-icons/ionicons";
2
+ import React from "react";
1
3
  interface MessageHandlerProps {
2
4
  message: string | Error | object;
3
5
  type?: 'error' | 'info' | 'message' | 'success' | 'warning';
4
- leftIcon?: string;
6
+ leftIcon?: React.ComponentProps<typeof Ionicons>['name'];
5
7
  }
6
8
  export type { MessageHandlerProps };
@@ -1,9 +1,15 @@
1
1
  import type { SSOInitiationData } from "../../types";
2
- declare const useSSOAuthenticationMethods: () => {
2
+ import type { Client } from "../../../../../types/types";
3
+ declare const useSSOAuthenticationMethods: ({ client }: {
4
+ client: Client;
5
+ }) => {
3
6
  loading: boolean;
4
7
  initiateSSOLogin: ({ clientId, onSSOLoginInitiated }: {
5
8
  clientId: number;
6
9
  onSSOLoginInitiated: (data: SSOInitiationData) => void;
7
10
  }) => void;
11
+ headingText: string;
12
+ subHeadingText: string | null;
13
+ hasMultipleLoginOptions: boolean;
8
14
  };
9
15
  export { useSSOAuthenticationMethods };
@@ -1,3 +1,4 @@
1
+ import type { ReactNode } from "react";
1
2
  import type { Client } from "../../../types/types";
2
3
  interface SSOSearchOrganizationProps {
3
4
  onPressBack: () => void;
@@ -8,6 +9,6 @@ type OptionWithClient = {
8
9
  value: string;
9
10
  image: string;
10
11
  client: Client;
11
- label: JSX.Element;
12
+ label: ReactNode;
12
13
  };
13
14
  export type { Client, SSOSearchOrganizationProps, OptionWithClient };
@@ -11,7 +11,7 @@ declare const useWelcome: () => {
11
11
  setShowSocialLoginModal: import("react").Dispatch<import("react").SetStateAction<boolean>>;
12
12
  socialLoginType: SocialLoginsEnum | undefined;
13
13
  setSocialLoginType: import("react").Dispatch<import("react").SetStateAction<SocialLoginsEnum | undefined>>;
14
- LogoComponent: import("react").ComponentType<{}> | import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
14
+ LogoComponent: import("react").ComponentType<{}> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | undefined;
15
15
  onLaunchAuthSession: (() => void) | undefined;
16
16
  socialLoginConfig: Partial<Record<SocialLoginsEnum, {
17
17
  enabled?: boolean;
@@ -1,5 +1,5 @@
1
1
  const path = require("path");
2
- const escape = require("escape-string-regexp")
2
+ const escapeStringRegexp = require("escape-string-regexp")
3
3
  const exclusionList = require("metro-config/src/defaults/exclusionList")
4
4
 
5
5
  const getMetroConfig = (dirPath, packagePath) => {
@@ -18,7 +18,7 @@ const getMetroConfig = (dirPath, packagePath) => {
18
18
  blacklistRE: exclusionList(
19
19
  packagePeerDeps.map(
20
20
  (peerDep) =>
21
- new RegExp(`^${escape(path.join(packageDir, 'node_modules', peerDep))}\\/.*$`)
21
+ new RegExp(`^${escapeStringRegexp(path.join(packageDir, 'node_modules', peerDep))}\\/.*$`)
22
22
  )
23
23
  ),
24
24
 
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "description": "Truworth Auth Package for React Native and Web",
7
- "version": "3.0.11",
7
+ "version": "3.0.13",
8
8
  "main": "build/src/index.js",
9
9
  "types": "build/types/index.d.ts",
10
10
  "files": [
@@ -27,11 +27,12 @@
27
27
  "@react-native-google-signin/google-signin": "12.2.1",
28
28
  "@react-navigation/native": "7.1.17",
29
29
  "@react-navigation/native-stack": "7.3.26",
30
- "@truworth/twc-rn-common": "2.0.2",
30
+ "@truworth/twc-rn-common": "2.0.4",
31
31
  "@truworth/twc-web-design": "2.1.0",
32
32
  "@twotalltotems/react-native-otp-input": "1.3.11",
33
+ "@react-native-vector-icons/ionicons": "13.0.0",
33
34
  "@types/lodash": "^4.17.15",
34
- "@types/react": "^18.2.0",
35
+ "@types/react": "19.2.14",
35
36
  "@types/react-google-recaptcha": "^2.1.9",
36
37
  "@types/react-native": "^0.72.0",
37
38
  "@types/url-parse": "^1.4.11",
@@ -54,9 +55,9 @@
54
55
  "react-native-fast-image": "8.6.3",
55
56
  "react-native-fbsdk-next": "13.4.1",
56
57
  "react-native-linear-gradient": "2.8.3",
58
+ "react-native-gesture-handler": "2.28.0",
57
59
  "react-native-modalize": "2.1.1",
58
60
  "react-native-parsed-text": "^0.0.22",
59
- "react-native-reanimated": "4.1.0",
60
61
  "react-native-rsa-native": "2.0.5",
61
62
  "react-native-safe-area-context": "5.6.1",
62
63
  "react-native-svg": "15.12.0",
@@ -70,8 +71,8 @@
70
71
  "@react-google-maps/api": ">=2.20.6",
71
72
  "date-fns": ">=3.0.0",
72
73
  "@react-native-community/datetimepicker": ">=8.1.1",
73
- "@react-navigation/native": ">=6.1.17",
74
- "@react-navigation/native-stack": ">=6.10.0",
74
+ "@react-navigation/native": ">=7.0.0",
75
+ "@react-navigation/native-stack": ">=7.0.0",
75
76
  "@truworth/twc-rn-common": ">=1.0.12",
76
77
  "@truworth/twc-web-design": ">=1.9.0",
77
78
  "@ui-kitten/components": ">=5.1.2",
@@ -82,16 +83,11 @@
82
83
  "lucide-react": ">=0.483.0",
83
84
  "react": ">=18.2.0",
84
85
  "react-dom": ">=18.2.0",
85
- "react-native": ">=0.74.5",
86
+ "react-native": ">=0.78.0",
86
87
  "react-native-fast-image": ">=8.6.3",
87
88
  "react-native-linear-gradient": ">=2.8.3",
88
- "react-native-modalize": ">=2.1.1",
89
- "react-native-reanimated": ">=3.13.0",
90
89
  "react-native-safe-area-context": ">=5.3.0",
91
90
  "react-native-svg": ">=15.8.0",
92
91
  "react-native-vector-icons": ">=9.2.0"
93
- },
94
- "overrides": {
95
- "prismjs": "1.30.0"
96
92
  }
97
93
  }