Commit a6d6ebfa authored by Farhani's avatar Farhani

Add image picker for upload photo

parent a6a500ec
......@@ -16,6 +16,7 @@
"react": "18.1.0",
"react-native": "^0.70.6",
"react-native-image-picker": "^4.10.3",
"react-native-input-spinner": "^1.7.12",
"react-native-numeric-input": "^1.9.1",
"react-native-safe-area-context": "^4.4.1",
"react-native-screens": "^3.18.2",
......
import React, {useEffect, useState} from 'react';
import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity, SafeAreaView, StyleSheet, Image, FlatList, Modal, Alert, TextInput, PermissionsAndroid, Platform} from 'react-native';
import { openDatabase } from 'react-native-sqlite-storage';
import ImagePicker from 'react-native-image-picker';
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';
import InputQuantity from 'react-native-numeric-input';
import InputSpinner from 'react-native-input-spinner';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
var db = openDatabase({ name: 'shoppinglist.db'});
......@@ -11,74 +11,23 @@ const meal = require('../../images/breakfast.png');
const plusbutton = require('../../images/plusbutton.png');
const editbutton = require('../../images/editbutton.png');
const deletebutton = require ('../../images/deletebutton.png');
// const LIST = [
// {
// id:'0',
// title:'Bread',
// quantity:1,
// image: require('../../images/breakfast.png'),
// },
// {
// id:'1',
// title:'Shirt',
// quantity:1,
// image: require('../../images/breakfast.png'),
// },
// {
// id:'2',
// title:'Shoes',
// quantity:1,
// image: require('../../images/breakfast.png'),
// },
// {
// id:'0',
// title:'Bread',
// quantity:1,
// image: require('../../images/breakfast.png'),
// },
// {
// id:'1',
// title:'Shirt',
// quantity:1,
// image: require('../../images/breakfast.png'),
// },
// {
// id:'2',
// title:'Shoes',
// quantity:1,
// image: require('../../images/breakfast.png'),
// },
// {
// id:'0',
// title:'Bread',
// quantity:1,
// image: require('../../images/breakfast.png'),
// },
// {
// id:'1',
// title:'Shirt',
// quantity:1,
// image: require('../../images/breakfast.png'),
// },
// {
// id:'2',
// title:'Shoes',
// quantity:1,
// image: require('../../images/breakfast.png'),
// },
// ]
const ListScreen = ({navigation}) => {
const [addImageVisible, setAddImageVisible] = useState(false);
const [addModalVisible, setAddModalVisible] = useState(false);
const [editModalVisible, setEditModalVisible] = useState(false);
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
const [confirmEditModalVisible, setConfirmEditModalVisible] = useState(false);
const [id, setId] = useState ('');
const [name, setName] = useState('');
const [quantity, setQuantity] = useState('');
const [quantity, setQuantity] = useState(0);
const [image, setImage] = useState('');
const [filePath, setFilePath] = useState({});
const [updateItem, setUpdateItem] = useState ([]);
const [items, setItems] = useState ([]);
const [empty, setEmpty] = useState ([]);
const [emptyList, setEmptyList] = useState ([]);
const [selectedId, setSelectedId] = useState ('');
const [imageUri, setImageUri]= useState('');
// const [imageUriGallery, setImageUriGallery]=useState('');
// create table
useEffect(() => {
......@@ -91,38 +40,41 @@ const ListScreen = ({navigation}) => {
if (res.rows.length == 0) {
txn.executeSql('DROP TABLE IF EXISTS list', []);
txn.executeSql(
'CREATE TABLE IF NOT EXISTS list(id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100), quantity INT(10))',
'CREATE TABLE IF NOT EXISTS list(id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(100), quantity INT(10), image LONGTEXT)',
[],
);
}
},
);
});
console.log('Sucess!');
}, []);
// add into list
const addToList = () => {
const insertList = () => {
console.log(name, quantity);
if (name == '') {
alert('Please enter name');
return;
} else
if (quantity == 0){
// return;
}
else if (quantity == 0){
alert('Please enter quantity');
return;
}
else{
setAddModalVisible(false)
db.transaction(function (tx) {
tx.executeSql(
'INSERT INTO list (name) VALUES (?)',
[name],
'INSERT INTO list (name, quantity, image) VALUES (?,?,?)',
[name, quantity, image],
(tx, results) => {
console.log('Results', results.rowsAffected);
if (results.rowsAffected > 0) {
Alert.alert('Data Inserted Successfully');
} else alert('Failed creating list');
Alert.alert(
'Success!',
'Data inserted successfully',
);
onRefresh()
setName(''), setQuantity(0), setImage('')
} else alert('Add list failed');
},(error)=>{
console.log(JSON.stringify(error))
}
......@@ -131,45 +83,59 @@ db.transaction(function (tx) {
}
}
// navigateToListScreen = () => {
// navigation.navigate('ListScreen');
// }
// view list
useEffect(() => {
db.transaction((tx) => {
tx.executeSql(
'SELECT * FROM list',
[],
(tx, results) => {
var temp = [];
for (let i = 0; i < results.rows.length; ++i)
temp.push(results.rows.item(i));
setItems(temp);
if (results.rows.length >=1){
setEmpty(false);
}else{
setEmpty(true)
}
console.log(JSON.stringify(temp))
}
);
});
onRefresh()
}, []);
// update list
const updateList = () => {
console.log(selectedId, name, quantity, image);
console.log(updateItem.name, updateItem.quantity, updateItem.image);
var n = ''
var q = 0
var i = ''
if (name == '' && updateItem.name == ''){
alert('Please enter name');
}
else if (quantity == 0 && updateItem.quantity == 0){
alert('Please enter quantity');
}
else if (image == '' && updateItem.image == ''){
alert('')
}
else {
if(name == ''){
n = updateItem.name
}
else{
n = name
}
if(quantity == 0){
q = updateItem.quantity
}
else{
q = quantity
}
if(image == ''){
i = updateItem.image
}
else{
i = image
}
console.log(n,q,i)
setEditModalVisible(false)
db.transaction((tx) => {
tx.executeSql(
'SELECT * FROM list where id = ?',
[id],
'UPDATE list set name = ?, quantity = ?, image = ? where id = ?',
[n, q, i, selectedId],
(tx, results) => {
console.log('Results', results.rowsAffected);
console.log('Results', JSON.stringify(results));
if (results.rowsAffected > 0) {
Alert.alert(
'Success',
'Successfully added into shopping list.',
'Success!',
'Data updated successfully.',
[
{
text: 'Ok',
......@@ -178,20 +144,28 @@ const updateList = () => {
],
{ cancelable: false }
);
} else alert('Failed creating list');
onRefresh()
setName(''), setQuantity(0), setImage('')
} (error)=>{
console.log(JSON.stringify(error))
}
}
);
});
}
});
}
};
//delete list
const deleteList = (id) => {
const deleteList = () => {
console.log('Delete item ' + selectedId)
var temp = selectedId
setDeleteModalVisible(false)
db.transaction((tx) => {
tx.executeSql(
'DELETE * FROM list where id = ?',
[name],
'DELETE FROM list where id = ?',
[temp],
(tx, results) => {
console.log('Results', results.rowsAffected);
console.log('Results', JSON.stringify(results));
if (results.rowsAffected > 0) {
Alert.alert(
'Success',
......@@ -204,12 +178,39 @@ const deleteList = (id) => {
],
{ cancelable: false }
);
} else alert('Failed creating list');
onRefresh()
setName(''), setQuantity(0)
}
},(error)=>{
console.log(JSON.stringify(error))
}
);
});
}
const onRefresh = () => {
db.transaction((tx) => {
tx.executeSql(
'SELECT * FROM list',
[],
(tx, results) => {
var temp = [];
for (let i = 0; i < results.rows.length; ++i)
temp.push(results.rows.item(i));
setItems(temp);
if (results.rows.length >=1){
setEmptyList(false);
}else{
setEmptyList(true)
}
console.log(JSON.stringify(temp))
},(error)=>{
console.log(JSON.stringify(error))
}
);
});
};
const listViewItemSeparator = () =>{
return (
<View style={{
......@@ -285,118 +286,58 @@ const requestExternalWritePermission = async () => {
} else return true;
};
const captureImage = async (type) => {
let options = {
mediaType: type,
maxWidth: 300,
maxHeight: 550,
quality: 1,
// videoQuality: 'low',
durationLimit: 30, //Video max duration in seconds
saveToPhotos: true,
const openCamera = () => {
const options={
storageOption : {
path:'images',
mediaType:'photo',
},
includeBase64: true,
};
let isCameraPermitted = await requestCameraPermission();
let isStoragePermitted = await requestExternalWritePermission();
if (isCameraPermitted && isStoragePermitted) {
launchCamera(options, (response) => {
console.log('Response = ', response);
launchCamera(options, response => {
console.log('Response = ', response);
if (response.didCancel) {
alert('User cancelled camera picker');
return;
} else if (response.errorCode == 'camera_unavailable') {
alert('Camera not available on device');
return;
} else if (response.errorCode == 'permission') {
alert('Permission not satisfied');
return;
} else if (response.errorCode == 'others') {
alert(response.errorMessage);
return;
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
alert(response.customButton);
} else {
const source = response.assets[0].uri
console.log(source)
setImageUri(source);
}
console.log('base64 -> ', response.base64);
console.log('uri -> ', response.uri);
console.log('width -> ', response.width);
console.log('height -> ', response.height);
console.log('fileSize -> ', response.fileSize);
console.log('type -> ', response.type);
console.log('fileName -> ', response.fileName);
setFilePath(response);
});
}
};
const chooseFile = (type) => {
let options = {
mediaType: type,
maxWidth: 300,
maxHeight: 550,
quality: 1,
const openGallery = () => {
const options={
storageOption : {
path:'images',
mediaType:'photo',
},
includeBase64: true,
};
launchImageLibrary(options, (response) => {
console.log('Response = ', response);
launchImageLibrary(options, response => {
console.log('Response = ', response);
if (response.didCancel) {
alert('User cancelled camera picker');
return;
} else if (response.errorCode == 'camera_unavailable') {
alert('Camera not available on device');
return;
} else if (response.errorCode == 'permission') {
alert('Permission not satisfied');
return;
} else if (response.errorCode == 'others') {
alert(response.errorMessage);
return;
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else if (response.customButton) {
console.log('User tapped custom button: ', response.customButton);
alert(response.customButton);
} else {
const source = response.assets[0].uri
console.log(source)
setImageUri(source);
}
console.log('base64 -> ', response.base64);
console.log('uri -> ', response.uri);
console.log('width -> ', response.width);
console.log('height -> ', response.height);
console.log('fileSize -> ', response.fileSize);
console.log('type -> ', response.type);
console.log('fileName -> ', response.fileName);
setFilePath(response);
});
};
// const chooseFile = () => {
// let options = {
// title: 'Select Image',
// customButtons: [
// {
// name: 'customOptionKey',
// title: 'Choose Photo from Custom Option'
// },
// ],
// storageOptions: {
// skipBackup: true,
// path: 'images',
// },
// };
// ImagePicker.showImagePicker(options, (response) => {
// console.log('Response = ', response);
// if (response.didCancel) {
// console.log('User cancelled image picker');
// } else if (response.error) {
// console.log('ImagePicker Error: ', response.error);
// } else if (response.customButton) {
// console.log(
// 'User tapped custom button: ',
// response.customButton
// );
// alert(response.customButton);
// } else {
// let source = response;
// // You can also display the image using data:
// // let source = {
// // uri: 'data:image/jpeg;base64,' + response.data
// // };
// setFilePath(source);
// }
// });
// };
const listItemView = ( item ) => (
<View
......@@ -415,7 +356,7 @@ const listItemView = ( item ) => (
style = {styles.imageContainer}>
<Image
style = {[styles.imageSize,{}]}
source={meal}
// source={imageUri}
/>
<View
style={{
......@@ -453,7 +394,7 @@ const listItemView = ( item ) => (
styles.editDeleteContainer,
{}
]}
onPress = {()=>setEditModalVisible(true)}
onPress = {()=>{setEditModalVisible(true), setUpdateItem(item), setSelectedId(item.id)}}
>
<MaterialCommunityIcons
name="square-edit-outline"
......@@ -466,7 +407,7 @@ const listItemView = ( item ) => (
styles.editDeleteContainer,
{marginHorizontal:10,}
]}
onPress = {()=>setDeleteModalVisible(true)}
onPress = {()=>{setDeleteModalVisible(true), setSelectedId(item.id)}}
>
<MaterialCommunityIcons
name="delete"
......@@ -515,22 +456,35 @@ const listItemView = ( item ) => (
style={styles.centeredView}>
<View
style={styles.addImageView}>
<TouchableOpacity
style={{
justifyContent:'flex-start'
}}
onPress={()=>setAddImageVisible(false)}
>
<MaterialCommunityIcons
name="close-circle"
size={30}
color='white'
/>
</TouchableOpacity>
<View
style={{
alignItems:'center',
marginTop:10
marginTop:1
}}>
<Text
style={{
fontSize:20,
color:'black',
fontWeight:'bold'
fontWeight:'bold',
justifyContent:'center'
}}>
Add image
</Text>
</View>
<TouchableOpacity
onPress={() => captureImage('photo')}>
onPress={() => {openCamera()}}
>
<View
style={{
marginTop:25
......@@ -545,7 +499,8 @@ const listItemView = ( item ) => (
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => chooseFile('photo')}>
onPress={() => openGallery()}
>
<View
style={{
marginTop:15
......@@ -560,6 +515,8 @@ const listItemView = ( item ) => (
</View>
</TouchableOpacity>
</View>
</View>
</View>
</View>
</View>
......@@ -570,6 +527,10 @@ const listItemView = ( item ) => (
}
const AddListModal = () => {
console.log("Results "+ imageUri)
var img = imageUri != null && imageUri != undefined && imageUri!='' ? {uri:imageUri} : ''
if(addModalVisible){
return(
<Modal
......@@ -642,7 +603,12 @@ const listItemView = ( item ) => (
marginHorizontal:19
}}>
<TextInput
style={{
fontSize:20,
marginHorizontal:5
}}
onChangeText={(name) => setName(name)}
// onSelectionChange={}
/>
</View>
</View>
......@@ -666,26 +632,27 @@ const listItemView = ( item ) => (
</View>
<View
style={{
marginHorizontal:5,
marginHorizontal:10,
marginTop:5
}}>
<InputQuantity
<InputSpinner
value={quantity}
minValue={0}
maxValue={100}
// onChangeText={(name) => setName(name)
onChangeNumber={(quantity) => setQuantity(quantity)}
min={0}
max={10}
step={1}
onChange={(quantity) => setQuantity(quantity)}
onLimitReached={(isMax, msg) => console.log(isMax,msg)}
totalHeight={50}
totalWidth={150}
iconSize={25}
step={1.5}
valueType='integer'
rounded
// showBorder skin='default'
textColor='black'
iconStyle={{color:'white'}}
rightButtonBackgroundColor='#ed81a1'
leftButtonBackgroundColor='#ed81a1'
fontSize={20}
colorMin='white'
colorMax='white'
buttonTextColor='#ed81a1'
colorPress="#ed81a1"
color='white'
width={200}
height={50}
/>
</View>
</View>
......@@ -707,6 +674,44 @@ const listItemView = ( item ) => (
Image
</Text>
</View>
{imageUri != null && imageUri != undefined && imageUri!=''
?
<View
style={{
backgroundColor:'transparent',
borderRadius:7,
borderColor:'white',
borderWidth:3,
width:90,
height:90,
marginHorizontal:17,
// alignItems:'center',
// justifyContent:'center'
}}>
<Image
style={{
height:'100%',
width:'100%',
}}
source={img}
/>
<View
style={{
position:'absolute',
// justifyContent:'flex-end',
right:0,
// marginHorizontal:60,
width:'100%',
height:'20%'
}}>
<MaterialCommunityIcons
name="window-close"
size={20}
color="white"
/>
</View>
</View>
:
<TouchableOpacity
style={{
backgroundColor:'transparent',
......@@ -719,17 +724,16 @@ const listItemView = ( item ) => (
alignItems:'center',
justifyContent:'center'
}}
onPress ={() =>setAddImageVisible(true)}>
<Image
source={meal}
style={{
width:30,
height:30}}
/>
<Image
source={{uri: filePath.uri}}
onPress ={() =>setAddImageVisible(true)}
>
<MaterialCommunityIcons
name="image-plus"
size={30}
color="#ebebeb"
/>
</TouchableOpacity>
}
</View>
<View
style={{
......@@ -770,7 +774,7 @@ const listItemView = ( item ) => (
style={{
marginHorizontal:5
}}
onPress={addToList}>
onPress={insertList}>
<View
style={{
backgroundColor:'transparent',
......@@ -842,7 +846,7 @@ const EditListModal = () => {
color:'black',
fontWeight:'bold'
}}>
Edit list
Update list
</Text>
</View>
......@@ -872,7 +876,13 @@ const EditListModal = () => {
marginHorizontal:19
}}>
<TextInput
style={{
fontSize:20,
marginHorizontal:5
}}
onChangeText={(name) => setName(name)}
placeholderTextColor='black'
placeholder={updateItem.name}
/>
</View>
</View>
......@@ -896,26 +906,28 @@ const EditListModal = () => {
</View>
<View
style={{
marginHorizontal:5,
marginHorizontal:10,
marginTop:5
}}>
<InputQuantity
value={quantity}
minValue={0}
maxValue={100}
// onChangeText={(name) => setName(name)
onChangeNumber={(quantity) => setQuantity(quantity)}
<InputSpinner
value={updateItem.quantity}
mine={0}
max={10}
step={1}
onChange={(quantity) => setQuantity(quantity)}
onLimitReached={(isMax, msg) => console.log(isMax,msg)}
totalHeight={50}
totalWidth={150}
iconSize={25}
step={1.5}
valueType='integer'
rounded
// showBorder skin='square'
textColor='black'
iconStyle={{color:'white'}}
rightButtonBackgroundColor='#ed81a1'
leftButtonBackgroundColor='#ed81a1'
fontSize={20}
colorMin='white'
colorMax='white'
buttonTextColor='#ed81a1'
colorPress="#ed81a1"
color='white'
width={200}
height={50}
/>
</View>
</View>
......@@ -951,13 +963,13 @@ const EditListModal = () => {
}}
onPress ={() =>setAddImageVisible(true)}>
<Image
source={meal}
// source={meal}
style={{
width:30,
height:30}}
/>
<Image
source={{uri: filePath.uri}}
// source={{uri: filePath.uri}}
/>
</TouchableOpacity>
</View>
......@@ -971,7 +983,7 @@ const EditListModal = () => {
style={{
marginHorizontal:5
}}
onPress={() => setEditModalVisible(false)}>
onPress={() => setConfirmEditModalVisible(true)}>
<View
style={{
backgroundColor:'transparent',
......@@ -1000,7 +1012,7 @@ const EditListModal = () => {
style={{
marginHorizontal:5
}}
// onPress={addToList}
onPress={updateList}
>
<View
style={{
......@@ -1075,7 +1087,7 @@ const DeleteListModal = () => {
fontWeight:'bold',
marginBottom:20
}}>
Delete this record?
Delete this shopping list?
</Text>
<Text
style={{
......@@ -1122,7 +1134,7 @@ const DeleteListModal = () => {
<TouchableOpacity
style={{
marginHorizontal:5}}
onPress={() => setDeleteModalVisible(false)}
onPress={deleteList}
>
<View
style={{
......@@ -1135,7 +1147,6 @@ const DeleteListModal = () => {
alignItems:'center',
alignContent:'center',
justifyContent:'center'}}
onPress={deleteList}
>
<Text
style={{
......@@ -1154,6 +1165,130 @@ const DeleteListModal = () => {
)
}
}
const confirmEditModal = () => {
if(confirmEditModalVisible){
return(
<Modal
animationType="none"
transparent={true}
visible={confirmEditModalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
setConfirmEditModalVisible(!confirmEditModalVisible);
}}>
<View
style={{
height:'100%',
width:'100%',
backgroundColor:'rgba(192,192,192,0.8)',
justifyContent:'center',
alignItems:'center'
}}>
<TouchableOpacity
style={{
height:'100%',
width:'100%',
position:'absolute',
top:0,
flex:1
}}
onPress={() => setConfirmEditModalVisible(false)}
/>
<View
style={
styles.centeredView
}>
<View
style={
styles.deleteModalView
}>
<Text
style={{
fontSize:20,
color:'black',
fontWeight:'bold',
marginBottom:20
}}>
Cancel this list update?
</Text>
<Text
style={{
fontSize:15,
color:'black',
fontWeight:'500'
}}>
Are you sure?
</Text>
<View
style={{
flexDirection:'row',
margin:20,
}}>
<TouchableOpacity
style={{
marginHorizontal:5}}
onPress={() => setConfirmEditModalVisible(false)}
>
<View
style={{
backgroundColor:'transparent',
borderRadius:7,
borderColor:'white',
borderWidth:3,
width:100,
height:50,
alignItems:'center',
alignContent:'center',
justifyContent:'center'
}}>
<Text
style={{
fontSize:20,
color:'black',
alignItems:'center',
alignContent:'center',
justifyContent:'center'
}}>
NO
</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
style={{
marginHorizontal:5}}
onPress={()=>{setConfirmEditModalVisible(false), setEditModalVisible(false)}}
>
<View
style={{
backgroundColor:'transparent',
borderRadius:7,
borderColor:'white',
borderWidth:3,
width:100,
height:50,
alignItems:'center',
alignContent:'center',
justifyContent:'center'}}
>
<Text
style={{
fontSize:20,
color:'black',
}}>
YES
</Text>
</View>
</TouchableOpacity>
</View>
</View>
</View>
</View>
</Modal>
)
}
}
return (
<SafeAreaView
style={{
......@@ -1166,7 +1301,7 @@ const DeleteListModal = () => {
backgroundColor:'pink',
width:'100%'
}}>
{empty ? emptyMessage(empty):
{emptyList ? emptyMessage(emptyList):
<FlatList
data={items}
ItemSeparatorComponent={listViewItemSeparator}
......@@ -1203,6 +1338,7 @@ const DeleteListModal = () => {
{AddListModal()}
{EditListModal()}
{DeleteListModal()}
{confirmEditModal()}
</SafeAreaView>
);
};
......@@ -1243,11 +1379,13 @@ const styles = StyleSheet.create({
height:75,
justifyContent:'center',
alignItems:'center',
borderWidth:0.1,
borderColor:'#ffdae0',
borderRadius:5,
},
imageContainer:{
// borderRadius:10,
// borderWidth:2,
// borderColor:'grey',
// borderWidth:5,
flexDirection:'row',
// backgroundColor:'red',
marginTop:11,
......@@ -1355,11 +1493,11 @@ const styles = StyleSheet.create({
marginHorizontal:100,
backgroundColor:'pink',
borderRadius:20,
padding:25,
padding:10,
width:'80%',
height:200,
// justifyContent:'center',
alignItems:'center',
// alignItems:'center',
shadowColor:'#000',
shadowOffset:{
width:0,
......
......@@ -5542,6 +5542,13 @@ react-native-image-picker@^4.10.3:
resolved "https://registry.yarnpkg.com/react-native-image-picker/-/react-native-image-picker-4.10.3.tgz#cdc11d9836b4cfa57e658c0700201babf8fdca10"
integrity sha512-gLX8J6jCBkUt6jogpSdA7YyaGVLGYywRzMEwBciXshihpFZjc/cRlKymAVlu6Q7HMw0j3vrho6pI8ZGC5O/FGg==
react-native-input-spinner@^1.7.12:
version "1.7.12"
resolved "https://registry.yarnpkg.com/react-native-input-spinner/-/react-native-input-spinner-1.7.12.tgz#a6640a0c591a209d508816f04bb71e30db514a54"
integrity sha512-U6Cprz8TocsBjbk2fXeaQs5wuHnJCrbIlVbSgL/5FF9X5Mpzp/xcTyqF4I/fUL0z5uZrVceAmEJ2pWBcAJPtew==
dependencies:
prop-types "^15.7.2"
react-native-numeric-input@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/react-native-numeric-input/-/react-native-numeric-input-1.9.1.tgz#eff20699a51ecce6cbd1e3682f68dad981d84e35"
......
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