Edits + better layout

This commit is contained in:
2021-03-18 17:41:10 +01:00
parent b16d70314b
commit 3afe7cb5d4
20 changed files with 1066 additions and 241 deletions

View File

@@ -14,6 +14,7 @@
android:name=".MainActivity" android:name=".MainActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:screenOrientation="portrait"
android:launchMode="singleTask" android:launchMode="singleTask"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustPan">
<intent-filter> <intent-filter>

View File

@@ -11,6 +11,7 @@ import AddItemModal from './modals/AddItemModal';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { toggleVisibility } from '../../redux/slices/groceryList/toggleSlice'; import { toggleVisibility } from '../../redux/slices/groceryList/toggleSlice';
import { removeCheckedItems } from '../../redux/slices/groceryList/itemsSlice'; import { removeCheckedItems } from '../../redux/slices/groceryList/itemsSlice';
import { pushItems } from '../../redux/slices/groceryList/storageSlice';
export const AddItemButton = () => { export const AddItemButton = () => {
@@ -27,24 +28,32 @@ export const AddItemButton = () => {
} }
export const AddNewRecipeButton = () => { export const AddNewRecipeButton = () => {
return ( return (
<TouchableOpacity style={[styles.buttonContainer, styles.AddNewProduct, { backgroundColor: COLORS.primaryVar2 }]} <TouchableOpacity style={[styles.buttonContainer, styles.AddNewRecipeButton, { backgroundColor: COLORS.primaryVar2 }]}
onPress={() => alert('hello')}> onPress={() => alert('hello')}>
<MaterialCommunityIcons name="file-document-edit-outline" color={COLORS.dp00} size={40} /> <MaterialCommunityIcons name="file-document-edit-outline" color={COLORS.dp00} size={40} />
</TouchableOpacity> </TouchableOpacity>
); );
} }
export const PushItemsToStorageButton = () => { export const PushItemsToStorageButton = () => {
const dispatch = useDispatch();
const items = useSelector(state => state.items)
const products = useSelector(state => state.products)
const checkedItems = items.filter((item)=>item.checked && products.find((product)=>product.id === item.productId) !== undefined)
const dispatchAll = () =>{
dispatch(pushItems(checkedItems))
dispatch(removeCheckedItems())
}
return ( return (
<TouchableOpacity style={[styles.buttonContainer, styles.AddNewRecipe, { backgroundColor: COLORS.actionSend }]} <TouchableOpacity style={[styles.buttonContainer, styles.PushItemsToStorageButton, { backgroundColor: COLORS.actionSend }]}
onPress={() => alert('lol')}> onPress={() => dispatchAll()}>
<MaterialCommunityIcons name="cloud-download-outline" color={COLORS.dp00} size={40} /> <MaterialCommunityIcons name="cart-arrow-up" color={COLORS.dp00} size={40} />
</TouchableOpacity> </TouchableOpacity>
); );
} }
export const RemoveItemsButton = () => { export const RemoveItemsButton = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
return ( return (
<TouchableOpacity style={[styles.buttonContainer, styles.downloadList, { backgroundColor: COLORS.actionCancel }]} <TouchableOpacity style={[styles.buttonContainer, styles.RemoveItemsButton, { backgroundColor: COLORS.actionCancel }]}
onPress={() => dispatch(removeCheckedItems())}> onPress={() => dispatch(removeCheckedItems())}>
<MaterialCommunityIcons name="delete-forever-outline" color={COLORS.dp00} size={40} /> <MaterialCommunityIcons name="delete-forever-outline" color={COLORS.dp00} size={40} />
</TouchableOpacity> </TouchableOpacity>
@@ -63,14 +72,14 @@ const styles = StyleSheet.create({
addItem: { addItem: {
backgroundColor: COLORS.primary, backgroundColor: COLORS.primary,
}, },
AddNewProduct: { AddNewRecipeButton: {
backgroundColor: COLORS.primary, backgroundColor: COLORS.primary,
}, },
AddNewRecipe: { PushItemsToStorageButton: {
backgroundColor: COLORS.primary, backgroundColor: COLORS.primary,
}, },
downloadList: { RemoveItemsButton: {
backgroundColor: COLORS.primary, backgroundColor: COLORS.primary,
}, },

View File

@@ -1,42 +1,31 @@
import React, { useRef, useEffect, useState } from 'react'; import React, { useRef, useEffect, useState, Suspense } from 'react';
import { StyleSheet, Text, TouchableOpacity, View, Animated } from 'react-native'; import { StyleSheet, Text, TouchableOpacity, View, Animated } from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
//themes //themes
import { COLORS } from '../../themes/Colors'; import { COLORS } from '../../themes/Colors';
//components
import EditItemModal from './modals/EditItemModal'
//redux //redux
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { checkToggle } from '../.././redux/slices/groceryList/itemsSlice'; import { checkToggle, modalToggle } from '../.././redux/slices/groceryList/itemsSlice';
const ListedItem = (props) => {
export const ListedItem = (props) => {
//redux //redux
const dispatch = useDispatch() const dispatch = useDispatch()
const products = useSelector(state => state.products) const item = useSelector(state => state.items.find((item) => item.productId === props.id))
const items = useSelector(state => state.items) let product = useSelector(state => state.products.find((product) => product.id === item.productId))
const tags = useSelector(state => state.tags) if (!product) {
let product;
const item = items.find((item) => item.productId === props.id);
if (products.find((product) => product.id === item.productId)) {
product = products.find((product) => product.id === item.productId);
}
else {
product = { product = {
productName: props.id, productName: props.id,
tag:'no-tag', tag: 'uncathegorized',
} }
} }
const tag = useSelector(state => state.tags.find((tag) => tag.tagName === product.tag))
const dispatchAnim = () => { const dispatchAnim = () => {
dispatch(checkToggle(item.productId)); dispatch(checkToggle(item.productId));
} }
const tag = tags.find((tag)=>tag.tagName === product.tag);
const scaleAnimValue = useRef(new Animated.Value(0)).current; // animation start value const scaleAnimValue = useRef(new Animated.Value(0)).current; // animation start value
const colorAnimValue = useRef(new Animated.Value(0)).current; // animation start value const colorAnimValue = useRef(new Animated.Value(0)).current; // animation start value
const checkAnimValue = useRef(new Animated.Value(0)).current; // animation start value const checkAnimValue = useRef(new Animated.Value(0)).current; // animation start value
@@ -106,33 +95,9 @@ export const ListedItem = (props) => {
return ( return (
<View style={{ flex: 1, paddingLeft: 8, paddingRight: 8, flexDirection: 'row', alignItems: 'flex-start', justifyContent: 'space-between' }}> <Suspense fallback={<Text>Loading</Text>}>
<Animated.View style={[styles.listedItemStyle, {backgroundColor: tag.color + '33'},item.checked ? styles.listedItemChecked : {}, { <View style={{ flex: 1, paddingLeft: 8, paddingRight: 8, flexDirection: 'row', alignItems: 'flex-start', justifyContent: 'space-between' }}>
transform: [{ <Animated.View style={[styles.listedItemStyle, { backgroundColor: tag.color + '33' }, item.checked ? styles.listedItemChecked : {}, {
scaleX: scaleAnimValue
},
{
scaleY: scaleAnimValue
}
]
}]} >
<TouchableOpacity style={{}}
onPress={() => console.log(item.checked)}
onLongPress={() => dispatchAnim()}>
<Text style={styles.textItem}>{product.productName}</Text>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'flex-end' }}>
<Text style={styles.textAmount}>Amount: {item.amount}</Text>
<Text style={styles.textPerson}>For: {item.person}</Text>
</View>
{item.details ? <Text style={styles.textDetails}>Details: {item.details}</Text> : <View />}
</TouchableOpacity>
</Animated.View>
<TouchableOpacity style={[styles.checkButton, { flex: 1 }]} onPress={() => dispatchAnim()}>
<Animated.View style={[styles.checkmark, item.checked ? {
borderWidth: 0,
} : {}, {
backgroundColor: interpolateColor,
transform: [{ transform: [{
scaleX: scaleAnimValue scaleX: scaleAnimValue
}, },
@@ -140,68 +105,119 @@ export const ListedItem = (props) => {
scaleY: scaleAnimValue scaleY: scaleAnimValue
} }
] ]
},]} > }]} >
<Animated.View style={[{ <TouchableOpacity style={{ flex: 1, }}
onPress={() => dispatch(modalToggle(props.id))}
onLongPress={() => dispatchAnim()}>
<View style={styles.nameTag}>
{item.person ? <Text style={styles.textPerson}>{item.person}</Text> : <View />}
</View>
<Text style={styles.textItem}>{product.productName}</Text>
<Text style={styles.textAmount}>{item.amount}</Text>
{item.details ? <Text style={styles.textDetails}>{item.details}</Text> : <View />}
</TouchableOpacity>
</Animated.View>
<TouchableOpacity style={[styles.checkButton, { flex: 1 }]} onPress={() => dispatchAnim()}>
<Animated.View style={[styles.checkmark, item.checked ? {
borderWidth: 0,
} : {}, {
backgroundColor: interpolateColor,
transform: [{ transform: [{
scaleX: checkAnimValue scaleX: scaleAnimValue
}, },
{ {
scaleY: checkAnimValue scaleY: scaleAnimValue
} }
] ]
}]}> },]} >
<MaterialCommunityIcons name="check" color={COLORS.dp00} size={25} /> <Animated.View style={[{
</Animated.View> transform: [{
scaleX: checkAnimValue
},
{
scaleY: checkAnimValue
}
]
}]}>
<MaterialCommunityIcons name="check" color={COLORS.dp00} size={25} />
</Animated.View>
</Animated.View> </Animated.View>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</Suspense>
); );
} }
export default React.memo(ListedItem);
const height = 30; const height = 30;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
listedItemStyle: { listedItemStyle: {
width: '90%', width: '90%',
minHeight: height + 30,
marginBottom: height / 7, marginBottom: height / 7,
borderRadius: height / 2, borderRadius: height / 2,
padding: 2,
paddingBottom: 3,
backgroundColor: COLORS.dp03, backgroundColor: COLORS.dp03,
}, },
listedItemChecked: { listedItemChecked: {
backgroundColor: '#253f34', backgroundColor: '#253f34',
}, },
textItem: { textItem: {
width: '80%',
flex: 1, flex: 1,
left: 8, marginLeft: 10,
marginTop: 5,
fontFamily: 'Roboto', fontFamily: 'Roboto',
fontSize: height * 0.65, fontSize: height * 0.65,
color: COLORS.textW0 + 'cc', color: COLORS.textW0 + 'cc',
}, },
textAmount: {
flex: 1,
left: 30,
fontFamily: 'Roboto',
fontSize: height * 0.5,
color: COLORS.textW1,
},
textPerson: {
flex: 1,
left: 25,
fontFamily: 'Roboto',
fontSize: height * 0.5,
color: COLORS.textW1,
},
textDetails: { textDetails: {
flex: 1, flex: 1,
left: 10,
marginBottom: 3, marginBottom: 3,
left: 8,
fontFamily: 'Roboto', fontFamily: 'Roboto',
fontSize: height * 0.5, fontSize: height * 0.5,
color: COLORS.primary + 'aa', color: COLORS.primary + 'aa',
}, },
nameTag: {
backgroundColor: '#000000' + '33',
minWidth: '30%',
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
right: 0,
bottom: 0,
borderTopLeftRadius: 10,
borderBottomRightRadius: height / 2,
},
textPerson: {
fontFamily: 'Roboto',
fontSize: height * 0.45,
color: COLORS.textW0 + 'cc',
padding: 5,
maxHeight: 40,
},
textAmount: {
position: 'absolute',
backgroundColor: '#000' + '3',
right: 5,
top: 5,
minWidth: 50,
borderTopRightRadius: height / 2,
borderRadius: height / 3,
paddingLeft: 5,
paddingRight: 10,
fontFamily: 'Roboto',
fontSize: height * 0.55,
color: COLORS.textW0 + 'bb',
},
checkmark: { checkmark: {
height: 30, height: 30,
width: 30, width: 30,

View File

@@ -1,21 +1,25 @@
import React, { useRef, useEffect } from 'react'; import React, { useRef, useEffect } from 'react';
import { Image, StyleSheet, Text, TouchableOpacity, View, Animated } from 'react-native'; import { Image, StyleSheet, Text, TouchableOpacity, View, Animated } from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'; import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
//components
import EditProductModal from './modals/EditProductModal'
//themes //themes
import { COLORS } from '../../themes/Colors'; import { COLORS } from '../../themes/Colors';
//redux //redux
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { checkToggle } from '../.././redux/slices/groceryList/productsSlice'; import { checkToggle, modalToggle } from '../.././redux/slices/groceryList/productsSlice';
export const ListedProduct = (props) => { const ListedProduct = (props) => {
const products = useSelector(state => state.products) const product = useSelector(state => state.products.find((product) => product.id === props.id));
const product = products.find((product) => product.id === props.id);
const tags = useSelector(state => state.tags) const tags = useSelector(state => state.tags)
const tag = tags.find((tag) => tag.tagName === product.tag); const tag = tags.find((tag) => tag.tagName === product.tag);
//redux
const dispatch = useDispatch()
const scaleAnimValue = useRef(new Animated.Value(0)).current; // animation start value const scaleAnimValue = useRef(new Animated.Value(0)).current; // animation start value
const colorAnimValue = useRef(new Animated.Value(0)).current; // animation start value const colorAnimValue = useRef(new Animated.Value(0)).current; // animation start value
@@ -78,12 +82,6 @@ export const ListedProduct = (props) => {
]).start() ]).start()
}, [product.checked]) }, [product.checked])
//redux
const dispatch = useDispatch()
const dispatchAnim = () => {
dispatch(checkToggle(product.id));
}
const interpolateColor = colorAnimValue.interpolate({ const interpolateColor = colorAnimValue.interpolate({
inputRange: [0, 1], inputRange: [0, 1],
outputRange: ['rgb(22, 26, 30)', 'rgb(62, 131, 98)'] outputRange: ['rgb(22, 26, 30)', 'rgb(62, 131, 98)']
@@ -99,24 +97,24 @@ export const ListedProduct = (props) => {
} }
] ]
}]} > }]} >
<TouchableOpacity style={[styles.listedItemButton]} <TouchableOpacity style={[styles.listedItemButton]}
onPress={() => alert('Not ready yet!!')} onPress={() => dispatch(modalToggle(product.id))}
onLongPress={() => dispatchAnim()}> onLongPress={() => dispatch(checkToggle(product.id))}>
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<Text style={styles.textItem}>{product.productName}</Text> <Text style={styles.textItem}>{product.productName}</Text>
<View style={{ flexDirection: 'row' }}> <Text style={[styles.textTags, { color: tag.color, }]}>{tag.tagName}</Text>
<Text style={[styles.textTags]}>Tag: </Text> {product.price ? <Text style={[styles.textPrice, { color: COLORS.price, }]}>{product.price}</Text> : <View />}
<Text style={[styles.textTags, { color: tag.color, }]}>{tag.tagName}</Text>
</View>
<View style={{ flexDirection: 'row' }}>
{product.price ? <Text style={styles.textPrice}>Price: </Text> : <View />}
{product.price ? <Text style={[styles.textPrice, { color: '#C6A337', }]}>{product.price}</Text> : <View />}
</View>
</View> </View>
<Image source={{ uri: product.image }} style={styles.image} /> <Image source={{ uri: product.image }} style={styles.image} />
</TouchableOpacity> </TouchableOpacity>
</Animated.View> </Animated.View>
<TouchableOpacity style={[styles.checkButton, { flex: 1 }]} onPress={() => dispatchAnim()}>
<TouchableOpacity style={[styles.checkButton, { flex: 1 }]} onPress={() => dispatch(checkToggle(product.id))}>
<Animated.View style={[styles.checkmark, product.checked ? { <Animated.View style={[styles.checkmark, product.checked ? {
borderWidth: 0, borderWidth: 0,
} : {}, { } : {}, {
@@ -146,6 +144,7 @@ export const ListedProduct = (props) => {
</View> </View>
); );
} }
export default React.memo(ListedProduct);
const height = 30; const height = 30;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@@ -192,7 +191,7 @@ const styles = StyleSheet.create({
position: 'absolute', position: 'absolute',
right: 0, right: 0,
top: 0, top: 0,
bottom:0, bottom: 0,
width: 80, width: 80,
borderRadius: 15, borderRadius: 15,
opacity: 0.8, opacity: 0.8,

View File

@@ -0,0 +1,112 @@
import React from 'react';
import { StyleSheet, Text, TouchableOpacity, View, Animated } from 'react-native';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
//themes
import { COLORS } from '../../themes/Colors';
//redux
import { useDispatch } from 'react-redux';
import { changeAmount, removeItem } from '../../redux/slices/groceryList/storageSlice';
const Hr = (props) => {
return (
<View style={{ backgroundColor: props.color, width: 1.0, height: props.height, borderRadius: 1, alignSelf: 'flex-end' }} />
)
}
let ListedStorageItem = ({product, storageItem, tag}) => {
const dispatch = useDispatch();
return (
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>
{/* delete button */}
{!storageItem.amount ? <TouchableOpacity style={[styles.deleteItem, {}]} onPress={() => dispatch(removeItem(storageItem.id))}>
<MaterialCommunityIcons name="window-close" color={COLORS.actionCancel + 'aa'} size={22} />
</TouchableOpacity> : <View style={{ width: 25 }} />}
<View style={[styles.listedItem, { backgroundColor: tag.color + '20' }]}>
<Text style={styles.textItem}>{product.productName}</Text>
<Text style={styles.textAmount}>{storageItem.amount}</Text>
<View style={styles.amountButtons}>
{/* button increment amount */}
<TouchableOpacity style={styles.amountIncrement} onPress={() => dispatch(changeAmount(storageItem.id, 1))}>
<MaterialCommunityIcons name="plus" color={COLORS.textW0 + '77'} size={20} />
</TouchableOpacity>
<Hr color={COLORS.textW0 + '11'} height={'100%'} />
{/* button decrement amount */}
<TouchableOpacity style={styles.amountDecrement} onPress={() => storageItem.amount > 0 ? dispatch(changeAmount(product.id, -1)) : 0}
onLongPress={() => dispatch(changeAmount(product.id, -storageItem.amount))}>
<MaterialCommunityIcons name="minus" color={COLORS.textW0 + '77'} size={20} />
</TouchableOpacity>
</View>
</View>
</View>)
}
ListedStorageItem = React.memo(ListedStorageItem);
export default ListedStorageItem;
const height = 30;
const styles = StyleSheet.create({
listedItem: {
borderTopLeftRadius: 15,
borderBottomLeftRadius: 15,
width: '90%',
paddingLeft: 8,
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'space-between',
marginTop: 2,
marginBottom: 2,
},
textItem: {
flex: 1,
left: 8,
marginTop: 2,
fontFamily: 'Roboto',
fontSize: height * 0.65,
color: COLORS.textW0 + 'bb',
},
textAmount: {
width: '10%',
backgroundColor: '#000000' + '40',
paddingLeft: 3,
borderRadius: 6,
marginTop: 3,
marginBottom: 3,
marginRight: 3,
fontFamily: 'Roboto',
fontSize: height * 0.6,
color: COLORS.textW1,
},
amountButtons: {
flexDirection: 'row',
marginTop: 3,
marginBottom: 3,
borderTopLeftRadius: 10,
borderBottomLeftRadius: 10,
height: 25,
width: 70,
alignItems: 'center',
justifyContent: 'space-evenly',
backgroundColor: '#ffffff' + '10',
},
amountIncrement: {
},
amountDecrement: {
},
deleteItem: {
height: 27,
width: 27,
backgroundColor: '#ffffff' + '10',
borderRadius: 12.5,
marginLeft: 5,
justifyContent: 'center',
alignItems: 'center',
}
})

View File

@@ -0,0 +1,114 @@
import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity, View, Text } from 'react-native';
//components
import ListedStorageItem from './ListedStorageItem';
//themes
import { COLORS } from '../../themes/Colors';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
//redux
import { useSelector, useDispatch } from 'react-redux';
export default StorageList = (props) => {
const products = useSelector(state => state.products)
const storageFiltered = useSelector(state => state.storage.filter((item) => products.find((product) => product.id === item.id).tag === props.tag))
const tag = useSelector(state => state.tags.find((tag) => tag.tagName === props.tag))
const [visible, setVisible] = useState(false);
if (storageFiltered.length !== 0) {
return(
<View style={styles.container}>
<TouchableOpacity style={[styles.categoryButton]}
onPress={() => setVisible(!visible)}>
{visible ?
<MaterialCommunityIcons name="menu-down" color={COLORS.textW3} size={28} />
: <MaterialCommunityIcons name="menu-right" color={COLORS.textW3} size={28} />
}
<Text style={[styles.textItem, { color: tag.color + 'aa' }]}>{props.tag}</Text>
</TouchableOpacity>
<View style={[styles.list]}>
{visible ? storageFiltered.map((storageItem, index) => {
const product = products.find((product) => product.id === storageItem.id)
return <ListedStorageItem key={index} product = {product} storageItem = {storageItem} tag = {tag}/>
}) : <View />}
</View>
</View>
)
}
else {
return <View />
}
}
const height = 30;
const styles = StyleSheet.create({
container: {
marginBottom: 5,
},
list: {
alignItems: 'flex-end',
},
categoryButton: {
flex: 1,
paddingLeft: 0,
paddingRight: 8,
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'space-between',
},
listedItem: {
borderTopLeftRadius: 15,
borderBottomLeftRadius: 15,
width: '90%',
paddingLeft: 8,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginTop: 2,
marginBottom: 2,
},
textItem: {
flex: 1,
left: 8,
fontFamily: 'Roboto',
fontSize: height * 0.65,
color: COLORS.textW0 + 'bb',
},
textAmount: {
width: '10%',
backgroundColor: '#ffffff' + '10',
paddingLeft: 3,
borderRadius: 6,
marginRight: 3,
fontFamily: 'Roboto',
fontSize: height * 0.6,
color: COLORS.textW1,
},
amountButtons: {
flexDirection: 'row',
marginTop: 3,
marginBottom: 3,
borderTopLeftRadius: 10,
borderBottomLeftRadius: 10,
height: 25,
width: 70,
alignItems: 'center',
alignSelf: 'flex-start',
justifyContent: 'space-evenly',
backgroundColor: '#ffffff' + '10',
},
amountIncrement: {
},
amountDecrement: {
},
deleteItem: {
height: 27,
width: 27,
backgroundColor: '#ffffff' + '10',
borderRadius: 12.5,
marginLeft: 5,
justifyContent: 'center',
alignItems: 'center',
}
})

View File

@@ -1,20 +1,21 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity, View, Text, TextInput, Image, ScrollView, TouchableOpacityComponent } from 'react-native'; import { StyleSheet, TouchableOpacity, View, Text, TextInput, ScrollView , Image } from 'react-native';
import Modal from 'react-native-modal'; import Modal from 'react-native-modal';
//themes //themes
import { COLORS } from '../../../themes/Colors'; import { COLORS } from '../../../themes/Colors';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
//redux //redux
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { itemAdded, amountUp } from '../../../redux/slices/groceryList/itemsSlice'; import { itemAdded, amountUp } from '../../../redux/slices/groceryList/itemsSlice';
import { toggleVisibility } from '../../../redux/slices/groceryList/toggleSlice'; import { toggleVisibility } from '../../../redux/slices/groceryList/toggleSlice';
export default AddItemModal = () => { export default AddItemModal = () => {
//redux //redux
const modalVisible = useSelector(state => state.toggle.addItemModalVisible); const modalVisible = useSelector(state => state.toggle.addItemModalVisible);
const products = useSelector(state => state.products); const products = useSelector(state => state.products);
const items = useSelector(state => state.items);
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -23,10 +24,9 @@ export default AddItemModal = () => {
newProductsList.push(products[i].productName); newProductsList.push(products[i].productName);
} }
const [item, setItem] = useState(''); const [item, setItem] = useState('');
const [focussed, setFocussed] = useState(false); const [focussed, setFocussed] = useState(false);
const [amount, setAmount] = useState(''); const [amount, setAmount] = useState();
const [person, setPerson] = useState(''); const [person, setPerson] = useState('');
const [details, setDetails] = useState(''); const [details, setDetails] = useState('');
@@ -47,39 +47,29 @@ export default AddItemModal = () => {
setItem(text); setItem(text);
}; };
const toggleModal = () => { const toggleModal = () => {
if (modalVisible && item !== '' && amount !== '') { if (modalVisible && item !== '' && 0 < amount) {
if (products.find((product) => product.productName === item)) { //find if item is in products , else create unregistered item if (products.find((product) => product.productName === item)) { //find if item is in products , else create unregistered item
const productId = products.find((product) => product.productName === item).id; const productId = products.find((product) => product.productName === item).id;
if(!items.find((itemObj) => itemObj.productId === productId)){ //check if amount up needed dispatch(itemAdded(productId, amount, person, details));
dispatch(itemAdded(productId, amount, person, details));
}
else{
}
}
else if (!items.find((itemObj) => itemObj.productId === item)) //unregistered item check for amount up
{
dispatch(itemAdded(item, amount, person, details));
}
else{
dispatch(amountUp(item,amount));
}
setItem('');
setAmount();
setPerson('');
setDetails('');
setProductsList(products.map((product) => product.productName));
} }
else { //unregistered
dispatch(itemAdded(item, amount, person, details));
}
setItem('');
setAmount();
setPerson('');
setDetails('');
setFocussed(false)
setProductsList(products.map((product) => product.productName));
}
else { else {
alert('You should give both a product and an amount'); alert('You should give both a product and an amount');
} }
dispatch(toggleVisibility('addItemModalVisible')); dispatch(toggleVisibility('addItemModalVisible'));
}; };
const closeModal = () => { const closeModal = () => {
setItem(''); setFocussed(false)
setAmount();
setPerson('');
setDetails('');
dispatch(toggleVisibility('addItemModalVisible')); dispatch(toggleVisibility('addItemModalVisible'));
setProductsList(products.map((product) => product.productName)); setProductsList(products.map((product) => product.productName));
}; };
@@ -96,19 +86,22 @@ export default AddItemModal = () => {
backgroundColor: COLORS.dp01, backgroundColor: COLORS.dp01,
}}> }}>
<Image source={{ uri: products.find((product) => product.productName === item) ? products.find((product) => product.productName === item).image : 'https://icons-for-free.com/iconfiles/png/512/linecon+products+round+icon-1320165923260225670.png'}}
style={styles.image} />
<Text style={styles.modalHeaderText}>Add item</Text> <Text style={styles.modalHeaderText}>Add item</Text>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start' }}> <View style={styles.inputRow}>
<MaterialCommunityIcons name="food" color={COLORS.textW0 + '55'} size={30} />
<TextInput <TextInput
placeholder={'Product name'} placeholder={'Product name'}
placeholderTextColor={'grey'} placeholderTextColor={'grey'}
style={styles.textInput} style={styles.textInput}
offFocus={() => setProductsList([])}
onChangeText={text => filterProductsList(text)} onChangeText={text => filterProductsList(text)}
value={item} value={item}
onFocus={() => setFocussed(true)} onFocus={() => setFocussed(true)}
onEndEditing={() => setFocussed(false)} onEndEditing={() => setFocussed(false)}
/> />
{item ? <MaterialCommunityIcons style = {{right:15}}name="check" color={COLORS.primary} size={25} /> : <MaterialCommunityIcons style = {{right:10, bottom: 10,}}name="asterisk" color={COLORS.actionCancel + 'aa'} size={12} />}
<ScrollView style={styles.dropdown} keyboardShouldPersistTaps={'always'}> <ScrollView style={styles.dropdown} keyboardShouldPersistTaps={'always'}>
{focussed ? productsList.map((product, index) => {focussed ? productsList.map((product, index) =>
@@ -119,7 +112,8 @@ export default AddItemModal = () => {
</ScrollView> </ScrollView>
</View> </View>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start' }}> <View style={styles.inputRow}>
<MaterialCommunityIcons name="label-percent-outline" color={COLORS.textW0 + '55'} size={30} />
<TextInput <TextInput
placeholder={'Amount'} placeholder={'Amount'}
placeholderTextColor={'grey'} placeholderTextColor={'grey'}
@@ -128,8 +122,10 @@ export default AddItemModal = () => {
value={amount} value={amount}
keyboardType={'number-pad'} keyboardType={'number-pad'}
/> />
{amount ? <MaterialCommunityIcons style = {{right:15}}name="check" color={COLORS.primary} size={25} /> : <MaterialCommunityIcons style = {{right:10, bottom: 10,}}name="asterisk" color={COLORS.actionCancel + 'aa'} size={12} />}
</View> </View>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start' }}> <View style={styles.inputRow}>
<MaterialCommunityIcons name="account-supervisor-circle" color={COLORS.textW0 + '55'} size={30} />
<TextInput <TextInput
placeholder={'For'} placeholder={'For'}
placeholderTextColor={'grey'} placeholderTextColor={'grey'}
@@ -137,8 +133,10 @@ export default AddItemModal = () => {
onChangeText={text => setPerson(text)} onChangeText={text => setPerson(text)}
value={person} value={person}
/> />
{person ? <MaterialCommunityIcons style = {{right:15}}name="check" color={COLORS.primary} size={25} /> : <View/>}
</View> </View>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-start' }}> <View style={styles.inputRow}>
<MaterialCommunityIcons name="folder-information-outline" color={COLORS.textW0 + '55'} size={30} />
<TextInput <TextInput
placeholder={'Additional details'} placeholder={'Additional details'}
placeholderTextColor={'grey'} placeholderTextColor={'grey'}
@@ -146,9 +144,10 @@ export default AddItemModal = () => {
onChangeText={text => setDetails(text)} onChangeText={text => setDetails(text)}
value={details} value={details}
/> />
{details ? <MaterialCommunityIcons style = {{right:15}}name="check" color={COLORS.primary} size={25} /> : <View/>}
</View> </View>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-evenly', marginTop: 20, marginBottom: 10, }}> <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-evenly', marginTop: 20, marginBottom: 10, }}>
<TouchableOpacity title="Hide modal" style={styles.modalCloseButton} onPress={closeModal} > <TouchableOpacity onPress={closeModal} >
<Text style={styles.modalCloseButtonText}>Cancel</Text> <Text style={styles.modalCloseButtonText}>Cancel</Text>
</TouchableOpacity> </TouchableOpacity>
<View style={{ <View style={{
@@ -159,7 +158,7 @@ export default AddItemModal = () => {
height: '80%', height: '80%',
}} /> }} />
<TouchableOpacity title="Hide modal" style={styles.modalAddButton} onPress={toggleModal} > <TouchableOpacity onPress={toggleModal} >
<Text style={styles.modalAddButtonText}>Add item</Text> <Text style={styles.modalAddButtonText}>Add item</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@@ -173,6 +172,18 @@ const styles = StyleSheet.create({
modalAddItem: { modalAddItem: {
margin: 0, margin: 0,
}, },
image: {
position: 'absolute',
right:0,
top: 0,
bottom: 0,
opacity: 0.04,
width: 200,
borderTopLeftRadius: 150,
borderBottomLeftRadius: 150,
},
modalHeaderText: { modalHeaderText: {
opacity: 0.9, opacity: 0.9,
fontSize: 23, fontSize: 23,
@@ -187,20 +198,20 @@ const styles = StyleSheet.create({
}, },
modalAddButtonText: { modalAddButtonText: {
opacity: 0.8, opacity: 0.8,
fontSize: 16, fontSize: 18,
color: COLORS.primary, color: COLORS.primary,
fontWeight: 'bold', fontWeight: 'bold',
}, },
modalCloseButtonText: { modalCloseButtonText: {
opacity: 0.8, opacity: 0.8,
fontSize: 16, fontSize: 18,
color: COLORS.primary, color: COLORS.primary,
fontWeight: 'bold', fontWeight: 'bold',
}, },
textInput: { textInput: {
height: 25, height: 25,
width: '80%', width: '70%',
left: 10, left: 10,
borderBottomColor: COLORS.primary + 'aa', borderBottomColor: COLORS.primary + 'aa',
@@ -209,12 +220,18 @@ const styles = StyleSheet.create({
marginTop: 10, marginTop: 10,
padding: 0, padding: 0,
paddingLeft: 5, paddingLeft: 5,
paddingRight: 5, paddingRight: 25,
color: COLORS.textW0, color: COLORS.textW0,
fontSize: 18 fontSize: 18
}, },
inputRow: {
flexDirection: 'row',
alignItems: 'flex-end',
justifyContent: 'flex-start'
},
dropdown: { dropdown: {
zIndex: 1, zIndex: 1,
@@ -232,8 +249,8 @@ const styles = StyleSheet.create({
zIndex: 1, zIndex: 1,
width: 150, width: 150,
minHeight: 20, minHeight: 20,
borderBottomWidth: 1.2, borderBottomWidth: 1.0,
borderColor: '#666' + 'e', borderColor: '#666' + '5',
}, },
dropdownText: { dropdownText: {

View File

@@ -1,10 +1,13 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity, View, Text, TextInput, Image, ScrollView } from 'react-native'; import { StyleSheet, TouchableOpacity, View, Text, TextInput, Image, ScrollView } from 'react-native';
//dependencies
import Modal from 'react-native-modal'; import Modal from 'react-native-modal';
//themes //themes
import { COLORS } from '../../../themes/Colors'; import { COLORS } from '../../../themes/Colors';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
//redux //redux
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
@@ -23,14 +26,10 @@ export default AddProductModal = () => {
//tags //tags
const tags = useSelector(state => state.tags) const tags = useSelector(state => state.tags)
let newTagsList = []; const [tagsList, setTagsList] = useState(tags.map((listTag) => listTag.tagName));
for (let i = 0; i < tags.length; i++) {
newTagsList.push(tags[i].tagName);
}
const [tagsList, setTagsList] = useState(newTagsList);
const [focussed, setFocussed] = useState(false); const [focussed, setFocussed] = useState(false);
const filterTagsList = (text) => { const filterTagsList = (text) => {
let regex = new RegExp(text); let regex = new RegExp(text);
@@ -61,6 +60,7 @@ export default AddProductModal = () => {
setProduct(''); setProduct('');
setTag(''); setTag('');
setPrice(); setPrice();
setFocussed(false)
setImage('https://icons-for-free.com/iconfiles/png/512/linecon+products+round+icon-1320165923260225670.png'); setImage('https://icons-for-free.com/iconfiles/png/512/linecon+products+round+icon-1320165923260225670.png');
}; };
@@ -70,7 +70,9 @@ export default AddProductModal = () => {
setTag(); setTag();
setPrice(); setPrice();
setImage('https://icons-for-free.com/iconfiles/png/512/linecon+products+round+icon-1320165923260225670.png'); setImage('https://icons-for-free.com/iconfiles/png/512/linecon+products+round+icon-1320165923260225670.png');
setFocussed(false)
dispatch(toggleVisibility('addProductModalVisible')); dispatch(toggleVisibility('addProductModalVisible'));
}; };
return ( return (
@@ -83,7 +85,6 @@ export default AddProductModal = () => {
borderRadius: 10, borderRadius: 10,
padding: 5, padding: 5,
backgroundColor: COLORS.dp01, backgroundColor: COLORS.dp01,
}}> }}>
<Text style={styles.modalHeaderText}>Add product</Text> <Text style={styles.modalHeaderText}>Add product</Text>
@@ -108,7 +109,6 @@ export default AddProductModal = () => {
placeholderTextColor={'grey'} placeholderTextColor={'grey'}
style={[styles.textInput]} style={[styles.textInput]}
value={tag} value={tag}
offFocus={() => setTagsList([])}
onChangeText={text => filterTagsList(text)} onChangeText={text => filterTagsList(text)}
onFocus={() => setFocussed(true)} onFocus={() => setFocussed(true)}
onEndEditing={() => setFocussed(false)} onEndEditing={() => setFocussed(false)}
@@ -136,7 +136,7 @@ export default AddProductModal = () => {
placeholder={'Image url'} placeholder={'Image url'}
placeholderTextColor={'grey'} placeholderTextColor={'grey'}
style={styles.textInput} style={styles.textInput}
onChangeText={text => { if (text !== '') { return setImage(text) } }} onChangeText={text => { if (text !== '') { return setImage(`https://www.googleapis.com/customsearch/v1?key=${product}&cx=017576662512468239146:omuauf_lfve&q=lectures`) } }}
/> />
</View> </View>
</View> </View>
@@ -168,7 +168,7 @@ export default AddProductModal = () => {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
//modals //modals
modalAddItem: { modalAddProduct: {
margin: 0, margin: 0,
}, },
modalHeaderText: { modalHeaderText: {
@@ -236,7 +236,9 @@ const styles = StyleSheet.create({
zIndex: 1, zIndex: 1,
width: 150, width: 150,
minHeight: 20 minHeight: 20,
borderBottomWidth: 1.0,
borderColor: '#666' + '5',
}, },
dropdownText: { dropdownText: {

View File

@@ -0,0 +1,183 @@
import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity, View, Text, TextInput, Image, FlatList, ScrollView } from 'react-native';
//dependencies
import Modal from 'react-native-modal';
//themes
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { COLORS } from '../../../themes/Colors';
//redux
import { useDispatch , useSelector} from 'react-redux';
import { modalToggle, editItem } from '../../../redux/slices/groceryList/itemsSlice';
export default EditItemModal = (props) => {
const item = useSelector(state => state.items.find((item) => item.modalVisible === true));
let product = useSelector(state => state.products.find((product) => product.id === item.productId))
if (!product) {
product = {
productName: props.id,
tag: 'uncathegorized',
}
}
const tags = useSelector(state => state.tags)
let tag = tags.find(tag => tag.tagName === product.tag)
const dispatch = useDispatch();
const [amount, setAmount] = useState(item.amount.toString());
const [person, setPerson] = useState(item.person);
const [details, setDetails] = useState(item.details);
const saveModal = () => {
dispatch(modalToggle(item.productId))
dispatch(editItem(item.productId, amount, person, details));
}
return (
<Modal isVisible={item.modalVisible} animationIn={'slideInUp'} animationOut={'slideOutDown'}
onBackdropPress={saveModal}
onBackButtonPress={()=>dispatch(modalToggle(item.productId))}
swipeDirection={'down'}
onSwipeComplete={saveModal}
useNativeDriverForBackdrop
style={styles.modalEditItem} >
<View style={styles.container}>
<View style={{ width: '100%', flexDirection: 'row', alignItems: 'flex-start', justifyContent: 'flex-start', backgroundColor: tag.color + '22', borderRadius: 10, }}>
{product.price ? <Text style={[styles.textPrice, { color: COLORS.textW0 + 'de', }]}>{product.price}</Text> : <View />}
<Image source={{ uri: product.image }}
style={styles.image} />
<Text style={[styles.textTags, { color: tag.color, }]}>{tag.tagName}</Text>
</View>
<Text style={styles.textItem}>{product.productName}</Text>
<View style={styles.inputRow}>
<MaterialCommunityIcons name="label-percent-outline" color={COLORS.textW0 + '55'} size={30} />
<TextInput
placeholder={'Amount'}
placeholderTextColor={'grey'}
style={[styles.textInput]}
onChangeText={text => setAmount(text)}
value={amount}
keyboardType={'number-pad'}
/>
</View>
<View style={styles.inputRow}>
<MaterialCommunityIcons name="account-supervisor-circle" color={COLORS.textW0 + '55'} size={30} />
<TextInput
placeholder={'For'}
placeholderTextColor={'grey'}
style={styles.textInput}
onChangeText={text => setPerson(text)}
value={person}
/>
</View>
<View style={styles.inputRow}>
<MaterialCommunityIcons name="pencil-plus-outline" color={COLORS.textW0 + '55'} size={30} />
<TextInput
placeholder={'Additional details'}
placeholderTextColor={'grey'}
style={styles.textInput}
onChangeText={text => setDetails(text)}
value={details}
/>
</View>
<TouchableOpacity style={styles.modalSaveButton} onPress={saveModal} >
<Text style={styles.modalSaveButtonText}>Save</Text>
</TouchableOpacity>
</View>
</Modal >
)
}
const styles = StyleSheet.create({
container: {
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
minHeight: '60%',
backgroundColor: COLORS.dp01,
alignItems: 'center',
},
modalEditItem: {
justifyContent: 'flex-end',
margin: 0,
padding: 0,
width: '100%',
},
image: {
marginTop: -70,
width: '50%',
height: 180,
alignSelf: 'flex-start',
borderTopRightRadius: 20,
},
textInput: {
height: 30,
width: '85%',
borderBottomColor: COLORS.primary + 'aa',
borderBottomWidth: 1,
borderRadius: 3,
marginBottom: 10,
padding: 0,
paddingLeft: 5,
paddingRight: 5,
color: COLORS.textW0 + 'dd',
fontSize: 20
},
inputRow: {
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'flex-start',
width: '100%',
marginLeft: 5,
},
textItem: {
width: '100%',
alignSelf: 'flex-start',
padding: 5,
paddingLeft: 15,
opacity: 0.9,
fontSize: 24,
color: COLORS.textW0 + 'ff',
backgroundColor: '#000' + '3',
},
textPrice: {
paddingLeft: '52%',
position: 'absolute',
fontSize: 18,
color: COLORS.textW1,
backgroundColor: '#000' + '5',
padding: 5,
width: '100%',
borderTopRightRadius: 10,
},
textTags: {
left: 8,
marginTop: 35,
fontFamily: 'Roboto',
fontSize: 18,
color: COLORS.textW1,
},
modalSaveButtonText: {
opacity: 0.8,
fontSize: 20,
color: COLORS.primary,
fontWeight: 'bold',
},
modalSaveButton:{
marginTop: 10,
marginBottom: 10,
}
})

View File

@@ -0,0 +1,171 @@
import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity, View, Text, TextInput, Image, FlatList, ScrollView } from 'react-native';
//dependencies
import Modal from 'react-native-modal';
//themes
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { COLORS } from '../../../themes/Colors';
//redux
import { useDispatch, useSelector } from 'react-redux';
import { modalToggle, editProduct } from '../../../redux/slices/groceryList/productsSlice';
export default EditProductModal = () => {
const product = useSelector(state => state.products.find((product) => product.modalVisible === true));
const dispatch = useDispatch();
const tags = useSelector(state => state.tags)
let tag = tags.find(tag => tag.tagName === product.tag)
const [productName, setProductName] = useState(product.productName);
const [tagName, setTagName] = useState(product.tag);
const [price, setPrice] = useState(product.price.toString());
const [imageURL, setImageURL] = useState(product.image);
const saveModal = () => {
dispatch(editProduct(product.id, productName, tagName, price, imageURL));
dispatch(modalToggle(product.id))
}
return (
<Modal isVisible={product.modalVisible} animationIn={'slideInUp'} animationOut={'slideOutDown'}
onBackdropPress={saveModal}
onBackButtonPress={() => dispatch(modalToggle(product.id))}
swipeDirection={'down'}
onSwipeComplete={saveModal}
useNativeDriverForBackdrop
style={styles.modalEditItem} >
<View style={styles.container}>
<View style={{ width: '100%', flexDirection: 'row', alignItems: 'flex-start', justifyContent: 'flex-start', backgroundColor: tag.color + '22', borderRadius: 10, }}>
<Image source={{ uri: product.image }}
style={styles.image} />
</View>
<View style={styles.inputRow}>
<TextInput
placeholder={'Product'}
placeholderTextColor={'grey'}
style={[styles.textInput, styles.textItem]}
onChangeText={text => setProductName(text)}
value={productName}
/>
</View>
<View style={styles.inputRow}>
<MaterialCommunityIcons name="tag-text-outline" color={COLORS.textW0 + '55'} size={30} />
<TextInput
placeholder={'Tag'}
placeholderTextColor={'grey'}
style={styles.textInput}
onChangeText={text => setTagName(text)}
value={tagName}
/>
</View>
<View style={styles.inputRow}>
<MaterialCommunityIcons name="currency-usd" color={COLORS.textW0 + '55'} size={30} />
<TextInput
placeholder={'Price'}
placeholderTextColor={'grey'}
style={styles.textInput}
onChangeText={text => setPrice(text)}
keyboardType={'number-pad'}
value={price}
/>
</View>
<TouchableOpacity style={styles.modalSaveButton} onPress={saveModal} >
<Text style={styles.modalSaveButtonText}>Save</Text>
</TouchableOpacity>
</View>
</Modal >
)
}
const styles = StyleSheet.create({
container: {
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
minHeight: '60%',
backgroundColor: COLORS.dp01,
alignItems: 'center',
},
modalEditItem: {
justifyContent: 'flex-end',
margin: 0,
padding: 0,
width: '100%',
},
image: {
marginTop: -70,
width: '50%',
height: 180,
alignSelf: 'flex-start',
borderTopRightRadius: 20,
},
textInput: {
height: 30,
width: '85%',
borderBottomColor: COLORS.primary + 'aa',
borderBottomWidth: 1,
borderRadius: 3,
marginBottom: 10,
padding: 0,
paddingLeft: 5,
paddingRight: 5,
color: COLORS.textW0 + 'dd',
fontSize: 20
},
inputRow: {
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'flex-start',
width: '100%',
marginLeft: 5,
},
textItem: {
width: '100%',
height: 40,
alignSelf: 'flex-start',
padding: 5,
paddingLeft: 15,
opacity: 0.9,
fontSize: 24,
color: COLORS.textW0 + 'ff',
backgroundColor: '#000' + '3',
},
textPrice: {
paddingLeft: '52%',
position: 'absolute',
fontSize: 18,
color: COLORS.textW1,
backgroundColor: '#000' + '5',
padding: 5,
width: '100%',
borderTopRightRadius: 10,
},
textTags: {
left: 8,
marginTop: 35,
fontFamily: 'Roboto',
fontSize: 18,
color: COLORS.textW1,
},
modalSaveButtonText: {
opacity: 0.8,
fontSize: 20,
color: COLORS.primary,
fontWeight: 'bold',
},
modalSaveButton: {
marginTop: 10,
marginBottom: 10,
}
})

View File

@@ -4,10 +4,11 @@ const initialState = [
{ {
amount: 5, amount: 5,
person: 'Wolf', person: 'Wolf',
details: 'margarita', details: 'Gezout',
productId: '20fdc79cde62', productId: '20fdc79cde62',
checked: false, checked: false,
modalVisible: false,
}, },
{ {
amount: 12, amount: 12,
@@ -15,6 +16,7 @@ const initialState = [
productId: '20fdc79cde63', productId: '20fdc79cde63',
checked: false, checked: false,
modalVisible: false,
}, },
{ {
amount: 2, amount: 2,
@@ -22,14 +24,25 @@ const initialState = [
productId: '20fdc79cde64', productId: '20fdc79cde64',
checked: false, checked: false,
modalVisible: false,
}, },
{ {
amount: 2, amount: 4,
person: 'Storm', person: 'Storm',
details: 'Gezout', details: 'margarita',
productId: '20fdc79cde60', productId: '20fdc79cde60',
checked: false, checked: false,
modalVisible: false,
},
{
amount: 3,
person: 'Wolfje',
details: 'Aardbei',
productId: 'Jam',
checked: false,
modalVisible: false,
}, },
] ]
@@ -40,7 +53,13 @@ const itemsSlice = createSlice({
reducers: { reducers: {
itemAdded: { itemAdded: {
reducer(state, action) { reducer(state, action) {
state.push(action.payload); let item = state.find((item) => item.productId === action.payload.productId)
if (item) {
item.amount += action.payload.amount;
}
else {
state.push(action.payload);
}
}, },
prepare(productId, amount, person, details) { prepare(productId, amount, person, details) {
return { return {
@@ -50,6 +69,7 @@ const itemsSlice = createSlice({
details: details, details: details,
productId: productId, productId: productId,
modalVisible: false,
checked: false, checked: false,
} }
} }
@@ -57,7 +77,8 @@ const itemsSlice = createSlice({
}, },
checkToggle: { checkToggle: {
reducer(state, action) { reducer(state, action) {
state.find((item) => item.productId === action.payload.productId).checked = !state.find((item) => item.productId === action.payload.productId).checked; let item = state.find((item) => item.productId === action.payload.productId)
item.checked = !item.checked
}, },
prepare(productId) { prepare(productId) {
return { return {
@@ -67,25 +88,48 @@ const itemsSlice = createSlice({
} }
} }
}, },
amountUp: { modalToggle: {
reducer(state, action) { reducer(state, action) {
state.find((item) => item.productId === action.payload.id).amount += Number(action.payload.amount); let item = state.find((item) => item.productId === action.payload.productId)
if (!state.find((item) => item.modalVisible === true) || item.modalVisible) {
item.modalVisible = !item.modalVisible;
}
}, },
prepare(id, amount) { prepare(productId) {
return { return {
payload: { payload: {
id, productId: productId
amount,
} }
} }
} }
}, },
removeCheckedItems(state){ removeCheckedItems(state) {
return state.filter(item=>item.checked === false) return state.filter(item => item.checked === false)
},
editItem: {
reducer(state, action) {
const { productId, amount, person, details } = action.payload
let item = state.find((item) => item.productId === productId)
item.productId = productId
item.amount = amount
item.person = person
item.details = details
},
prepare(productId, amount, person, details) {
return {
payload: {
amount: Number(amount),
person: person,
details: details,
productId: productId,
}
}
}
} }
}, },
}); });
export const { itemAdded, checkToggle, amountUp, removeCheckedItems } = itemsSlice.actions; export const { itemAdded, checkToggle, modalToggle, removeCheckedItems, editItem } = itemsSlice.actions;
export default itemsSlice.reducer; export default itemsSlice.reducer;

View File

@@ -1,4 +1,4 @@
import { createSlice } from '@reduxjs/toolkit'; import { createSlice, nanoid } from '@reduxjs/toolkit';
const initialState = [ const initialState = [
{ {
@@ -9,6 +9,7 @@ const initialState = [
nutrients: [{ calories: 120 }, { proteins: 20 }], nutrients: [{ calories: 120 }, { proteins: 20 }],
modalVisible: false,
checked: false, checked: false,
id: '20fdc79cde60', id: '20fdc79cde60',
}, },
@@ -18,6 +19,7 @@ const initialState = [
image: 'https://www.kitchensanctuary.com/wp-content/uploads/2020/10/Lasagne-square-FS-79.jpg', image: 'https://www.kitchensanctuary.com/wp-content/uploads/2020/10/Lasagne-square-FS-79.jpg',
price: 1.99, price: 1.99,
modalVisible: false,
checked: false, checked: false,
id: '20fdc79cde64', id: '20fdc79cde64',
}, },
@@ -28,19 +30,54 @@ const initialState = [
nutrients: [{ calories: 120 }, { proteins: 20 }], nutrients: [{ calories: 120 }, { proteins: 20 }],
price: 1.39, price: 1.39,
modalVisible: false,
checked: false, checked: false,
id: '20fdc79cde63', id: '20fdc79cde63',
}, },
{ {
productName: 'Gedroogde worstjes', productName: 'Dried sausages',
tag: 'Snack', tag: 'Meat',
image: 'https://www.aldi.be/content/aldi/belgium/promotions/source-localenhancement/2019/2019-01/2019-01-02/vast_assortiment/1308/1/0/_jcr_content/assets/imported-images/BILD_INTERNET2/1308_PRIMARY_0_nl-fr-de_Mini_sticks_100_g_Panda_GROEP.png/_jcr_content/renditions/opt.1250w.png.res/1590539744886/opt.1250w.png', image: 'https://www.aldi.be/content/aldi/belgium/promotions/source-localenhancement/2019/2019-01/2019-01-02/vast_assortiment/1308/1/0/_jcr_content/assets/imported-images/BILD_INTERNET2/1308_PRIMARY_0_nl-fr-de_Mini_sticks_100_g_Panda_GROEP.png/_jcr_content/renditions/opt.1250w.png.res/1590539744886/opt.1250w.png',
price: 1.79, price: 1.79,
modalVisible: false,
checked: false, checked: false,
id: '20fdc79cde62', id: '20fdc79cde62',
}, },
{
productName: 'Minced meat',
tag: 'Meat',
image: 'https://www.aldi.nl/content/dam/aldi/netherlands/offers/weekactie/2020/wk19/1387_01.png/_jcr_content/renditions/opt.1250w.png.res/1587557386020/opt.1250w.png',
price: 3.99,
modalVisible: false,
checked: false,
id: '20adc79cde61',
},
{
productName: 'Veggie soup',
tag: 'Vegetables & fruit',
image: 'https://www.cookingclassy.com/wp-content/uploads/2014/10/vegetable-soup-7.jpg',
price: 1.49,
modalVisible: false,
checked: false,
id: '20fdc29cde61',
},
{
productName: 'Dorito\'s',
tag: 'Snack',
image: 'https://40aprons.com/wp-content/uploads/2020/09/taco-bell-nacho-cheese-sauce-recipe-4.jpg',
price: 1.12,
modalVisible: false,
checked: false,
id: '29fdc29cde61',
},
{ {
productName: 'Berliner ball', productName: 'Berliner ball',
tag: 'Snack', tag: 'Snack',
@@ -48,8 +85,9 @@ const initialState = [
price: 1.79, price: 1.79,
modalVisible: false,
checked: false, checked: false,
id: '20fdc79cde61', id: '20fdc71cde61',
}, },
] ]
@@ -61,24 +99,49 @@ const productsSlice = createSlice({
reducer(state, action) { reducer(state, action) {
state.push(action.payload); state.push(action.payload);
}, },
prepare(productName, tag, price, image, nutrients) { prepare(productName, tag, price, image) {
return { return {
payload: { payload: {
productName, productName,
tag, tag,
price, price: Number(price),
image, image,
nutrients,
id: Math.floor((1 + Math.random()) * 0x1000000000000).toString(16).substring(1), modalVisible: false,
checked: false, checked: false,
id: nanoid(),
}
}
}
},
editProduct: {
reducer(state, action) {
const { productId, productName, tag, price, image } = action.payload
let product = state.find((product) => product.id === productId)
product.productName = productName
product.tag = tag
product.price = price
product.image = image
},
prepare(productId, productName, tag, price, image) {
return {
payload: {
productId: productId,
productName: productName,
tag: tag,
price: price,
image: image
} }
} }
} }
}, },
checkToggle: { checkToggle: {
reducer(state, action) { reducer(state, action) {
state.find((product) => product.id === action.payload.id).checked = !state.find((product) => product.id === action.payload.id).checked; let product = state.find((product) => product.id === action.payload.id)
product.checked = !product.checked;
}, },
prepare(id) { prepare(id) {
return { return {
@@ -88,9 +151,24 @@ const productsSlice = createSlice({
} }
} }
}, },
modalToggle: {
reducer(state, action) {
let product = state.find((product) => product.id === action.payload.productId)
if (!state.find((nProduct) => nProduct.modalVisible === true) || product.modalVisible) {
product.modalVisible = !product.modalVisible;
}
},
prepare(productId) {
return {
payload: {
productId: productId
}
}
}
},
}, },
}); });
export const { productAdded, checkToggle } = productsSlice.actions; export const { productAdded, editProduct, checkToggle, modalToggle } = productsSlice.actions;
export default productsSlice.reducer; export default productsSlice.reducer;

View File

@@ -1,13 +1,60 @@
import {createSlice} from '@reduxjs/toolkit' import { createSlice } from '@reduxjs/toolkit'
const initialState = [ const initialState = [
{ id: '20fdc79cde60', amount: 5 },
] ]
const storageSlice = createSlice({ const storageSlice = createSlice({
name: 'storage', name: 'storage',
initialState, initialState,
reducers:{ reducers: {
pushItems: {
} reducer(state, action) {
action.payload.items.forEach((item) => {
let storageItem = state.find((storageItem) => storageItem.id === item.productId)
if (storageItem) {
state.find((storageItem) => storageItem.id === item.productId).amount += item.amount
}
else {
state.push({ id: item.productId, amount: item.amount })
}
})
},
prepare(items) {
return {
payload: {
items,
}
}
}
},
changeAmount: {
reducer(state, action) {
state.find((storageItem) => storageItem.id === action.payload.id).amount += action.payload.difference
},
prepare(id, difference) {
return {
payload: {
id,
difference,
}
}
}
},
removeItem: {
reducer(state, action) {
state.splice(state.findIndex((storageItem) => storageItem.id === action.payload.id),1)
},
prepare(id) {
return {
payload: {
id,
}
}
}
}
},
}) })
export const { pushItems, changeAmount, removeItem } = storageSlice.actions
export default storageSlice.reducer

View File

@@ -1,9 +1,9 @@
import { createSlice } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit';
const initialState = [ const initialState = [
{ tagName: 'no-tag', color: '#253f34' }, { tagName: 'uncathegorized', color: '#253f34' },
{ tagName: 'Meat', color: '#ef5350' }, { tagName: 'Meat', color: '#ef5350' },
{ tagName: 'Vegetable', color: '#BDD684' }, { tagName: 'Vegetables & fruit', color: '#BDD684' },
{ tagName: 'Meal', color: '#FA9600' }, { tagName: 'Meal', color: '#FA9600' },
{ tagName: 'Snack', color: '#9575CD' }, { tagName: 'Snack', color: '#9575CD' },
{ tagName: 'Instant', color: '#86CAC5' }, { tagName: 'Instant', color: '#86CAC5' },

View File

@@ -3,6 +3,7 @@ import { createSlice } from '@reduxjs/toolkit';
const initialState = { const initialState = {
addItemModalVisible: false, addItemModalVisible: false,
addProductModalVisible: false, addProductModalVisible: false,
popupVisibility: false, popupVisibility: false,
} }
const toggleSlice = createSlice({ const toggleSlice = createSlice({

View File

@@ -2,8 +2,10 @@ import { configureStore } from '@reduxjs/toolkit';
import itemsReducer from './slices/groceryList/itemsSlice'; import itemsReducer from './slices/groceryList/itemsSlice';
import productsReducer from './slices/groceryList/productsSlice'; import productsReducer from './slices/groceryList/productsSlice';
import toggleReducer from './slices/groceryList/toggleSlice'; import storageReducer from './slices/groceryList/storageSlice';
import tagsReducer from './slices/groceryList/tagsSlice'; import tagsReducer from './slices/groceryList/tagsSlice';
import toggleReducer from './slices/groceryList/toggleSlice';
@@ -11,6 +13,8 @@ export default configureStore({
reducer: { reducer: {
items: itemsReducer, items: itemsReducer,
products: productsReducer, products: productsReducer,
storage: storageReducer,
tags: tagsReducer, tags: tagsReducer,
toggle: toggleReducer, toggle: toggleReducer,
}, },

View File

@@ -1,14 +1,13 @@
import React, { useRef, useEffect, useState } from 'react'; import React, { useRef, useEffect, useState } from 'react';
import { StyleSheet, View, ScrollView, Text, Animated, TouchableOpacity, Image } from 'react-native'; import { StyleSheet, View, FlatList, Text, Animated, TouchableOpacity, Image } from 'react-native';
import { useHeaderHeight } from '@react-navigation/stack'; import { useHeaderHeight } from '@react-navigation/stack';
//dependencies\ //dependencies\
//components //components
import { AddItemButton, PushItemsToStorageButton, RemoveItemsButton, AddNewRecipeButton } from '../../components/groceryComponents/GroceryListButtons'; import { AddItemButton, PushItemsToStorageButton, RemoveItemsButton, AddNewRecipeButton } from '../../components/groceryComponents/GroceryListButtons';
import { ListedItem } from '../../components/groceryComponents/ListedItem'; import ListedItem from '../../components/groceryComponents/ListedItem';
//redux //redux
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
//themes //themes
@@ -98,25 +97,36 @@ const AnimButtons = () => {
function GroceryList() { function GroceryList() {
const items = useSelector(state => state.items); const products = useSelector(state=> state.products)
const itemList = items.map((item, index) => { const compareFunction = (a,b) => {
return <ListedItem key={index} id={item.productId} /> const newA = products.find((product=>product.id === a.productId))
}); let tagA
newA ? tagA = newA.tag : tagA = "uncathegorized"
const newB = products.find((product=>product.id === b.productId))
let tagB
newB ? tagB = newB.tag : tagB = "uncathegorized"
return tagA.localeCompare(tagB)
}
const items = useSelector(state => state.items).slice().sort(compareFunction);
return ( return (
<View style={[styles.container]}> <View style={[styles.container]}>
{/* Scroll */} <FlatList
<ScrollView> ListHeaderComponent={<View style={{ height: useHeaderHeight() }} />}
<View style={{ height: useHeaderHeight() }} /> ListFooterComponent={<View style={{ height: 100 }} />}
<View style= initialNumToRender={10}
{{ maxToRenderPerBatch={10}
top: 5, data={items}
}}> renderItem={({ item }) => (
{itemList} <ListedItem id={item.productId} />
</View> )}
<View style={{ height: 500 }}></View> keyExtractor={(item, index) => {
</ScrollView> return index.toString();
}}
/>
{items.find(item => item.modalVisible === true) ? <EditItemModal /> : <View />}
{/* buttons */} {/* buttons */}
<View style={styles.itemLocation} > <View style={styles.itemLocation} >
@@ -145,4 +155,4 @@ const styles = StyleSheet.create({
bottom: 10, bottom: 10,
} }
}); });
export default GroceryList; export default React.memo(GroceryList);

View File

@@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
import { StyleSheet, View, ScrollView, Text } from 'react-native'; import { StyleSheet, View, FlatList } from 'react-native';
import { useHeaderHeight } from '@react-navigation/stack'; import { useHeaderHeight } from '@react-navigation/stack';
//dependencies\ //dependencies\
//components //components
import { AddProductButton } from '../../components/groceryComponents/ProductButtons'; import { AddProductButton } from '../../components/groceryComponents/ProductButtons';
import { ListedProduct } from '../../components/groceryComponents/ListedProduct'; import ListedProduct from '../../components/groceryComponents/ListedProduct';
import { COLORS } from '../../themes/Colors'; import { COLORS } from '../../themes/Colors';
@@ -14,23 +14,24 @@ import { useSelector } from 'react-redux';
function Products() { function Products() {
const products = useSelector(state => state.products) const products = useSelector(state => state.products).slice().sort((a,b)=> a.tag.localeCompare(b.tag))
const productsList = products.map((product, index) => {
return <ListedProduct key={index} id={product.id}/>
});
return ( return (
<View style={[styles.container]}> <View style={[styles.container]}>
{/* Scroll */} {/* Scroll */}
<ScrollView> <FlatList
<View style={{ height: useHeaderHeight() }} /> ListHeaderComponent={<View style={{ height: useHeaderHeight() }} />}
<View style= ListFooterComponent={<View style={{ height: 100 }} />}
{{ initialNumToRender={10}
top: 5, maxToRenderPerBatch={10}
}}> data={products}
{productsList} renderItem={({ item, index }) => {
</View> return <ListedProduct id={item.id} />
<View style={{ height: 3000 }}></View> }}
</ScrollView> keyExtractor={(item, index) => {
return index.toString();
}}
/>
{products.find(product=>product.modalVisible === true) ? <EditProductModal /> : <View/>}
{/* buttons */} {/* buttons */}
<View style={styles.addProductView} > <View style={styles.addProductView} >
<AddProductButton /> <AddProductButton />
@@ -56,4 +57,4 @@ const styles = StyleSheet.create({
bottom: 10, bottom: 10,
} }
}); });
export default Products; export default React.memo(Products);

View File

@@ -1,26 +1,41 @@
import React from 'react'; import React from 'react';
import { StyleSheet, View, ScrollView, Text } from 'react-native'; import { StyleSheet, View, ScrollView, Text } from 'react-native';
//dependencies\ //dependencies
//components import { useHeaderHeight } from '@react-navigation/stack';
import AddItemButton from '../../components/groceryComponents/GroceryListButtons';
import { COLORS } from '../../themes/Colors';
//components
import StorageList from '../../components/groceryComponents/StorageList';
import { COLORS } from '../../themes/Colors';
//redux
import { useSelector } from 'react-redux';
function Storage() { function Storage() {
const tags = useSelector(state => state.tags)
return ( return (
<View style={styles.dp00}> <View style={styles.dp00}>
<View> <View>
</View> </View>
<ScrollView> <ScrollView>
<View style={{ height: useHeaderHeight() }} />
<View style=
{{
top: 5,
}}>
{tags.map((tag, index)=>{
return <StorageList key = {index} tag={tag.tagName}/>
})}
</View>
<View style={{ height: 500 }}></View>
</ScrollView> </ScrollView>
</View> </View>
); );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
background: { background: {
flex:1, flex: 1,
backgroundColor: COLORS.dp00, backgroundColor: COLORS.dp00,
} }
}); });
export default Storage; export default Storage;

View File

@@ -12,11 +12,12 @@ export const COLORS = {
secondary: '#f2aeac', //secondary secondary: '#f2aeac', //secondary
secondaryVar: '#ffffff', //secondary variant secondaryVar: '#ffffff', //secondary variant
price: '#C6A337',
//layout colors #DAE9FF //layout colors #DAE9FF
dp00: '#0C0F12', //0% dp00: '#0C0F12', //0%
dp01: '#161a1e', //5% dp01: '#121518', //5%
dp02: '#1a1e23', //7% dp02: '#1a1e23', //7%
dp03: '#1f2225', //8% dp03: '#1f2225', //8%
dp04: '#1c2025', //9% dp04: '#1c2025', //9%