Commit 4aafcc3c by Nguyễn Thị Thúy

login by biometry

parent e1170e14
......@@ -49,6 +49,7 @@
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* InvestTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InvestTests.m; sourceTree = "<group>"; };
12715EC58B6699B513B54F09 /* Pods-Invest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Invest.debug.xcconfig"; path = "Target Support Files/Pods-Invest/Pods-Invest.debug.xcconfig"; sourceTree = "<group>"; };
133EF4C3267736DE00366B03 /* InvestDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = InvestDebug.entitlements; path = Invest/InvestDebug.entitlements; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* Invest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Invest.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Invest/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Invest/AppDelegate.m; sourceTree = "<group>"; };
......@@ -148,6 +149,7 @@
13B07FAE1A68108700A75B9A /* Invest */ = {
isa = PBXGroup;
children = (
133EF4C3267736DE00366B03 /* InvestDebug.entitlements */,
52FB2B08262400D400DD7983 /* BootSplash.storyboard */,
9345F6C125FF213F006B5233 /* Fonts */,
52E1A15225F1255E00EA970D /* Invest.entitlements */,
......@@ -900,7 +902,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Invest/Invest.entitlements;
CODE_SIGN_ENTITLEMENTS = Invest/InvestDebug.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8;
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)dcv.investcustomer.vn</string>
</array>
</dict>
</plist>
......@@ -372,7 +372,7 @@ PODS:
- React-cxxreact (= 0.62.2)
- React-jsi (= 0.62.2)
- ReactCommon/callinvoker (= 0.62.2)
- RNBootSplash (3.2.0):
- RNBootSplash (3.2.3):
- React-Core
- RNCAsyncStorage (1.12.1):
- React-Core
......@@ -408,6 +408,8 @@ PODS:
- React-Core
- React-RCTImage
- TOCropViewController
- RNKeychain (7.0.0):
- React-Core
- RNReanimated (1.13.2):
- React-Core
- RNScreens (2.18.0):
......@@ -421,6 +423,8 @@ PODS:
- libwebp (~> 1.0)
- SDWebImage/Core (~> 5.7)
- TOCropViewController (2.6.0)
- TouchID (4.4.1):
- React
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
......@@ -490,9 +494,11 @@ DEPENDENCIES:
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNI18n (from `../node_modules/react-native-i18n`)
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
- RNKeychain (from `../node_modules/react-native-keychain`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- TouchID (from `../node_modules/react-native-touch-id`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
......@@ -611,12 +617,16 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-i18n"
RNImageCropPicker:
:path: "../node_modules/react-native-image-crop-picker"
RNKeychain:
:path: "../node_modules/react-native-keychain"
RNReanimated:
:path: "../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
TouchID:
:path: "../node_modules/react-native-touch-id"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
......@@ -675,7 +685,7 @@ SPEC CHECKSUMS:
React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d
React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256
ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
RNBootSplash: 24175aa28fe203b10c48dc34e78d946fd33c77af
RNBootSplash: 8ef5ffa03dadd35f66510b42960ce40f397c98bf
RNCAsyncStorage: b03032fdbdb725bea0bd9e5ec5a7272865ae7398
RNCCheckbox: d1749e6a92178ce5dbc31e63becd1f34f0c76bbd
RNCClipboard: 245417a78ab585e0d4d83926c28907e7b2bc24bd
......@@ -687,16 +697,18 @@ SPEC CHECKSUMS:
RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
RNI18n: e2f7e76389fcc6e84f2c8733ea89b92502351fd8
RNImageCropPicker: 35a3ceb837446fa11547704709bb22b5fac6d584
RNKeychain: f75b8c8b2f17d3b2aa1f25b4a0ac5b83d947ff8f
RNReanimated: e03f7425cb7a38dcf1b644d680d1bfc91c3337ad
RNScreens: f0d7a2a440a8ba9f4574ca1ddb3368f473891be4
RNVectorIcons: 31cebfcf94e8cf8686eb5303ae0357da64d7a5a4
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
SDWebImageWebPCoder: d0dac55073088d24b2ac1b191a71a8f8d0adac21
TOCropViewController: 3105367e808b7d3d886a74ff59bf4804e7d3ab38
TouchID: ba4c656d849cceabc2e4eef722dea5e55959ecf4
Yoga: 3ebccbdd559724312790e7742142d062476b698e
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
YoutubePlayer-in-WKWebView: cfbf46da51d7370662a695a8f351e5fa1d3e1008
PODFILE CHECKSUM: f6cddf7564cb78360d1490a138d2ad23d4135637
PODFILE CHECKSUM: d5f4bf13be9a761040ff8b01e612e6d56a04f1d5
COCOAPODS: 1.10.1
......@@ -8588,11 +8588,6 @@
"resolved": "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.5.6.tgz",
"integrity": "sha512-HDwEaXcQIuXXCV70O+bK1rizFong3wj+5Q/jSyifKFLg0VWF95xh8XQgfzXwtq0NggL9vNjPKXa016KuFu+VFg=="
},
"react-native-local-auth": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/react-native-local-auth/-/react-native-local-auth-1.6.0.tgz",
"integrity": "sha512-36cYGZGCG82pMiVJbQa5WMA93khP4v5JqLutFkMyB/eRpCULHmojNIBlbUPIY9SCeN4sg5VBRFTVGCtTg2r2kA=="
},
"react-native-modal": {
"version": "11.7.0",
"resolved": "https://registry.yarnpkg.com/react-native-modal/-/react-native-modal-11.7.0.tgz",
......@@ -8628,6 +8623,11 @@
"resolved": "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-2.18.0.tgz",
"integrity": "sha512-8+lCEsxzSu55GWRw6yZpyt3OszxN1OngfBsFXdqspaEfq6uIChanzlcD2PLVQl+iN82GAcrZM800Kd1pA477ZQ=="
},
"react-native-segmented-control-tab": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/react-native-segmented-control-tab/-/react-native-segmented-control-tab-3.4.1.tgz",
"integrity": "sha512-BNPdlE9Unr0Xabewn8W+FhBMLjssXy9Ey7S7AY0hXlrKrEKFdC9z0yT+eEWd5dLam4T6T4IuGL8b7ZF4uGyWNw=="
},
"react-native-simple-radio-button": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/react-native-simple-radio-button/-/react-native-simple-radio-button-2.7.4.tgz",
......
......@@ -17,6 +17,7 @@ import R from '../assets/R';
import {isTablet} from 'react-native-device-info';
import {RSA_KEY, MY_RSA_KEY} from './constants';
import JSEncrypt from 'jsencrypt';
import KEY from '../assets/AsynStorage';
export const encryptRSAString = (val) => {
var encrypt = new JSEncrypt();
......@@ -33,7 +34,7 @@ export const decryptRSAString = (val) => {
};
export const logout = (navigation) => {
AsyncStorage.clear();
AsyncStorage.multiRemove([KEY.ACCOUNT, KEY.FIREBASE, KEY.TOKEN])
navigation.reset({
index: 1,
routes: [{name: AUTHEN}],
......
import React, {useState, useEffect, useRef} from 'react';
import {Modal, View, TouchableWithoutFeedback, TouchableOpacity, StyleSheet, Text, TextInput} from 'react-native';
import IconClose from 'react-native-vector-icons/AntDesign';
import R from '../../assets/R';
import {WIDTHXD, getWidth, HEIGHTXD, getFontXD, getHeight} from '../../Config/Functions';
import {showAlert, TYPE} from '../../components/DropdownAlert';
import I18n from '../../helper/i18/i18n';
import DropdownAlert from 'react-native-dropdownalert';
const EnterPasswordModal = (props) => {
const [visible, setVisible] = useState(true);
const [pass, setPass] = useState('');
const dropDownAlertRef = useRef(null);
const onChangeText = (text) => {
setPass(text);
};
useEffect(() => {
setVisible(props.visible);
}, [props.visible]);
return <Modal
animationType='slide'
transparent={true}
visible={visible}
onRequestClose={() => {
props.setVisible(false);
setPass('');
}}
>
<TouchableWithoutFeedback
onPress={() => {
setPass('');
props.setVisible(false);
}}
>
<View
style={styles.opacity}
>
<View style={styles.modal}>
<View style={styles.viewTitle}>
<View style={styles.viewEmpty}></View>
<Text style={styles.titlePopup}>{I18n.t('EnterPassword')}</Text>
<TouchableOpacity onPress={() => {
setPass('');
props.setVisible(false);
}} style={styles.btClose}>
<IconClose name='close' size={WIDTHXD(48)} color={R.colors.black}/>
</TouchableOpacity>
</View>
<TextInput
autoCapitalize="none"
onChangeText={(val) => onChangeText(val)}
style={styles.txtInput}
placeholderTextColor={R.colors.placeHolder}
secureTextEntry={true}
autoFocus={true}
value={pass}
/>
<TouchableOpacity onPress={() => {
if (pass == '') {
dropDownAlertRef.current.alertWithType(TYPE.WARN, I18n.t('Notification'), `${I18n.t('Please_fill_in')}${I18n.t('Password')}`);
} else {
props.accept(pass);
setPass('')
}
}}>
<Text style={styles.txtAccept}>{I18n.t('Ok')}</Text>
</TouchableOpacity>
</View>
<DropdownAlert
inactiveStatusBarBackgroundColor={R.colors.main}
activeStatusBarBackgroundColor={R.colors.main}
warnImageSrc={R.images.iconWarn}
successImageSrc={R.images.iconSuccess}
errorImageSrc={R.images.iconError}
titleStyle={{color: '#fff'}}
messageStyle={{color: '#fff'}}
updateStatusBar={false}
closeInterval={1000}
ref={dropDownAlertRef}
warnColor={R.colors.orange400}
defaultContainer={{
borderBottomRightRadius: WIDTHXD(30),
borderBottomLeftRadius: WIDTHXD(30),
paddingTop: HEIGHTXD(30),
paddingVertical: HEIGHTXD(30),
paddingHorizontal: WIDTHXD(20),
}}
/>
</View>
</TouchableWithoutFeedback>
</Modal>;
};
const styles = StyleSheet.create({
opacity: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#rgba(0,0,0,0.7)',
},
titlePopup: {
fontSize: getFontXD(48),
color: R.colors.black,
textAlign: 'center',
flex: 10,
},
viewEmpty: {
flex: 1,
},
viewTitle: {
flexDirection: 'row',
width: WIDTHXD(960),
borderBottomWidth: 0.3,
paddingBottom: HEIGHTXD(50),
borderBottomColor: R.colors.iconGray,
borderColor: R.colors.iconGray,
},
txtAccept: {
fontSize: getFontXD(48),
color: R.colors.main,
textAlign: 'center',
marginTop: HEIGHTXD(50),
},
btClose: {
flex: 1,
justifyContent: 'center',
alignItems: 'flex-start',
},
modal: {
backgroundColor: R.colors.white,
width: WIDTHXD(960),
justifyContent: 'center',
// maxHeight: HEIGHTXD(1300),
borderRadius: WIDTHXD(20),
paddingBottom: WIDTHXD(40),
// minHeight: HEIGHTXD(369),
paddingVertical: HEIGHTXD(59),
paddingTop: HEIGHTXD(61),
paddingHorizontal: WIDTHXD(60),
alignItems: 'center',
},
txtInput: {
width: WIDTHXD(880),
height: HEIGHTXD(109),
marginTop: HEIGHTXD(50),
color: 'black',
borderRadius: 7,
borderWidth: 0.7,
borderColor: '#DBDBDB',
fontSize: getFontXD(42),
paddingVertical: 5,
paddingHorizontal: 10,
backgroundColor: 'white',
shadowColor: '#AFA9A9',
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.25,
shadowRadius: 1.84,
elevation: 1,
},
});
export default EnterPasswordModal;
import React, {useEffect, useState} from 'react';
import {View, Text, Switch, StyleSheet} from 'react-native';
import {View, Text, Switch, StyleSheet, Platform} from 'react-native';
import HeaderBack from '../../components/Header/HeaderBack';
import {getFontXD} from '../../Config/Functions';
import {encryptRSAString, getFontXD} from '../../Config/Functions';
import PickerItem from '../../components/Picker/PickerItem';
import AppText from '../../components/AppText';
import {changeLanguage} from '../../actions/language';
......@@ -9,86 +9,146 @@ import {connect} from 'react-redux';
import AsyncStorage from '@react-native-community/async-storage';
import KEY from '../../assets/AsynStorage';
import I18n, {setLocation} from '../../helper/i18/i18n';
import EnterPasswordModal from './EnterPasswordModal';
import * as Keychain from 'react-native-keychain';
import {showLoading, hideLoading} from '../../actions/loadingAction';
import {verifyPassword} from '../../apis/Functions/users';
import {showAlert, TYPE} from '../../components/DropdownAlert';
const dataLanguage = [
{
value: 'vi',
name: 'Vietnamese',
},
{
value: 'en',
name: 'English',
},
{
value: 'vi',
name: 'Vietnamese',
},
{
value: 'en',
name: 'English',
},
];
const SettingView = (props) => {
const [isEnabled, setIsEnabled] = useState(true);
const toggleSwitch = () => setIsEnabled((previousState) => !previousState);
const [isEnabled, setIsEnabled] = useState(false);
const [visible, setVisible] = useState(false);
const [biometryType, setBiometryType] = useState(null);
const toggleSwitch = async () => {
if (isEnabled == true) {
await Keychain.resetGenericPassword();
AsyncStorage.setItem(KEY.IS_LOGIN_BY_BIOMETRY, JSON.stringify({isLoginByBiometry : false}));
setIsEnabled(false);
} else {
setVisible(true);
}
};
const [language, setLanguage] = useState();
useEffect(() => {
convertLanguage();
getLoginByBiometry()
Keychain.getSupportedBiometryType({}).then((biometryType) => {
setBiometryType(biometryType);
});
}, []);
const getLoginByBiometry = async () => {
let loginByBiometry = await AsyncStorage.getItem(KEY.IS_LOGIN_BY_BIOMETRY);
if (loginByBiometry) {
setIsEnabled(JSON.parse(loginByBiometry).isLoginByBiometry);
}
};
const [language, setLanguage] = useState();
const savePass = async (pass) => {
setVisible(false);
props.showLoading();
console.log(props.user);
const res = await verifyPassword({
password: encryptRSAString(pass),
platform: Platform.OS,
account_type: 'CUSTOMER',
});
if (res.status == 200 && res.data) {
if (res.data.code == 200) {
await Keychain.setGenericPassword(props.user.email, pass, {
accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SET,
});
AsyncStorage.setItem(KEY.IS_LOGIN_BY_BIOMETRY, JSON.stringify({isLoginByBiometry : true}));
setIsEnabled(true);
} else {
showAlert(TYPE.ERROR, I18n.t('Notification'), res.data.message);
}
} else {
showAlert(TYPE.ERROR, I18n.t('Notification'), I18n.t('HaveIssue'));
}
props.hideLoading();
};
useEffect(() => {
convertLanguage();
}, []);
const convertLanguage = () => {
const temp = dataLanguage.filter((e) => e.value == props.language.language);
setLanguage(temp[0].name);
};
const convertLanguage = () => {
const temp = dataLanguage.filter((e) => e.value == props.language.language);
setLanguage(temp[0].name);
};
return (
<>
<View style={{flex: 1}}>
<HeaderBack title={'Setting'}/>
<View style={{flex: 1, padding: 10}}>
<View style={styles.row}>
<AppText i18nKey={'Language'} style={styles.txtTitle}/>
<PickerItem
width={200}
defaultValue={language}
value={language}
data={dataLanguage}
onValueChange={(value, items) => {
setLanguage(items.name);
props.changeLanguage(items.value);
AsyncStorage.setItem(KEY.LANGUAGE, items.value);
setLocation(I18n, items.value);
props.updateLangue(items.value);
}}
/>
</View>
<View style={styles.row}>
<Text style={styles.txtTitle}>{I18n.t('LoginBy', {type: biometryType =='FaceID' ? I18n.t('FaceId') : I18n.t('Fingerprint')})}</Text>
<Switch
trackColor={{false: '#DBDBDB', true: '#1C6AF6'}}
ios_backgroundColor="#767577"
thumbColor={'#f4f3f4'}
onValueChange={toggleSwitch}
value={isEnabled}
/>
</View>
return (
<View style={{flex: 1}}>
<HeaderBack title={'Setting'} />
<View style={{flex: 1, padding: 10}}>
{/* <View style={styles.row}>
<Text style={styles.txtTitle}>Bật thông báo</Text>
<Switch
trackColor={{false: '#DBDBDB', true: '#1C6AF6'}}
ios_backgroundColor="#767577"
thumbColor={'#f4f3f4'}
onValueChange={toggleSwitch}
value={isEnabled}
/>
</View> */}
</View>
<EnterPasswordModal
visible={visible}
accept={(pass) => savePass(pass)}
setVisible={(visible) => setVisible(visible)}
/>
<View style={styles.row}>
<AppText i18nKey={'Language'} style={styles.txtTitle} />
<PickerItem
width={200}
defaultValue={language}
value={language}
data={dataLanguage}
onValueChange={(value, items) => {
setLanguage(items.name);
props.changeLanguage(items.value);
AsyncStorage.setItem(KEY.LANGUAGE, items.value);
setLocation(I18n, items.value);
props.updateLangue(items.value);
}}
/>
</View>
</View>
</View>
);
</View>
</>
);
};
const styles = StyleSheet.create({
txtTitle: {
fontSize: getFontXD(46),
color: '#001C51',
},
txtTitle: {
fontSize: getFontXD(46),
color: '#001C51',
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20,
},
});
const mapStateToProps = (state) => {
return {
language: state.languageReducer,
};
return {
user: state.userReducer,
language: state.languageReducer,
};
};
export default connect(mapStateToProps, {changeLanguage})(SettingView);
export default connect(mapStateToProps, {changeLanguage, showLoading, hideLoading})(SettingView);
......@@ -91,3 +91,8 @@ export const updateOTPApiSmart = async (body) =>
PostData(url.urlUpdateSmartOTP, body)
.then((res) => res)
.catch((err) => err);
export const verifyPassword = async (body) =>
PostData(url.urlVerifyPassword, body)
.then((res) => res)
.catch((err) => err);
......@@ -68,4 +68,5 @@ export default {
urlGetSmartOTP: root + 'api/v2/customer-get-otp',
urlStoreOTPSmart: root + 'api/v1/customers/store-otp-password',
urlVerufySmartOTP: root + 'api/v1/customers/verify-otp-password',
urlVerifyPassword: `${root}api/auth/customer-verify-password`,
};
......@@ -3,6 +3,7 @@ const KEY = {
FIREBASE: '@Firebase',
ACCOUNT: '@ACCOUNT',
LANGUAGE: '@LANGUAGE',
IS_LOGIN_BY_BIOMETRY: '@IS_LOGIN_BY_BIOMETRY',
};
export default KEY;
......@@ -118,6 +118,8 @@ const images = {
rules: require('./images/rules.png'),
changeSmart: require('./images/changeSmart.png'),
faq: require('./images/faq.png'),
fingerprint: require('./images/fingerprint.png'),
iconFaceId: require('./images/iconFaceID.png'),
};
export default images;
......@@ -328,4 +328,10 @@ export default {
WarnMaxReqestWithdraw: 'Invalid withdrawal amount',
YouHaveNotSettingSmartOTP: 'You have not installed Smart OTP',
OTP: 'Enter OTP',
HaveIssue: 'Have an issue, try again!',
HaveNotCredential: 'Can not login by %{type}, please return on login by %{type}',
LoginBy: 'Login by %{type}',
Fingerprint: 'Fingerprint',
FaceId: 'FaceId'
};
......@@ -325,4 +325,9 @@ export default {
ForgotSmartOTP: 'Quên Smart OTP',
YouHaveNotSettingSmartOTP: 'Bạn chưa cài đặt Smart OTP',
OTP: 'Nhập OTP',
HaveIssue: 'Có lỗi xảy ra, vui lòng thử lại',
HaveNotCredential: 'Không thể đăng nhập được bằng %{type}, vui lòng bật lại chức năng đăng nhập bằng %{type}',
LoginBy: 'Đăng nhập bằng %{type}',
Fingerprint: 'vân tay',
FaceId: 'nhận diện khuôn mặt'
};
......@@ -6949,6 +6949,11 @@ react-native-screens@^2.17.1:
resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-2.18.0.tgz"
integrity sha512-8+lCEsxzSu55GWRw6yZpyt3OszxN1OngfBsFXdqspaEfq6uIChanzlcD2PLVQl+iN82GAcrZM800Kd1pA477ZQ==
react-native-segmented-control-tab@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/react-native-segmented-control-tab/-/react-native-segmented-control-tab-3.4.1.tgz#b6e54b8975ce8092315c9b0a1ab58b834d8ccf8e"
integrity sha512-BNPdlE9Unr0Xabewn8W+FhBMLjssXy9Ey7S7AY0hXlrKrEKFdC9z0yT+eEWd5dLam4T6T4IuGL8b7ZF4uGyWNw==
react-native-simple-radio-button@^2.7.4:
version "2.7.4"
resolved "https://registry.yarnpkg.com/react-native-simple-radio-button/-/react-native-simple-radio-button-2.7.4.tgz#86e2dbe8af9e6bf60eee088f60466f7a975e7758"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment