app integraded :)😈
This commit is contained in:
@@ -15,13 +15,14 @@
|
|||||||
"@react-navigation/stack": "^6.0.11",
|
"@react-navigation/stack": "^6.0.11",
|
||||||
"@reduxjs/toolkit": "^1.6.2",
|
"@reduxjs/toolkit": "^1.6.2",
|
||||||
"expo": "~43.0.0",
|
"expo": "~43.0.0",
|
||||||
|
"expo-image-picker": "^11.0.3",
|
||||||
"expo-status-bar": "~1.1.0",
|
"expo-status-bar": "~1.1.0",
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
"react-dom": "17.0.1",
|
"react-dom": "17.0.1",
|
||||||
"react-native": "0.64.2",
|
"react-native": "0.64.2",
|
||||||
|
"react-native-animatable": "^1.3.3",
|
||||||
"react-native-gesture-handler": "~1.10.2",
|
"react-native-gesture-handler": "~1.10.2",
|
||||||
"react-native-modal": "^13.0.0",
|
"react-native-modal": "^13.0.0",
|
||||||
"react-native-reanimated": "~2.2.0",
|
|
||||||
"react-native-safe-area-context": "3.3.2",
|
"react-native-safe-area-context": "3.3.2",
|
||||||
"react-native-screens": "~3.8.0",
|
"react-native-screens": "~3.8.0",
|
||||||
"react-native-vector-icons": "^9.0.0",
|
"react-native-vector-icons": "^9.0.0",
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ function App() {
|
|||||||
<Stack.Screen name="Groceries" component={GroceryListPage} />
|
<Stack.Screen name="Groceries" component={GroceryListPage} />
|
||||||
<Stack.Screen name="Recipes" component={RecipePage} />
|
<Stack.Screen name="Recipes" component={RecipePage} />
|
||||||
<Stack.Screen name="Recipe" component={Recipe} />
|
<Stack.Screen name="Recipe" component={Recipe} />
|
||||||
|
<Stack.Screen name="AddRecipe" component={AddRecipe} />
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
</NavigationContainer>
|
</NavigationContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const Dropdown = (props) => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<WrapperDropdown keyboardShouldPersistTaps={'always'}>
|
<WrapperDropdown keyboardShouldPersistTaps={'always'} nestedScrollEnabled = {true}>
|
||||||
{dropdownList}
|
{dropdownList}
|
||||||
</WrapperDropdown>
|
</WrapperDropdown>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export default React.memo((props) => {
|
|||||||
< WrapperList listLength={items.length} onLayout={(event) => { HandleAnimation(event) }}>
|
< WrapperList listLength={items.length} onLayout={(event) => { HandleAnimation(event) }}>
|
||||||
{list.open &&
|
{list.open &&
|
||||||
<FlatList style={{ width: '100%' }}
|
<FlatList style={{ width: '100%' }}
|
||||||
initialNumToRender={5}
|
initialNumToRender={10}
|
||||||
maxToRenderPerBatch={5}
|
maxToRenderPerBatch={5}
|
||||||
ListFooterComponent={filteredItems.length === 0 && <ListSubtitle style={{ fontSize: 22, width: '100%', height: 30, alignSelf: 'center' }} >Add a grocery</ListSubtitle>}
|
ListFooterComponent={filteredItems.length === 0 && <ListSubtitle style={{ fontSize: 22, width: '100%', height: 30, alignSelf: 'center' }} >Add a grocery</ListSubtitle>}
|
||||||
data={filteredItems}
|
data={filteredItems}
|
||||||
|
|||||||
@@ -7,18 +7,19 @@ import { CheckButton } from '../../../styles/componentBlueprints';
|
|||||||
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
|
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
|
||||||
import { selectItemById, checkToggle, modalToggle } from '../../../redux/slices/groceryList/itemsSlice'
|
import { selectItemById, checkToggle, modalToggle } from '../../../redux/slices/groceryList/itemsSlice'
|
||||||
import { findTag } from '../../../redux/slices/groceryList/tagsSlice'
|
import { findTag } from '../../../redux/slices/groceryList/tagsSlice'
|
||||||
|
import { toggleOffAnim, toggleOnAnim } from '../../../styles/animations';
|
||||||
|
|
||||||
const ListedItem = React.memo((props) => {
|
const ListedItem = React.memo((props) => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const item = useSelector(state => selectItemById(state, props.item._id), shallowEqual)
|
const item = useSelector(state => selectItemById(state, props.item._id), shallowEqual)
|
||||||
const tag = useSelector(state => findTag(state, item.tag))
|
const tag = useSelector(state => findTag(state, item.tag))
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper >
|
||||||
<DarkLayer>
|
<DarkLayer animation={item.checked ? toggleOnAnim: toggleOffAnim}>
|
||||||
<WrapperItem person={item.person} color={tag.color} checked={item.checked} onPress={() => { props.setVisible(true); return dispatch(modalToggle(item._id)) }}>
|
<WrapperItem person={item.person} color={tag.color} checked={item.checked} onPress={() => { props.setVisible(true); return dispatch(modalToggle(item._id)) }}>
|
||||||
<TextProductName >{item.productName}</TextProductName>
|
<TextProductName >{item.productName}</TextProductName>
|
||||||
{item.details != "" && <TextDetails>{item.details}</TextDetails>}
|
{item.details != "" && <TextDetails>{item.details}</TextDetails>}
|
||||||
<TextAmount>{item.amount.am}{item.amount.qt && " " + item.amount.qt}</TextAmount>
|
<TextAmount>{item.amount.am}{item.amount.qt && " x (" + item.amount.qt + ")"}</TextAmount>
|
||||||
{item.person != "" && <TextPerson>{item.person}</TextPerson>}
|
{item.person != "" && <TextPerson>{item.person}</TextPerson>}
|
||||||
</WrapperItem>
|
</WrapperItem>
|
||||||
</DarkLayer>
|
</DarkLayer>
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
|
||||||
import { Text, View, Image, TouchableOpacity} from 'react-native'
|
import { Text, View, Image, TouchableOpacity} from 'react-native'
|
||||||
|
|
||||||
import styled, { css } from 'styled-components'
|
import styled, { css } from 'styled-components'
|
||||||
|
|
||||||
export const Wrapper = styled(View)`
|
export const Wrapper = styled(View)`
|
||||||
@@ -8,7 +11,7 @@ export const Wrapper = styled(View)`
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`
|
`
|
||||||
export const DarkLayer = styled(View)`
|
export const DarkLayer = styled(Animatable.View)`
|
||||||
background-color: ${({ theme }) => theme.colors.dp00};
|
background-color: ${({ theme }) => theme.colors.dp00};
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@@ -31,7 +34,7 @@ export const WrapperItem = styled(TouchableOpacity)`
|
|||||||
background-color: ${props => props.checked ? props.theme.colors.itemSelected : props.color + '66'};
|
background-color: ${props => props.checked ? props.theme.colors.itemSelected : props.color + '66'};
|
||||||
`
|
`
|
||||||
export const WrapperButton = styled(TouchableOpacity)`
|
export const WrapperButton = styled(TouchableOpacity)`
|
||||||
height: 30px;
|
min-height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
`
|
`
|
||||||
export const TextProductName = styled(Text)`
|
export const TextProductName = styled(Text)`
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import * as Animatable from 'react-native-animatable';
|
||||||
//components
|
//components
|
||||||
//styling
|
//styling
|
||||||
import { Wrapper, WrapperProduct, WrapperButton, WrapperText, TextProductName, TextTag, TextPrice, StyledImage, IconCheck } from './styles/listedProduct'
|
import { Wrapper, WrapperProduct, WrapperButton, WrapperText, TextProductName, TextTag, TextPrice, StyledImage, IconCheck } from './styles/listedProduct'
|
||||||
@@ -7,6 +8,7 @@ import { useSelector, useDispatch, shallowEqual } from 'react-redux';
|
|||||||
import { selectProductById, checkToggle, modalToggle } from '../../../redux/slices/groceryList/productsSlice'
|
import { selectProductById, checkToggle, modalToggle } from '../../../redux/slices/groceryList/productsSlice'
|
||||||
import { findTag } from '../../../redux/slices/groceryList/tagsSlice'
|
import { findTag } from '../../../redux/slices/groceryList/tagsSlice'
|
||||||
import { CheckButton } from '../../../styles/componentBlueprints';
|
import { CheckButton } from '../../../styles/componentBlueprints';
|
||||||
|
import { toggleOffAnim, toggleOnAnim } from '../../../styles/animations';
|
||||||
|
|
||||||
const ListedProduct = React.memo((props) => {
|
const ListedProduct = React.memo((props) => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
@@ -14,14 +16,16 @@ const ListedProduct = React.memo((props) => {
|
|||||||
const tag = useSelector(state => findTag(state, product.tag))
|
const tag = useSelector(state => findTag(state, product.tag))
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<WrapperProduct color={tag.color} checked={product.checked} onPress={() => { props.setVisible(true); return dispatch(modalToggle(product._id)) }}>
|
<Animatable.View style={{ flex: 1 }} animation={product.checked ? toggleOnAnim : toggleOffAnim} >
|
||||||
<WrapperText>
|
<WrapperProduct color={tag.color} checked={product.checked} onPress={() => { props.setVisible(true); return dispatch(modalToggle(product._id)) }}>
|
||||||
<TextProductName >{product.productName}</TextProductName>
|
<WrapperText>
|
||||||
<TextTag color={tag.color}>{tag.tagName}</TextTag>
|
<TextProductName >{product.productName}</TextProductName>
|
||||||
{product.price !== 0 && <TextPrice>€ {product.price}</TextPrice>}
|
<TextTag color={tag.color}>{tag.tagName}</TextTag>
|
||||||
</WrapperText>
|
{product.price !== 0 && <TextPrice>€ {product.price}</TextPrice>}
|
||||||
{product.image != "" && <StyledImage source={{ uri: product.image }} />}
|
</WrapperText>
|
||||||
</WrapperProduct>
|
{product.image != "" && <StyledImage source={{ uri: product.image }} />}
|
||||||
|
</WrapperProduct>
|
||||||
|
</Animatable.View>
|
||||||
<WrapperButton onPress={() => dispatch(checkToggle(product._id))} >
|
<WrapperButton onPress={() => dispatch(checkToggle(product._id))} >
|
||||||
<CheckButton checked={product.checked}>
|
<CheckButton checked={product.checked}>
|
||||||
<IconCheck checked={product.checked} />
|
<IconCheck checked={product.checked} />
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ export const Wrapper = styled(View)`
|
|||||||
export const WrapperProduct = styled(TouchableOpacity)`
|
export const WrapperProduct = styled(TouchableOpacity)`
|
||||||
box-shadow: ${({theme})=> theme.colors.shadow};
|
box-shadow: ${({theme})=> theme.colors.shadow};
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
width: 98%;
|
||||||
width: 100%;
|
|
||||||
min-height: 70px;
|
min-height: 70px;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -30,7 +29,7 @@ export const WrapperProduct = styled(TouchableOpacity)`
|
|||||||
background-color: ${props => props.checked ? props.theme.colors.itemSelected : props.color + '66'};
|
background-color: ${props => props.checked ? props.theme.colors.itemSelected : props.color + '66'};
|
||||||
`
|
`
|
||||||
export const WrapperButton = styled(TouchableOpacity)`
|
export const WrapperButton = styled(TouchableOpacity)`
|
||||||
height: 60px;
|
height: 70px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useHeaderHeight } from '@react-navigation/elements';
|
|||||||
const HeaderPadding = (props) => {
|
const HeaderPadding = (props) => {
|
||||||
const headerHeight = useHeaderHeight();
|
const headerHeight = useHeaderHeight();
|
||||||
return (
|
return (
|
||||||
<View style={{ height: headerHeight + 10 }} />
|
<View style={{ opacity: 0, height: headerHeight + 10 }} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default HeaderPadding;
|
export default HeaderPadding;
|
||||||
@@ -61,7 +61,7 @@ const ModalAddItem = (props) => {
|
|||||||
let product = products.find((product) => product.productName === pName);
|
let product = products.find((product) => product.productName === pName);
|
||||||
setProductName(product.productName)
|
setProductName(product.productName)
|
||||||
setTag(product.tag)
|
setTag(product.tag)
|
||||||
setPrice(product.price)
|
setPrice(product.price && product.price.toString())
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<StyledModal isVisible={props.visible} animationIn={'slideInUp'} animationOut={'slideOutDown'}
|
<StyledModal isVisible={props.visible} animationIn={'slideInUp'} animationOut={'slideOutDown'}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const ModalEditProduct = (props) => {
|
|||||||
</WrapperDropdown>}
|
</WrapperDropdown>}
|
||||||
<WrapperInput>
|
<WrapperInput>
|
||||||
<IconDollar />
|
<IconDollar />
|
||||||
<Input type={"number-pad"}
|
<Input keyboardType={"number-pad"}
|
||||||
value={price}
|
value={price}
|
||||||
onChangeText={(text) => setPrice(text)}
|
onChangeText={(text) => setPrice(text)}
|
||||||
placeholder="Price" />
|
placeholder="Price" />
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const ModalAddIngredients = (props) => {
|
|||||||
<IconMeal />
|
<IconMeal />
|
||||||
<Input
|
<Input
|
||||||
style={{ fontSize: 20, width: 100 }}
|
style={{ fontSize: 20, width: 100 }}
|
||||||
type="number-pad"
|
keyboardType={"number-pad"}
|
||||||
value={servings}
|
value={servings}
|
||||||
onChangeText={(text) => setServings(text)}
|
onChangeText={(text) => setServings(text)}
|
||||||
min={amountOfServings}
|
min={amountOfServings}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const WrapperInput = styled(View)`
|
|||||||
margin-bottom:5px;
|
margin-bottom:5px;
|
||||||
`
|
`
|
||||||
export const WrapperDropdown = styled(View)`
|
export const WrapperDropdown = styled(View)`
|
||||||
margin-left: 16px;
|
margin-left: 42px;
|
||||||
margin-top: -5px;
|
margin-top: -5px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
`
|
`
|
||||||
@@ -68,7 +68,7 @@ export const Button = styled(TouchableOpacity)`
|
|||||||
export const ButtonText = styled(Text)`
|
export const ButtonText = styled(Text)`
|
||||||
color: ${props => props.theme.colors.primary};
|
color: ${props => props.theme.colors.primary};
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: ${({ theme }) => theme.fontSizes.fontS}px;
|
font-size: ${({ theme }) => theme.fontSizes.fontM}px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const ModalHeader = styled(Text)`
|
export const ModalHeader = styled(Text)`
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native'
|
import { TouchableOpacity, View } from 'react-native'
|
||||||
import styled from "styled-components"
|
import styled from "styled-components"
|
||||||
|
|
||||||
import { Ingredient } from "./addRecipe/Ingredient"
|
import { Ingredient } from "./addRecipe/Ingredient"
|
||||||
@@ -14,7 +14,7 @@ export const Wrapper = styled(View)`
|
|||||||
border-bottom-color: ${props => props.theme.colors.dp01};
|
border-bottom-color: ${props => props.theme.colors.dp01};
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
${props => props.checked === true && css`background-color: ` + props.theme.colors.selected + '33'};
|
${props => props.checked === true && "background-color:" + props.theme.colors.selected + '33'};
|
||||||
`
|
`
|
||||||
|
|
||||||
const IngredientButton = (props) => {
|
const IngredientButton = (props) => {
|
||||||
@@ -23,9 +23,9 @@ const IngredientButton = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Wrapper checked={ingredient.checked}>
|
<Wrapper checked={ingredient.checked}>
|
||||||
<Ingredient ingredient={ingredient} multiplier={props.multiplier}/>
|
<Ingredient ingredient={ingredient} multiplier={props.multiplier}/>
|
||||||
<View onClick={() => dispatch(ingredientCheckToggle({ id: props.recipeId, productName: ingredient.productName }))} >
|
<TouchableOpacity onPress={() => dispatch(ingredientCheckToggle({ id: props.recipeId, productName: ingredient.productName }))} >
|
||||||
<CheckButton checked={ingredient.checked} />
|
<CheckButton checked={ingredient.checked} />
|
||||||
</View>
|
</TouchableOpacity>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,22 +11,22 @@ import { removeRecipe } from "../../../redux/slices/recipesSlice"
|
|||||||
import { PlusIcon, WrapperAddItem } from "../../GroceryList/groceries/styles/buttons"
|
import { PlusIcon, WrapperAddItem } from "../../GroceryList/groceries/styles/buttons"
|
||||||
import ModalAddIngredients from "../../modals/recipes/ModalAddIngredients"
|
import ModalAddIngredients from "../../modals/recipes/ModalAddIngredients"
|
||||||
import { Alert } from "react-native"
|
import { Alert } from "react-native"
|
||||||
|
import { useNavigation } from "@react-navigation/core";
|
||||||
|
import { TouchableOpacity } from "react-native-gesture-handler";
|
||||||
|
|
||||||
export const AddRecipeButton = () => {
|
export const AddRecipeButton = () => {
|
||||||
|
let navigation = useNavigation()
|
||||||
return (
|
return (
|
||||||
// < Link to="/recipes/addRecipe">
|
<WrapperAddRecipe onPress={() => navigation.navigate('AddRecipe', {id: ""})}>
|
||||||
<WrapperAddRecipe>
|
<IconPlus />
|
||||||
<IconPlus />
|
</WrapperAddRecipe>
|
||||||
</WrapperAddRecipe>
|
|
||||||
// </Link>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddIngredientsButton = (props) => {
|
export const AddIngredientsButton = (props) => {
|
||||||
const [visible, setVisible] = useState(false)
|
const [visible, setVisible] = useState(false)
|
||||||
return (<>
|
return (<>
|
||||||
<WrapperAddItem style={{ bottom: 10 }} onPress={()=>setVisible(true)}>
|
<WrapperAddItem style={{ bottom: 10 }} onPress={() => setVisible(true)}>
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
</WrapperAddItem>
|
</WrapperAddItem>
|
||||||
<ModalAddIngredients id={props.id} visible={visible} closeModal={() => setVisible(false)} />
|
<ModalAddIngredients id={props.id} visible={visible} closeModal={() => setVisible(false)} />
|
||||||
@@ -37,6 +37,7 @@ export const AddIngredientsButton = (props) => {
|
|||||||
export const OptionsButtonRecipe = (props) => {
|
export const OptionsButtonRecipe = (props) => {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
const [toggled, setToggled] = useState(false)
|
const [toggled, setToggled] = useState(false)
|
||||||
|
const navigation = useNavigation()
|
||||||
const handleRemove = () => {
|
const handleRemove = () => {
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
"Warning",
|
"Warning",
|
||||||
@@ -45,6 +46,7 @@ export const OptionsButtonRecipe = (props) => {
|
|||||||
{ text: "Cancel", },
|
{ text: "Cancel", },
|
||||||
{
|
{
|
||||||
text: "Remove", onPress: () => {
|
text: "Remove", onPress: () => {
|
||||||
|
navigation.goBack()
|
||||||
dispatch(removeRecipe(props.id))
|
dispatch(removeRecipe(props.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,11 +56,12 @@ export const OptionsButtonRecipe = (props) => {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<WrapperOptions toggled={toggled} >
|
<WrapperOptions toggled={toggled} >
|
||||||
<IconOptions toggled={toggled} onClick={() => setToggled(!toggled)} />
|
<TouchableOpacity onPress={() => handleRemove()} >
|
||||||
<WrapperOptionButtons toggled={toggled}>
|
<IconRemove />
|
||||||
<IconRemove onClick={() => handleRemove()} />
|
</TouchableOpacity>
|
||||||
<IconEdit onClick={() => history.push("/recipes/addRecipe/" + props.id)} />
|
<TouchableOpacity onPress={() => navigation.navigate("AddRecipe", { id: props.id })} >
|
||||||
</WrapperOptionButtons>
|
<IconEdit />
|
||||||
|
</TouchableOpacity>
|
||||||
</WrapperOptions>
|
</WrapperOptions>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from "react-native"
|
import { TouchableOpacity, View } from "react-native"
|
||||||
import { useSelector } from "react-redux"
|
import { useSelector } from "react-redux"
|
||||||
|
|
||||||
import { WrapperIngredient, WrapperIngredientLeft, TagCircle, IngredientName, IngredientPortion, IngredientAmount, IconEdit, IconRemove } from './styles/ingredient'
|
import { WrapperIngredient, WrapperIngredientLeft, TagCircle, IngredientName, IngredientPortion, IngredientAmount, IconEdit, IconRemove } from './styles/ingredient'
|
||||||
@@ -12,12 +12,11 @@ export const Ingredient = (props) => {
|
|||||||
<WrapperIngredient>
|
<WrapperIngredient>
|
||||||
<WrapperIngredientLeft>
|
<WrapperIngredientLeft>
|
||||||
<TagCircle tagColor={tag.color} />
|
<TagCircle tagColor={tag.color} />
|
||||||
<IngredientPortion>{ingredient.portion}</IngredientPortion>
|
<IngredientPortion>{ingredient.amount * multiplier} x ({ingredient.portion})</IngredientPortion>
|
||||||
<IngredientName>{ingredient.productName}</IngredientName>
|
<IngredientName>{ingredient.productName}</IngredientName>
|
||||||
</WrapperIngredientLeft>
|
</WrapperIngredientLeft>
|
||||||
<IngredientAmount>{ingredient.amount*multiplier}</IngredientAmount>
|
{props.EditIngredient && <TouchableOpacity onPress={() => props.EditIngredient(props.index)} ><IconEdit /></TouchableOpacity>}
|
||||||
{props.EditIngredient && <IconEdit onClick={() => props.EditIngredient(props.index)} />}
|
{props.RemoveIngredient && <TouchableOpacity onPress={() => props.RemoveIngredient(props.index)} ><IconRemove /></TouchableOpacity>}
|
||||||
{props.RemoveIngredient && <IconRemove onClick={() => props.RemoveIngredient(props.index)} />}
|
|
||||||
</WrapperIngredient>
|
</WrapperIngredient>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { View } from "react-native"
|
import { Text, View } from "react-native"
|
||||||
import { useSelector } from "react-redux"
|
import { useSelector } from "react-redux"
|
||||||
import { selectAllProducts } from "../../../redux/slices/groceryList/productsSlice"
|
import { selectAllProducts } from "../../../redux/slices/groceryList/productsSlice"
|
||||||
import Dropdown from "../../Dropdown"
|
import Dropdown from "../../Dropdown"
|
||||||
import { Input } from "../../modals/styles/modal"
|
import { ButtonText, Input } from "../../modals/styles/modal"
|
||||||
import { Ingredient } from "./Ingredient"
|
import { Ingredient } from "./Ingredient"
|
||||||
import {
|
import {
|
||||||
WrapperIngredients, InputIngredientName, InputAmount, InputPortion, Button
|
WrapperIngredients, InputIngredientName, InputAmount, InputPortion, Button, Row, TitleIngredients, WrapperDropdown
|
||||||
} from "./styles/inputs"
|
} from "./styles/inputs"
|
||||||
export const Ingredients = (props) => {
|
export const Ingredients = (props) => {
|
||||||
let ingredients = props.ingredients
|
let ingredients = props.ingredients
|
||||||
@@ -67,54 +67,56 @@ export const Ingredients = (props) => {
|
|||||||
let product = products.find((product) => product.productName === pName);
|
let product = products.find((product) => product.productName === pName);
|
||||||
setIngredientName(product.productName)
|
setIngredientName(product.productName)
|
||||||
setTag(product.tag)
|
setTag(product.tag)
|
||||||
setPrice(product.price)
|
setPrice(product.price && product.price.toString())
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<WrapperIngredients>
|
<WrapperIngredients>
|
||||||
<h2>Ingredients</h2>
|
<TitleIngredients>Ingredients</TitleIngredients>
|
||||||
<View id="row">
|
<Row>
|
||||||
<View style={{ width: '100%' }}>
|
<View style={{ width: '100%' }}>
|
||||||
{IngredientList}
|
{IngredientList}
|
||||||
<InputIngredientName onFocus={() => setFocused(true)} onBlur={() => { setTimeout(() => { setFocused(false) }, 100) }}
|
<InputIngredientName onFocus={() => setFocused(true)} onEndEditing={() => setFocused(false)}
|
||||||
type="text"
|
|
||||||
value={ingredientName}
|
value={ingredientName}
|
||||||
onChange={(text) => setIngredientName(text.target.value)}
|
onChangeText={(text) => setIngredientName(text)}
|
||||||
placeholder="Name ingredient" />
|
placeholder="Name ingredient" />
|
||||||
{focused &&
|
|
||||||
<Dropdown array={products.map(product => product.productName)} text={ingredientName} setElement={handleDropdownPress} />}
|
<WrapperDropdown>
|
||||||
<View id="row">
|
{focused &&
|
||||||
<View>
|
<Dropdown array={products.map(product => product.productName)} text={ingredientName} setElement={handleDropdownPress} />}
|
||||||
<Input
|
</WrapperDropdown>
|
||||||
onFocus={() => setFocusedTag(true)} onBlur={() => { setTimeout(() => { setFocusedTag(false) }, 100) }}
|
<Row>
|
||||||
style={{ marginLeft: 0 }}
|
|
||||||
type="text"
|
|
||||||
value={tag}
|
|
||||||
onChange={(text) => setTag(text.target.value)}
|
|
||||||
placeholder="tag" />
|
|
||||||
{focusedTag &&
|
|
||||||
<Dropdown array={tags.map(t => t.tagName)} text={tag} setElement={setTag} />}
|
|
||||||
</View>
|
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
onFocus={() => setFocusedTag(true)} onEndEditing={() => setFocusedTag(false)}
|
||||||
|
style={{ marginLeft: 0 }}
|
||||||
|
type="text"
|
||||||
|
value={tag}
|
||||||
|
onChangeText={(text) => setTag(text)}
|
||||||
|
placeholder="tag" />
|
||||||
|
<Input
|
||||||
|
keyboardType={"number-pad"}
|
||||||
value={price}
|
value={price}
|
||||||
onChange={(text) => setPrice(text.target.value)}
|
onChangeText={(text) => setPrice(text)}
|
||||||
placeholder="Price" />
|
placeholder="Price" />
|
||||||
|
</Row>
|
||||||
|
<View style={{marginTop: 0}}>
|
||||||
|
{focusedTag &&
|
||||||
|
<Dropdown array={tags.map(t => t.tagName)} text={tag} setElement={setTag} />}
|
||||||
</View>
|
</View>
|
||||||
<View id="row">
|
<Row>
|
||||||
<InputAmount
|
<InputAmount
|
||||||
type="number"
|
keyboardType={"number-pad"}
|
||||||
value={amount}
|
value={amount}
|
||||||
onChange={(text) => setAmount(text.target.value)}
|
onChangeText={(text) => setAmount(text)}
|
||||||
placeholder="amt." />
|
placeholder="amt." />
|
||||||
<InputPortion
|
<InputPortion
|
||||||
type="text"
|
type="text"
|
||||||
value={portion}
|
value={portion}
|
||||||
onChange={(text) => setPortion(text.target.value)}
|
onChangeText={(text) => setPortion(text)}
|
||||||
placeholder="Portion (1/2 teaspoon, ...)" />
|
placeholder="Portion (1/2 teaspoon, ...)" />
|
||||||
<Button onClick={submitIngredient} >{index === -1 ? 'Add' : 'Edit'}</Button>
|
<Button onPress={submitIngredient} ><ButtonText>{index === -1 ? 'Add' : 'Edit'}</ButtonText></Button>
|
||||||
</View>
|
</Row>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</Row >
|
||||||
</WrapperIngredients>
|
</WrapperIngredients >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -15,30 +15,8 @@ export const WrapperAddRecipe = styled(Button)`
|
|||||||
export const IconPlus = () => <MaterialCommunityIcons name="plus" color={theme.colors.textB2} size={60} />
|
export const IconPlus = () => <MaterialCommunityIcons name="plus" color={theme.colors.textB2} size={60} />
|
||||||
|
|
||||||
export const WrapperOptions = styled(View)`
|
export const WrapperOptions = styled(View)`
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
right: 0px;
|
|
||||||
`
|
|
||||||
|
|
||||||
export const WrapperOptionButtons = styled(View)`
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
position: absolute;
|
|
||||||
align-items: center;
|
|
||||||
top: 60px;
|
|
||||||
width: 55px;
|
|
||||||
right: 0px;
|
|
||||||
height: ${props => props.toggled ? css`100px` : css`0px` };
|
|
||||||
background-color: ${({theme}) => theme.colors.dp00 +'bb'};
|
|
||||||
border-bottom-left-radius: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
`
|
|
||||||
const Options = () => <MaterialCommunityIcons name="cog-outline" color={theme.colors.textW2} size={40} />
|
|
||||||
|
|
||||||
export const IconOptions = styled(Options)`
|
|
||||||
margin-right: 5px;
|
|
||||||
margin-top: 10px;
|
|
||||||
transform: rotate(${props => props.toggled ? css`135deg` : css`0deg` });
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const Remove = () => <MaterialCommunityIcons name="trash-can-outline" color={theme.colors.error} size={40} />
|
const Remove = () => <MaterialCommunityIcons name="trash-can-outline" color={theme.colors.error} size={40} />
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import React from 'react';
|
||||||
import { Text, View } from "react-native"
|
import { Text, View } from "react-native"
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ export const WrapperIngredient = styled(View)`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
`
|
`
|
||||||
export const WrapperIngredientLeft = styled(View)`
|
export const WrapperIngredientLeft = styled(View)`
|
||||||
@@ -29,32 +30,25 @@ export const TagCircle = styled(View)`
|
|||||||
export const IngredientName = styled(Text)`
|
export const IngredientName = styled(Text)`
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
width: 60%;
|
width: 60%;
|
||||||
font-size: ${({ theme }) => theme.fontSizes.fontS}px;
|
font-size: 18px;
|
||||||
/* overflow-wrap: break-word; */
|
/* overflow-wrap: break-word; */
|
||||||
color: ${props => props.theme.colors.textW4};
|
color: ${props => props.theme.colors.textW4};
|
||||||
`
|
`
|
||||||
export const IngredientPortion = styled(Text)`
|
export const IngredientPortion = styled(Text)`
|
||||||
width: 35%;
|
width: 35%;
|
||||||
font-size: ${({ theme }) => theme.fontSizes.fontS}px;
|
font-size: 18px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
/* word-break: break-all; */
|
/* word-break: break-all; */
|
||||||
/* overflow-wrap: break-word; */
|
/* overflow-wrap: break-word; */
|
||||||
color: ${props => props.theme.colors.textW5};
|
color: ${props => props.theme.colors.textW5};
|
||||||
`
|
`
|
||||||
export const IngredientAmount = styled(Text)`
|
const Remove = () => <MaterialCommunityIcons name="trash-can-outline" color={theme.colors.error + 'aa'} size={theme.fontSizes.fontM} />
|
||||||
max-width: 12%;
|
|
||||||
margin-right: 48px;
|
|
||||||
font-size: ${({theme}) => theme.fontSizes.fontS}px;
|
|
||||||
/* overflow-wrap: break-word; */
|
|
||||||
color: ${props => props.theme.colors.textW5};
|
|
||||||
`
|
|
||||||
const Remove = () => <MaterialCommunityIcons name="trash-can-outline" color={theme.colors.error + 'aa'} size={theme.fontSizes.fontS} />
|
|
||||||
|
|
||||||
export const IconRemove = styled(Remove)`
|
export const IconRemove = styled(Remove)`
|
||||||
position:absolute;
|
position:absolute;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
`
|
`
|
||||||
const Edit = () => <MaterialCommunityIcons name="pencil-outline" color={theme.colors.textW5} size={theme.fontSizes.fontS} />
|
const Edit = () => <MaterialCommunityIcons name="pencil-outline" color={theme.colors.textW5} size={theme.fontSizes.fontM} />
|
||||||
|
|
||||||
export const IconEdit = styled(Edit)`
|
export const IconEdit = styled(Edit)`
|
||||||
position:absolute;
|
position:absolute;
|
||||||
|
|||||||
@@ -1,34 +1,49 @@
|
|||||||
import { Text, TextInput, View } from "react-native"
|
import { Text, TextInput, View } from "react-native"
|
||||||
import styled from "styled-components"
|
import styled from "styled-components"
|
||||||
|
import { Input } from "../../../modals/styles/modal"
|
||||||
export const WrapperIngredients = styled(View)`
|
export const WrapperIngredients = styled(View)`
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
width:100%;
|
width:100%;
|
||||||
border-left: solid ${props => props.theme.colors.primary + '55'} 1px;
|
border-left-color: ${props => props.theme.colors.primary + '55'} ;
|
||||||
|
border-left-width: 1px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
/* #row{
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex:1;
|
|
||||||
width:100%;
|
|
||||||
} */
|
|
||||||
`
|
`
|
||||||
export const InputIngredientName = styled(TextInput)`
|
export const Row = styled(View)`
|
||||||
margin-top: 10px;
|
position: relative;
|
||||||
font-size: ${({theme}) => theme.fontSizes.fontS}px;
|
display: flex;
|
||||||
border-bottom-color: ${props => props.theme.colors.primary + 'aa'};
|
flex-direction: row;
|
||||||
|
width:100%;
|
||||||
|
`
|
||||||
|
export const WrapperDropdown = styled(View)`
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
`
|
||||||
|
export const TitleIngredients = styled(Text)`
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: ${props => props.theme.colors.textW2};
|
||||||
|
`
|
||||||
|
export const InputIngredientName = styled(Input)`
|
||||||
|
margin-left: 0px;
|
||||||
|
padding-right: 0px;
|
||||||
|
|
||||||
|
margin: 10px 0px;
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
width:100%;
|
width:100%;
|
||||||
`
|
`
|
||||||
export const InputAmount = styled(TextInput)`
|
export const InputAmount = styled(Input)`
|
||||||
font-size: ${({theme}) => theme.fontSizes.fontS}px;
|
margin-left: 0px;
|
||||||
border-bottom-color: ${props => props.theme.colors.primary + 'aa'};
|
padding-right: 0px;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
`
|
`
|
||||||
export const InputPortion = styled(TextInput)`
|
export const InputPortion = styled(Input)`
|
||||||
font-size: ${({theme}) => theme.fontSizes.fontS}px;
|
margin-left: 0px;
|
||||||
border-bottom-color: ${props => props.theme.colors.primary + 'aa'};
|
padding-right: 0px;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
width: 60%;
|
width: 60%;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
@@ -39,5 +54,5 @@ export const Button = styled(Text)`
|
|||||||
bottom: -8px;
|
bottom: -8px;
|
||||||
color: ${props => props.theme.colors.primary + 'bb'};
|
color: ${props => props.theme.colors.primary + 'bb'};
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: ${({theme}) => theme.fontSizes.fontS}px;
|
font-size: ${({ theme }) => theme.fontSizes.fontS}px;
|
||||||
`
|
`
|
||||||
@@ -13,6 +13,7 @@ export const WrapperRecipeCard = styled(TouchableOpacity)`
|
|||||||
width: 48%;
|
width: 48%;
|
||||||
background-color: ${props => props.theme.colors.dp01 + 'dd'};
|
background-color: ${props => props.theme.colors.dp01 + 'dd'};
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
|
margin-bottom: 4px;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const RecipeName = styled(Text)`
|
export const RecipeName = styled(Text)`
|
||||||
|
|||||||
@@ -1,57 +1,66 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, View } from 'react-native'
|
import { Button, ScrollView, Text, View } from 'react-native'
|
||||||
|
import * as ImagePicker from 'expo-image-picker';
|
||||||
//components
|
//components
|
||||||
import Header from "../../components/Header"
|
import Header from "../../components/Header"
|
||||||
import { ArrowBack } from "../styles/page";
|
import { ArrowBack } from "../styles/page";
|
||||||
import { Ingredients } from "../../components/recipes/addRecipe/Inputs";
|
import { Ingredients } from "../../components/recipes/addRecipe/Inputs";
|
||||||
//styling
|
//styling
|
||||||
import { Wrapper, WrapperRecipe, Input, InputInstructions, IconRecipe, IconPot, IconMeal, IconImage, IconCheck } from "./styles/addRecipe"
|
import { Wrapper, WrapperRecipe, Input, InputInstructions, IconRecipe, IconPot, IconMeal, IconImage, IconCheck, AddImageButton, ButtonText, SubmitButton } from "./styles/addRecipe"
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { addRecipe, findRecipeById, updateRecipe } from "../../redux/slices/recipesSlice";
|
import { addRecipe, findRecipeById, updateRecipe } from "../../redux/slices/recipesSlice";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { StyledImage } from "./styles/recipe";
|
import { StyledImage } from "./styles/recipe";
|
||||||
import HeaderPadding from '../../components/Header';
|
import HeaderPadding from '../../components/Header';
|
||||||
|
import { Row } from '../../components/recipes/addRecipe/styles/inputs';
|
||||||
|
import { useNavigation } from '@react-navigation/core';
|
||||||
|
|
||||||
|
const AddRecipe = (props) => {
|
||||||
|
const id = props.route.params.id
|
||||||
|
|
||||||
|
const navigation = useNavigation()
|
||||||
|
|
||||||
const AddRecipe = () => {
|
|
||||||
let { id } = useParams()
|
|
||||||
const foundRecipe = useSelector(state => findRecipeById(state, id))
|
const foundRecipe = useSelector(state => findRecipeById(state, id))
|
||||||
let recipe = foundRecipe ? foundRecipe : { name: '', prepTime: '', servings: '', image: '', ingredients: [], instructions: '' }
|
let recipe = foundRecipe ? foundRecipe : { name: '', prepTime: '', servings: '', image: '', ingredients: [], instructions: '' }
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
|
|
||||||
const [name, setName] = useState(recipe.name)
|
const [name, setName] = useState(recipe.name)
|
||||||
const [prepTime, setPrepTime] = useState(recipe.prepTime)
|
const [prepTime, setPrepTime] = useState(recipe.prepTime && recipe.prepTime.toString())
|
||||||
const [servings, setServings] = useState(recipe.servings)
|
const [servings, setServings] = useState(recipe.servings && recipe.servings.toString())
|
||||||
const [image, setImage] = useState(recipe.image)
|
const [image, setImage] = useState(recipe.image)
|
||||||
const [ingredients, setIngredients] = useState(recipe.ingredients)
|
const [ingredients, setIngredients] = useState(recipe.ingredients)
|
||||||
const [instructions, setInstructions] = useState(recipe.instructions)
|
const [instructions, setInstructions] = useState(recipe.instructions)
|
||||||
|
|
||||||
const handleImageInput = (input) => {
|
const pickImage = async () => {
|
||||||
if (input.target.files && input.target.files[0] && input.target.files[0].size < 1000000) {
|
let result = await ImagePicker.launchImageLibraryAsync({
|
||||||
let reader = new FileReader()
|
mediaTypes: ImagePicker.MediaTypeOptions.All,
|
||||||
reader.onload = function (e) {
|
allowsEditing: true,
|
||||||
setImage(e.target.result)
|
aspect: [4, 3],
|
||||||
};
|
quality: 1,
|
||||||
reader.readAsDataURL(input.target.files[0])
|
});
|
||||||
|
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
|
if (!result.cancelled) {
|
||||||
|
setImage(result.uri);
|
||||||
}
|
}
|
||||||
else {
|
};
|
||||||
alert("That file is too big! Choose an image with a size lower than 1MB")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const submitRecipe = () => {
|
const submitRecipe = () => {
|
||||||
if (name && ingredients && instructions) {
|
if (name && ingredients && instructions) {
|
||||||
foundRecipe ? dispatch(updateRecipe({ _id: foundRecipe._id, name, prepTime: Number(prepTime), servings: Number(servings), image, ingredients, instructions }))
|
foundRecipe ? dispatch(updateRecipe({ _id: foundRecipe._id, name, prepTime: Number(prepTime), servings: Number(servings), image, ingredients, instructions }))
|
||||||
: dispatch(addRecipe({ name, prepTime: Number(prepTime), servings: Number(servings), image, ingredients, instructions }))
|
: dispatch(addRecipe({ name, prepTime: Number(prepTime), servings: Number(servings), image, ingredients, instructions }))
|
||||||
console.log(typeof (image))
|
navigation.goBack()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
alert("You should add a name, at least one ingredient and the instructions")
|
alert("You should add a name, at least one ingredient and the instructions")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleInput = (event) => {
|
const handleInput = (text) => {
|
||||||
let lastChar = event.target.value.substr(-1).charCodeAt(0);
|
let lastChar = text.substr(-1).charCodeAt(0);
|
||||||
let penultimateChar = event.target.value.substr(-2).charCodeAt(0);
|
let penultimateChar = text.substr(-2).charCodeAt(0);
|
||||||
|
|
||||||
if (instructions === '') {
|
if (instructions === '') {
|
||||||
setInstructions("- " + event.target.value)
|
setInstructions("- " + text)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
else if (penultimateChar === 10 && lastChar === 45) {
|
else if (penultimateChar === 10 && lastChar === 45) {
|
||||||
@@ -59,55 +68,56 @@ const AddRecipe = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
else if (lastChar === 10) {
|
else if (lastChar === 10) {
|
||||||
setInstructions(event.target.value + "- ")
|
setInstructions(text + "- ")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setInstructions(event.target.value)
|
setInstructions(text)
|
||||||
}
|
}
|
||||||
return (
|
return (<>
|
||||||
<>
|
<ScrollView nestedScrollEnabled={true} keyboardShouldPersistTaps={'always'}>
|
||||||
<HeaderPadding/>
|
<HeaderPadding />
|
||||||
|
{image != "" && <StyledImage source={{ uri: image }} alt="Recipe" />}
|
||||||
<Wrapper >
|
<Wrapper >
|
||||||
{image && <StyledImage source={image} alt="Recipe" />}
|
|
||||||
<WrapperRecipe>
|
<WrapperRecipe>
|
||||||
<View id="row">
|
<Row>
|
||||||
<IconRecipe />
|
<IconRecipe />
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(text) => setName(text.target.value)}
|
onChangeText={(text) => setName(text)}
|
||||||
placeholder="Recipe name" />
|
placeholder="Recipe name" />
|
||||||
</View>
|
</Row>
|
||||||
<View id="row">
|
<Row>
|
||||||
<IconPot />
|
<IconPot />
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
keyboardType={"number-pad"}
|
||||||
value={prepTime}
|
value={prepTime}
|
||||||
onChange={(text) => setPrepTime(text.target.value)}
|
onChangeText={(text) => setPrepTime(text)}
|
||||||
placeholder="Prep time (min)" />
|
placeholder="Prep time (min)" />
|
||||||
<IconMeal />
|
<IconMeal />
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
keyboardType={"number-pad"}
|
||||||
value={servings}
|
value={servings}
|
||||||
onChange={(text) => setServings(text.target.value)}
|
onChangeText={(text) => setServings(text)}
|
||||||
placeholder="Servings" />
|
placeholder="Servings" />
|
||||||
</View>
|
</Row>
|
||||||
<View id="row">
|
<AddImageButton onPress={pickImage}
|
||||||
<IconImage />
|
style={{ borderBottom: 'none' }} >
|
||||||
<Input
|
<ButtonText>Add Image</ButtonText>
|
||||||
style={{ borderBottom: 'none' }}
|
</AddImageButton>
|
||||||
type="file" accept="image/*"
|
|
||||||
onChange={input => handleImageInput(input)} />
|
|
||||||
</View>
|
|
||||||
<Ingredients ingredients={ingredients} setIngredients={setIngredients} />
|
<Ingredients ingredients={ingredients} setIngredients={setIngredients} />
|
||||||
<InputInstructions
|
<InputInstructions
|
||||||
type="text"
|
minHeight={40}
|
||||||
|
multiline={true}
|
||||||
value={instructions}
|
value={instructions}
|
||||||
onChange={(text) => handleInput(text)}
|
onChangeText={(text) => handleInput(text)}
|
||||||
placeholder='Instructions' />
|
placeholder='Instructions' />
|
||||||
</WrapperRecipe>
|
</WrapperRecipe>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</>
|
</ScrollView>
|
||||||
|
{name != "" && ingredients != "" && instructions != "" && <SubmitButton onPress={submitRecipe} >
|
||||||
|
<IconCheck />
|
||||||
|
</SubmitButton>}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import { OptionsButtonRecipe, AddIngredientsButton } from "../../components/reci
|
|||||||
import { ScrollView, Text, View } from "react-native"
|
import { ScrollView, Text, View } from "react-native"
|
||||||
import HeaderPadding from "../../components/Header"
|
import HeaderPadding from "../../components/Header"
|
||||||
|
|
||||||
const Recipe = (props) => {
|
const Recipe = ({route, navigation}) => {
|
||||||
const id = props.route.params.id
|
const id = route.params.id
|
||||||
const recipe = useSelector(state => findRecipeById(state, id))
|
const recipe = useSelector(state => findRecipeById(state, id))
|
||||||
const IngredientList = recipe.ingredients.map((ingredient, index) => {
|
const IngredientList = recipe.ingredients.map((ingredient, index) => {
|
||||||
return (<Ingredient ingredient={ingredient} index={index} key={index} />)
|
return (<Ingredient ingredient={ingredient} index={index} key={index} />)
|
||||||
@@ -25,12 +25,21 @@ const Recipe = (props) => {
|
|||||||
<InstructionText>{instruction}</InstructionText>
|
<InstructionText>{instruction}</InstructionText>
|
||||||
</InstructionWrapper>)
|
</InstructionWrapper>)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
React.useLayoutEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
headerRight: () => (
|
||||||
|
<OptionsButtonRecipe id={id} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}, [navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<HeaderPadding/>
|
||||||
|
{recipe.image != "" && <StyledImage source={{ uri: recipe.image }} />}
|
||||||
<ScrollView>
|
<ScrollView>
|
||||||
|
|
||||||
<Wrapper >
|
<Wrapper >
|
||||||
{recipe.image != "" && <StyledImage source={{ uri: recipe.image }} />}
|
|
||||||
<WrapperRecipe>
|
<WrapperRecipe>
|
||||||
<Title>{recipe.name}</Title>
|
<Title>{recipe.name}</Title>
|
||||||
<Hr />
|
<Hr />
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Image, Text, TextInput, View } from "react-native"
|
import { Image, Text, TextInput, TouchableOpacity, View } from "react-native"
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import theme from "../../../styles/theme";
|
import theme from "../../../styles/theme";
|
||||||
|
import { Button } from '../../../styles/componentBlueprints';
|
||||||
|
|
||||||
export const Wrapper = styled(View)`
|
export const Wrapper = styled(View)`
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -38,35 +39,49 @@ export const Input = styled(TextInput)`
|
|||||||
color: ${props => props.theme.colors.textW1};
|
color: ${props => props.theme.colors.textW1};
|
||||||
border-bottom-color: ${props => props.theme.colors.primary + 'aa'};
|
border-bottom-color: ${props => props.theme.colors.primary + 'aa'};
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
font-size: ${({theme}) => theme.fontSizes.fontS}px;
|
font-size: ${({theme}) => theme.fontSizes.fontM}px;
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`
|
`
|
||||||
|
|
||||||
export const InputInstructions = styled(Text)`
|
export const InputInstructions = styled(TextInput)`
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
|
||||||
color: ${props => props.theme.colors.textW1};
|
color: ${props => props.theme.colors.textW1};
|
||||||
border-bottom-color: ${props => props.theme.colors.primary + 'aa'};
|
border-bottom-color: ${props => props.theme.colors.primary + 'aa'};
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
font-size: ${({theme}) => theme.fontSizes.fontS}px;
|
font-size: ${({theme}) => theme.fontSizes.fontM}px;
|
||||||
min-height: 600px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`
|
`
|
||||||
export const StyledImage = styled(Image)`
|
export const AddImageButton = styled(TouchableOpacity)`
|
||||||
width: 100px;
|
justify-content: center;
|
||||||
height: 100px;
|
align-items: center;
|
||||||
|
align-self: center;
|
||||||
|
margin: 10px;
|
||||||
|
width: 140px;
|
||||||
|
height: 40px;
|
||||||
|
background-color:${props => props.theme.colors.primary};
|
||||||
|
border-radius: 15px;
|
||||||
`
|
`
|
||||||
export const IconRecipe = () => <MaterialCommunityIcons name="pasta" color={theme.colors.primaryVar} size={theme.fontSizes.fontM} />
|
export const ButtonText = styled(Text)`
|
||||||
|
color: ${props => props.theme.colors.textB1};
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 20px;
|
||||||
|
`
|
||||||
|
export const SubmitButton = styled(Button)`
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
background-color: ${({theme}) => theme.colors.primary};
|
||||||
|
`
|
||||||
|
export const IconRecipe = () => <MaterialCommunityIcons name="pasta" color={theme.colors.primaryVar} size={theme.fontSizes.fontL} />
|
||||||
|
|
||||||
export const IconPot = () => <MaterialCommunityIcons name="pot-steam" color={theme.colors.primaryVar} size={theme.fontSizes.fontM} />
|
export const IconPot = () => <MaterialCommunityIcons name="pot-steam" color={theme.colors.primaryVar} size={theme.fontSizes.fontL} />
|
||||||
|
|
||||||
export const IconMeal = () => <MaterialCommunityIcons name="pot-steam" color={theme.colors.primaryVar} size={theme.fontSizes.fontM} />
|
export const IconMeal = () => <MaterialCommunityIcons name="pot-steam" color={theme.colors.primaryVar} size={theme.fontSizes.fontL} />
|
||||||
|
|
||||||
export const IconImage = () => <MaterialCommunityIcons name="food" color={theme.colors.primaryVar} size={theme.fontSizes.fontM} />
|
const Check = () => <MaterialCommunityIcons name="check" color={theme.colors.textB3} size={50} />
|
||||||
|
|
||||||
const Check = () => <MaterialCommunityIcons name="check" color={theme.colors.textB3} size={theme.fontSizes.fontL} />
|
|
||||||
|
|
||||||
export const IconCheck = styled(Check)`
|
export const IconCheck = styled(Check)`
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export const InstructionNumber = styled(Text)`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export const StyledImage = styled(Image)`
|
export const StyledImage = styled(Image)`
|
||||||
|
margin-top: -110px;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ export const fetchRecipes = createAsyncThunk('recipes/fetchRecipes', async () =>
|
|||||||
})
|
})
|
||||||
|
|
||||||
export const addRecipe = createAsyncThunk('recipes/addRecipe', async (payload) => {
|
export const addRecipe = createAsyncThunk('recipes/addRecipe', async (payload) => {
|
||||||
const size = new TextEncoder().encode(JSON.stringify(payload)).length
|
// const size = new TextEncoder().encode(JSON.stringify(payload)).length
|
||||||
console.log(size)
|
// console.log(size)
|
||||||
const requestOptions = {
|
const requestOptions = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
|||||||
40
src/styles/animations.js
Normal file
40
src/styles/animations.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
export const toggleOnAnim = {
|
||||||
|
0.0: {
|
||||||
|
scale: 1
|
||||||
|
},
|
||||||
|
0.2: {
|
||||||
|
scale: 0.94
|
||||||
|
},
|
||||||
|
0.4: {
|
||||||
|
scale: 1.02
|
||||||
|
},
|
||||||
|
0.6: {
|
||||||
|
scale: 0.99
|
||||||
|
},
|
||||||
|
0.8: {
|
||||||
|
scale: 1.005
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
scale: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const toggleOffAnim = {
|
||||||
|
0.0: {
|
||||||
|
scale: 1
|
||||||
|
},
|
||||||
|
0.2: {
|
||||||
|
scale: 0.94
|
||||||
|
},
|
||||||
|
0.4: {
|
||||||
|
scale: 1.02
|
||||||
|
},
|
||||||
|
0.6: {
|
||||||
|
scale: 0.99
|
||||||
|
},
|
||||||
|
0.8: {
|
||||||
|
scale: 1.0051
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
scale: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -5,6 +5,8 @@ import styled, { css } from 'styled-components'
|
|||||||
import LightenDarken from '../functions'
|
import LightenDarken from '../functions'
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
import theme from "./theme";
|
import theme from "./theme";
|
||||||
|
import * as Animatable from 'react-native-animatable'
|
||||||
|
import { toggleOffAnim, toggleOnAnim } from "./animations";
|
||||||
//standard button layout
|
//standard button layout
|
||||||
export const Button = styled(TouchableOpacity)`
|
export const Button = styled(TouchableOpacity)`
|
||||||
box-shadow: ${({ theme }) => theme.colors.shadow};
|
box-shadow: ${({ theme }) => theme.colors.shadow};
|
||||||
@@ -15,7 +17,7 @@ export const Button = styled(TouchableOpacity)`
|
|||||||
width: 65px;
|
width: 65px;
|
||||||
border-radius: 35px;
|
border-radius: 35px;
|
||||||
`
|
`
|
||||||
const CheckButtonWrapper = styled(View)`
|
const CheckButtonWrapper = styled(Animatable.View)`
|
||||||
display:flex;
|
display:flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -29,14 +31,14 @@ const CheckButtonWrapper = styled(View)`
|
|||||||
`
|
`
|
||||||
|
|
||||||
export const CheckButton = (props) => {
|
export const CheckButton = (props) => {
|
||||||
return (
|
return (
|
||||||
<CheckButtonWrapper checked={props.checked} >
|
<CheckButtonWrapper animation={props.checked ? toggleOnAnim : toggleOffAnim} checked={props.checked} >
|
||||||
<MaterialCommunityIcons name="check" color={theme.colors.dp00} size={25} />
|
<MaterialCommunityIcons name="check" color={theme.colors.dp00} size={25} />
|
||||||
</CheckButtonWrapper>
|
</CheckButtonWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//load anim
|
//load anim
|
||||||
export const LoadAnimation = () => {
|
export const LoadAnimation = () => {
|
||||||
return (<View style={{ backgroundColor: "red", width: 100, height: 100 }} />)
|
return (<View style={{ backgroundColor: "red", width: 100, height: 100 }} />)
|
||||||
}
|
}
|
||||||
43
yarn.lock
43
yarn.lock
@@ -719,7 +719,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.14.5"
|
"@babel/helper-plugin-utils" "^7.14.5"
|
||||||
|
|
||||||
"@babel/plugin-transform-object-assign@^7.0.0", "@babel/plugin-transform-object-assign@^7.10.4":
|
"@babel/plugin-transform-object-assign@^7.0.0":
|
||||||
version "7.14.5"
|
version "7.14.5"
|
||||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.14.5.tgz"
|
resolved "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.14.5.tgz"
|
||||||
integrity sha512-lvhjk4UN9xJJYB1mI5KC0/o1D5EcJXdbhVe+4fSk08D6ZN+iuAIs7LJC+71h8av9Ew4+uRq9452v9R93SFmQlQ==
|
integrity sha512-lvhjk4UN9xJJYB1mI5KC0/o1D5EcJXdbhVe+4fSk08D6ZN+iuAIs7LJC+71h8av9Ew4+uRq9452v9R93SFmQlQ==
|
||||||
@@ -2605,6 +2605,15 @@ expo-font@~10.0.3:
|
|||||||
expo-modules-core "~0.4.4"
|
expo-modules-core "~0.4.4"
|
||||||
fontfaceobserver "^2.1.0"
|
fontfaceobserver "^2.1.0"
|
||||||
|
|
||||||
|
expo-image-picker@^11.0.3:
|
||||||
|
version "11.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-11.0.3.tgz#c0b6cb9b5fa027f1bd1879a879fe13157f31ac94"
|
||||||
|
integrity sha512-s7nXB+hop5htcETlSvtPhEGE6RM4BX0G2e6mhr2SjJdJPAFn+hY7R3vCGBFMtxV11Gjzr0D1w2y9gDqhOj0GKg==
|
||||||
|
dependencies:
|
||||||
|
"@expo/config-plugins" "^4.0.2"
|
||||||
|
expo-modules-core "~0.4.4"
|
||||||
|
uuid "7.0.2"
|
||||||
|
|
||||||
expo-keep-awake@~10.0.0:
|
expo-keep-awake@~10.0.0:
|
||||||
version "10.0.0"
|
version "10.0.0"
|
||||||
resolved "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-10.0.0.tgz"
|
resolved "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-10.0.0.tgz"
|
||||||
@@ -4147,11 +4156,6 @@ mkdirp@^0.5.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
mockdate@^3.0.2:
|
|
||||||
version "3.0.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb"
|
|
||||||
integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==
|
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||||
@@ -4662,6 +4666,13 @@ react-native-animatable@1.3.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
|
|
||||||
|
react-native-animatable@^1.3.3:
|
||||||
|
version "1.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-animatable/-/react-native-animatable-1.3.3.tgz#a13a4af8258e3bb14d0a9d839917e9bb9274ec8a"
|
||||||
|
integrity sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==
|
||||||
|
dependencies:
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
|
||||||
react-native-codegen@^0.0.6:
|
react-native-codegen@^0.0.6:
|
||||||
version "0.0.6"
|
version "0.0.6"
|
||||||
resolved "https://registry.npmjs.org/react-native-codegen/-/react-native-codegen-0.0.6.tgz"
|
resolved "https://registry.npmjs.org/react-native-codegen/-/react-native-codegen-0.0.6.tgz"
|
||||||
@@ -4690,16 +4701,6 @@ react-native-modal@^13.0.0:
|
|||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
react-native-animatable "1.3.3"
|
react-native-animatable "1.3.3"
|
||||||
|
|
||||||
react-native-reanimated@~2.2.0:
|
|
||||||
version "2.2.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-2.2.3.tgz#edecfe477ad9efac6f006f7e1194e8c9aa4fc6d5"
|
|
||||||
integrity sha512-d+BV39Jp4Om0ZkgVjop672/004ytlTfDT01EloO3HFZs9wR2QTuCjekq8yi3xl0G2xGZKd4DXhvqabIa7OnMYA==
|
|
||||||
dependencies:
|
|
||||||
"@babel/plugin-transform-object-assign" "^7.10.4"
|
|
||||||
fbjs "^3.0.0"
|
|
||||||
mockdate "^3.0.2"
|
|
||||||
string-hash-64 "^1.0.3"
|
|
||||||
|
|
||||||
react-native-safe-area-context@3.3.2:
|
react-native-safe-area-context@3.3.2:
|
||||||
version "3.3.2"
|
version "3.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz#9549a2ce580f2374edb05e49d661258d1b8bcaed"
|
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz#9549a2ce580f2374edb05e49d661258d1b8bcaed"
|
||||||
@@ -5357,11 +5358,6 @@ strict-uri-encode@^2.0.0:
|
|||||||
resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz"
|
||||||
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
|
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
|
||||||
|
|
||||||
string-hash-64@^1.0.3:
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/string-hash-64/-/string-hash-64-1.0.3.tgz#0deb56df58678640db5c479ccbbb597aaa0de322"
|
|
||||||
integrity sha512-D5OKWKvDhyVWWn2x5Y9b+37NUllks34q1dCDhk/vYcso9fmhs+Tl3KR/gE4v5UNj2UA35cnX4KdVVGkG1deKqw==
|
|
||||||
|
|
||||||
string-width@^4.1.0, string-width@^4.2.0:
|
string-width@^4.1.0, string-width@^4.2.0:
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
|
||||||
@@ -5667,6 +5663,11 @@ utils-merge@1.0.1:
|
|||||||
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
|
||||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||||
|
|
||||||
|
uuid@7.0.2:
|
||||||
|
version "7.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.2.tgz#7ff5c203467e91f5e0d85cfcbaaf7d2ebbca9be6"
|
||||||
|
integrity sha512-vy9V/+pKG+5ZTYKf+VcphF5Oc6EFiu3W8Nv3P3zIh0EqVI80ZxOzuPfe9EHjkFNvf8+xuTHVeei4Drydlx4zjw==
|
||||||
|
|
||||||
uuid@^3.3.2, uuid@^3.4.0:
|
uuid@^3.3.2, uuid@^3.4.0:
|
||||||
version "3.4.0"
|
version "3.4.0"
|
||||||
resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"
|
resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user