From 3f864c8922cbf7c2b5d5489ae5dc5263ad4bbbc5 Mon Sep 17 00:00:00 2001 From: carol Date: Tue, 2 Jan 2024 16:31:34 +0100 Subject: [PATCH 01/45] circle --- components/common/loadingSymbol.tsx | 58 +++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/components/common/loadingSymbol.tsx b/components/common/loadingSymbol.tsx index 17fb29a..b7e1a03 100644 --- a/components/common/loadingSymbol.tsx +++ b/components/common/loadingSymbol.tsx @@ -1,23 +1,49 @@ -import { StyleSheet, View } from "react-native"; +import React, { useEffect, useRef } from "react"; +import { StyleSheet, View, Animated, Easing, ActivityIndicator } from "react-native"; const LoadingSymbol = () => { + const color = ["blue", "red", "purple", "green", "yellow", "orange"]; + const random = Math.floor(Math.random() * color.length); - const color = ["blue", "red", "purple", "green", "yellow", "orange"]; - const random = Math.floor(Math.random() * color.length); + const spinValue = useRef(new Animated.Value(0)).current; - const styles = StyleSheet.create({ - container: { - backgroundColor: color[random], - width: "100%", - height: "100%", - position: "absolute", - } - }); + const spin = spinValue.interpolate({ + inputRange: [0, 1], + outputRange: ["0deg", "360deg"], + }); - return ( - + useEffect(() => { + Animated.loop( + Animated.timing(spinValue, { + toValue: 1, + duration: 2000, + easing: Easing.linear, + useNativeDriver: true, + }) + ).start(); + }, [spinValue]); + + const styles = StyleSheet.create({ + container: { + backgroundColor: color[random], + width: "100%", + height: "100%", + position: "absolute", + justifyContent: "center", + alignItems: "center", + }, + loader: { + width: 100, + height: 100, + transform: [{ rotate: spin }], + }, + }); + + return ( + + + ); -} +}; - -export default LoadingSymbol; \ No newline at end of file +export default LoadingSymbol; From 9204d6f235a6adf64d5f70b6165f11da92df2ac8 Mon Sep 17 00:00:00 2001 From: Thomas Schleicher Date: Wed, 3 Jan 2024 17:31:59 +0000 Subject: [PATCH 02/45] Resolve "Add Category Modal" --- app/(tabs)/budget/_layout.tsx | 10 +-- app/(tabs)/budget/addCategory.tsx | 82 +++++++++++++++++++++++- app/(tabs)/budget/index.tsx | 5 +- babel.config.js | 1 + components/budget/customColorPicker.tsx | 51 +++++++++++++++ components/budget/typeSelectorSwitch.tsx | 53 +++++++++++++++ components/common/button.tsx | 39 +++++++++++ components/index.tsx | 17 +++-- package-lock.json | 56 +++++++++++++++- package.json | 4 +- 10 files changed, 297 insertions(+), 21 deletions(-) create mode 100644 components/budget/customColorPicker.tsx create mode 100644 components/budget/typeSelectorSwitch.tsx create mode 100644 components/common/button.tsx diff --git a/app/(tabs)/budget/_layout.tsx b/app/(tabs)/budget/_layout.tsx index 5c62492..238abc6 100644 --- a/app/(tabs)/budget/_layout.tsx +++ b/app/(tabs)/budget/_layout.tsx @@ -1,16 +1,10 @@ import { Stack } from "expo-router"; -import { useTheme } from "../../contexts/ThemeContext"; export default function _Layout() { - const { colors } = useTheme(); return ( - + - + ); } \ No newline at end of file diff --git a/app/(tabs)/budget/addCategory.tsx b/app/(tabs)/budget/addCategory.tsx index e8edbbc..c211406 100644 --- a/app/(tabs)/budget/addCategory.tsx +++ b/app/(tabs)/budget/addCategory.tsx @@ -1,8 +1,86 @@ +import { router, useLocalSearchParams } from "expo-router"; +import { useState } from "react"; +import { SafeAreaView, StyleSheet, Text, TextInput, View } from "react-native"; +import { CustomColorPicker, NavigationButton, TypeSelectorSwitch } from "../../../components"; +import { useTheme } from "../../contexts/ThemeContext"; const addCategory = () => { + const {colors} = useTheme(); + + const parameters = useLocalSearchParams(); + + const [categoryName, setCartegoryName] = useState("Enter Category Name..."); + const [categoryColor, setCartegoryColor] = useState(null); + const [selectedType, setSelectedType] = useState("expense"); + return ( - <> + + Category Editor + + + + { + setCartegoryName(newName); + }}/> + + + { + setSelectedType(type); + }} + /> + + + { + setCartegoryColor(color); + }}/> + + + + { + router.back(); + }}/> + { + console.log("Implement Saving here!"); + router.back(); + }}/> + + + ); } -export default addCategory; \ No newline at end of file +export default addCategory; + +const styles = StyleSheet.create({ + containerStyle: { + flex: 1, + margin: 10, + borderRadius: 10, + }, + safeAreaViewStyle: { + flex: 1, + flexDirection: "column" + }, + headingTextStyle: { + fontSize: 40, + fontWeight: "bold", + alignSelf: "center", + marginVertical: 10, + }, + navigationButtonViewStyle: { + flexDirection: "row", + justifyContent: "center", + marginBottom: 10, + }, + textInputViewStyle: { + borderRadius: 10, + paddingVertical: 10, + margin: 10, + }, + textInputStyle: { + paddingHorizontal: 10, + fontSize: 25, + } +}); \ No newline at end of file diff --git a/app/(tabs)/budget/index.tsx b/app/(tabs)/budget/index.tsx index 4ca010e..3e2052c 100644 --- a/app/(tabs)/budget/index.tsx +++ b/app/(tabs)/budget/index.tsx @@ -4,8 +4,7 @@ import { useEffect, useState } from 'react'; import { StyleSheet, View } from 'react-native'; import { FlatList } from 'react-native-gesture-handler'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { BudgetHeader, LoadingSymbol, Plus } from '../../../components'; -import CategoryItem from '../../../components/budget/categoryItem'; +import { BudgetHeader, CategoryItem, LoadingSymbol, Plus } from '../../../components'; import useFetch from '../../../hooks/useFetch'; import { useTheme } from '../../contexts/ThemeContext'; @@ -43,7 +42,7 @@ export default function Page() { { - router.push("/(tabs)/budget/addCategory") + router.push({pathname: '/(tabs)/budget/addCategory', params: { guid: '123'}}); //This needs to be changed to a regular push, without parameters }}/> {isLoading ? () : ( diff --git a/babel.config.js b/babel.config.js index 79900d3..f76b519 100644 --- a/babel.config.js +++ b/babel.config.js @@ -4,6 +4,7 @@ module.exports = function (api) { presets: ['babel-preset-expo'], plugins: [ 'expo-router/babel', + 'react-native-reanimated/plugin', ], }; }; diff --git a/components/budget/customColorPicker.tsx b/components/budget/customColorPicker.tsx new file mode 100644 index 0000000..b78e1b4 --- /dev/null +++ b/components/budget/customColorPicker.tsx @@ -0,0 +1,51 @@ +import { StyleSheet } from "react-native"; +import ColorPicker, { BrightnessSlider, HueSlider, Preview, SaturationSlider, } from "reanimated-color-picker"; + +export type CustomColorPickerProperties = { + currentColor?: string | null, + handleColorChange: (color: string) => void | undefined, +} + +const CustomColorPicker = (properties: CustomColorPickerProperties) => { + return ( + { + properties.handleColorChange(color["hex"]) + }} + style={styles.colorPickerStyle} + sliderThickness={30} + thumbSize={40} + thumbShape= "circle"> + + + + + + + ); +} + +const generateRandomColor = (): string => { + return '#' + Math.floor(Math.random()*16777215).toString(16); +} + +export default CustomColorPicker; + +const styles = StyleSheet.create({ + colorPickerStyle: { + justifyContent: 'center', + }, + sliderStyle: { + margin: 10, + }, + previewStyle: { + margin: 10, + height: 50, + borderRadius: 10, + } + }); \ No newline at end of file diff --git a/components/budget/typeSelectorSwitch.tsx b/components/budget/typeSelectorSwitch.tsx new file mode 100644 index 0000000..dc6231b --- /dev/null +++ b/components/budget/typeSelectorSwitch.tsx @@ -0,0 +1,53 @@ + +import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; +import { useTheme } from "../../app/contexts/ThemeContext"; + +export type TypeSelectorSwitchProperties = { + handleButtonPress: (type: string) => void, + currentSelected: string, +} + +const TypeSelectorSwitch = (properties: TypeSelectorSwitchProperties) => { + const {colors} = useTheme(); + + return ( + + { + properties.handleButtonPress("expense"); + }} + style={[styles.touchableOpacityStyle, properties.currentSelected == "expense" ? {backgroundColor: colors.accentColor} : {backgroundColor: colors.elementDefaultColor}] + }> + Expenses + + { + properties.handleButtonPress("saving"); + }} + style={[styles.touchableOpacityStyle, properties.currentSelected == "saving" ? {backgroundColor: colors.accentColor} : {backgroundColor: colors.elementDefaultColor}] + }> + Savings + + + ); +} + +export default TypeSelectorSwitch; + +const styles = StyleSheet.create({ + containerStyle: { + flexDirection: "row", + justifyContent: "center", + }, + touchableOpacityStyle: { + flex: 1, + marginHorizontal: 10, + borderRadius: 10, + }, + textStyle: { + paddingHorizontal: 10, + fontSize: 25, + textAlign: "center", + paddingVertical: 5, + } +}); \ No newline at end of file diff --git a/components/common/button.tsx b/components/common/button.tsx new file mode 100644 index 0000000..a846cfd --- /dev/null +++ b/components/common/button.tsx @@ -0,0 +1,39 @@ + +import { StyleSheet, Text, TouchableHighlight } from "react-native"; +import { useTheme } from "../../app/contexts/ThemeContext"; + +export type NavigationButtonProperties = { + onPress?: () => void | undefined, + text: string, +} + +const NavigationButton = (properties: NavigationButtonProperties) => { + + const {colors} = useTheme(); + + return ( + + + {properties.text} + + ); +} + +export default NavigationButton; + +const styles = StyleSheet.create({ + touchableHighlightStyle: { + borderRadius: 10, + marginVertical: 10, + marginHorizontal: 15, + paddingHorizontal: 20, + paddingVertical: 5, + }, + buttonTextStyle: { + textAlign: "center", + fontSize: 30, + } +}); diff --git a/components/index.tsx b/components/index.tsx index 4d74a43..f3dd44f 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -4,19 +4,24 @@ import Welcome from "./home/Welcome" import ExpenseItem from "./home/expenseItem" //common +import CustomCard from "./common/CustomCard" +import SearchBar from "./common/SearchBar" +import NavigationButton from "./common/button" import LoadingSymbol from "./common/loadingSymbol" import Plus from "./common/plus" -import SearchBar from "./common/SearchBar" -import CustomCard from "./common/CustomCard" +//budget +import BudgetHeader from "./budget/budgetHeader" +import CategoryItem from "./budget/categoryItem" +import CustomColorPicker from "./budget/customColorPicker" +import TypeSelectorSwitch from "./budget/typeSelectorSwitch" //login -import BudgetHeader from "./budget/budgetHeader" import Input from "./login/input" export { - BudgetHeader, ButtonSetting, CustomCard, ExpenseItem, Input, - LoadingSymbol, Plus, - SearchBar, ToggleSetting, Welcome + BudgetHeader, ButtonSetting, CategoryItem, CustomCard, CustomColorPicker, ExpenseItem, Input, + LoadingSymbol, NavigationButton, Plus, + SearchBar, ToggleSetting, TypeSelectorSwitch, Welcome } diff --git a/package-lock.json b/package-lock.json index d597893..16b6032 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,10 +26,12 @@ "react-native": "0.72.6", "react-native-calendars": "^1.1303.0", "react-native-gesture-handler": "~2.12.0", + "react-native-reanimated": "^3.3.0", "react-native-safe-area-context": "4.6.3", "react-native-screens": "~3.22.0", "react-native-uuid": "^2.0.1", - "react-native-web": "~0.19.6" + "react-native-web": "~0.19.6", + "reanimated-color-picker": "^2.4.2" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -1415,6 +1417,20 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-object-assign": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.23.3.tgz", + "integrity": "sha512-TPJ6O7gVC2rlQH2hvQGRH273G1xdoloCj9Pc07Q7JbIZYDi+Sv5gaE2fu+r5E7qK4zyt6vj0FbZaZTRU5C3OMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-object-rest-spread": { "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", @@ -15761,6 +15777,27 @@ "react-native": "*" } }, + "node_modules/react-native-reanimated": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.3.0.tgz", + "integrity": "sha512-LzfpPZ1qXBGy5BcUHqw3pBC0qSd22qXS3t8hWSbozXNrBkzMhhOrcILE/nEg/PHpNNp1xvGOW8NwpAMF006roQ==", + "dependencies": { + "@babel/plugin-transform-object-assign": "^7.16.7", + "@babel/preset-typescript": "^7.16.7", + "convert-source-map": "^2.0.0", + "invariant": "^2.2.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0-0", + "@babel/plugin-proposal-optional-chaining": "^7.0.0-0", + "@babel/plugin-transform-arrow-functions": "^7.0.0-0", + "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", + "@babel/plugin-transform-template-literals": "^7.0.0-0", + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-safe-area-context": { "version": "4.6.3", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.6.3.tgz", @@ -15909,6 +15946,23 @@ "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" }, + "node_modules/reanimated-color-picker": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/reanimated-color-picker/-/reanimated-color-picker-2.4.2.tgz", + "integrity": "sha512-UNw1jEEOjY2m6F/sI7/nZQwT+yl7kSxao9wJtdkHUsrmm66QRqWKku76/hZGXyl+wzdKk4yLmXMPaEaLq69GBg==", + "peerDependencies": { + "expo": ">=44.0.0", + "react": "*", + "react-native": "*", + "react-native-gesture-handler": ">=2.0.0", + "react-native-reanimated": "^2.0.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } + } + }, "node_modules/recast": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/recast/-/recast-0.21.5.tgz", diff --git a/package.json b/package.json index 7f833d1..abb4142 100644 --- a/package.json +++ b/package.json @@ -36,10 +36,12 @@ "react-native": "0.72.6", "react-native-calendars": "^1.1303.0", "react-native-gesture-handler": "~2.12.0", + "react-native-reanimated": "^3.3.0", "react-native-safe-area-context": "4.6.3", "react-native-screens": "~3.22.0", "react-native-uuid": "^2.0.1", - "react-native-web": "~0.19.6" + "react-native-web": "~0.19.6", + "reanimated-color-picker": "^2.4.2" }, "devDependencies": { "@babel/core": "^7.20.0", From 93d16fc08a3fa13c869a469c54a1e2d9c4bf17bc Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Tue, 2 Jan 2024 18:23:57 +0100 Subject: [PATCH 03/45] feat: amountField --- app/(tabs)/_layout.tsx | 1 + app/(tabs)/home/_layout.tsx | 4 +- app/(tabs)/home/addItem.tsx | 28 ++++++-- app/(tabs)/home/index.tsx | 16 ++--- components/common/AutoDecimalInput.tsx | 97 ++++++++++++++++++++++++++ components/index.tsx | 5 +- 6 files changed, 134 insertions(+), 17 deletions(-) create mode 100644 components/common/AutoDecimalInput.tsx diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 4eb7245..79ff582 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -25,6 +25,7 @@ export default function Layout() { tabBarInactiveTintColor: colors.tabIconDefault, headerShown: false, tabBarStyle: styles.tabBar, + tabBarHideOnKeyboard: true } if(!authState?.authenticated){ diff --git a/app/(tabs)/home/_layout.tsx b/app/(tabs)/home/_layout.tsx index f6c2a2b..42d452e 100644 --- a/app/(tabs)/home/_layout.tsx +++ b/app/(tabs)/home/_layout.tsx @@ -24,7 +24,9 @@ export default function _Layout() { title: "test", headerShown: false, }}/> - + (""); + const [formatedValue, setFormatedValue] = useState(""); + + const handleValueChange = (formatedValue: string) => { + setFormatedValue(formatedValue); + } + console.log(formatedValue) return ( - - addItem + + ) -} \ No newline at end of file +} + +const styles = StyleSheet.create({ + container: { + margin: SIZES.normal, + } +}) \ No newline at end of file diff --git a/app/(tabs)/home/index.tsx b/app/(tabs)/home/index.tsx index eaa0b22..edd6794 100644 --- a/app/(tabs)/home/index.tsx +++ b/app/(tabs)/home/index.tsx @@ -74,15 +74,15 @@ export default function Page() { return ( {plusShow && { - // router.push("/(tabs)/home/addItem"); + router.push("/(tabs)/home/addItem"); - executeQuery({sql: "SELECT guid FROM category", args: []}).then((result) => { - if("rows" in result[0]) { - newExpense("Test Title", result[0]["rows"][0]["guid"], "69.69.1234", 100).then(() => { - reFetch(); - }); - } - }) + // executeQuery({sql: "SELECT guid FROM category", args: []}).then((result) => { + // if("rows" in result[0]) { + // newExpense("Test Title", result[0]["rows"][0]["guid"], "69.69.1234", 100).then(() => { + // reFetch(); + // }); + // } + // }) }}/>} {isLoading && } diff --git a/components/common/AutoDecimalInput.tsx b/components/common/AutoDecimalInput.tsx new file mode 100644 index 0000000..e56aaa8 --- /dev/null +++ b/components/common/AutoDecimalInput.tsx @@ -0,0 +1,97 @@ +import { View, Text, TouchableOpacity, TextInput, StyleSheet, NativeSyntheticEvent, TextInputKeyPressEventData } from 'react-native' +import React, {LegacyRef, MutableRefObject, useRef, useState} from 'react' +import colors from '../../constants/colors'; +import { SIZES } from '../../constants/theme'; +import { useTheme } from '../../app/contexts/ThemeContext'; + +const formatDecimal = (value: string)=>{ + switch(value.length){ + case 0: + return ""; + case 1: + return "0.0"+value + case 2: + return "0."+value + default: + return value.substring(0, value.length - 2) + "." + value.substring(value.length - 2, value.length) + } +} + +interface AutoDecimalInputProps{ + onValueChange?: (formattedValue: string) => void | undefined + label: string, +} + +const AutoDecimalInput: React.FC = ({onValueChange, label}) => { + const { colors } = useTheme(); + const inputRef = useRef(null); + const [pressedNumbers, setPressedNumbers] = useState(""); + + const update = (newValues : string) => { + if(onValueChange){ + onValueChange(formatDecimal(newValues)) + } + setPressedNumbers(newValues); + } + + const handleInput = (e: NativeSyntheticEvent)=>{ + const pressedKey:string = e.nativeEvent.key + if(Number.isInteger(Number.parseInt(pressedKey))){ + if(pressedNumbers.length === 0 && pressedKey === "0"){ + return + } + update(pressedNumbers + pressedKey) + }else if(pressedKey === "Backspace"){ + update(pressedNumbers.substring(0, pressedNumbers.length - 1)) + } + } + return ( + { + if(inputRef.current) + inputRef.current.focus() + }}> + {label} + + + EUR + + + ) +} + +const styles = StyleSheet.create({ + container: { + margin: SIZES.normal, + }, + inputContainer: { + borderRadius: 20, + flexDirection: "row", + justifyContent: 'space-between' + }, + text:{ + fontSize: SIZES.large, + marginVertical: 12, + marginHorizontal: 15, + }, + currency: { + fontSize: SIZES.normal, + color: "#007acc", + marginRight: 15, + }, + currencyWrapper: { + flexDirection:"row", + justifyContent: 'flex-end', + alignItems: "center" + } +}) + +export default AutoDecimalInput; \ No newline at end of file diff --git a/components/index.tsx b/components/index.tsx index f3dd44f..c717b9e 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -9,8 +9,9 @@ import SearchBar from "./common/SearchBar" import NavigationButton from "./common/button" import LoadingSymbol from "./common/loadingSymbol" import Plus from "./common/plus" +import AutoDecimalInput from "./common/AutoDecimalInput" -//budget +//login import BudgetHeader from "./budget/budgetHeader" import CategoryItem from "./budget/categoryItem" import CustomColorPicker from "./budget/customColorPicker" @@ -22,6 +23,6 @@ import Input from "./login/input" export { BudgetHeader, ButtonSetting, CategoryItem, CustomCard, CustomColorPicker, ExpenseItem, Input, LoadingSymbol, NavigationButton, Plus, - SearchBar, ToggleSetting, TypeSelectorSwitch, Welcome + SearchBar, ToggleSetting, TypeSelectorSwitch, Welcome, AutoDecimalInput } From 5876fcaf8e48e51415cf93cc12851b7db484f8d0 Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Thu, 4 Jan 2024 19:57:05 +0100 Subject: [PATCH 04/45] feat: now has onchange functionality. clear works now --- components/common/SearchBar.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/components/common/SearchBar.tsx b/components/common/SearchBar.tsx index 372d052..714a3d6 100644 --- a/components/common/SearchBar.tsx +++ b/components/common/SearchBar.tsx @@ -1,14 +1,18 @@ import { AntDesign } from '@expo/vector-icons'; -import React from 'react'; +import React, { useState } from 'react'; import { StyleSheet, TextInput, TouchableOpacity, View, ViewProps } from 'react-native'; import { SIZES } from '../../constants/theme'; import { useTheme } from '../../app/contexts/ThemeContext'; -type SearchBarProps = {placeholder: string} & ViewProps +interface SearchBarProps extends ViewProps { + placeholder? : string; + onChangeText? : (text: string) => void | undefined +} export default function SearchBar(props: SearchBarProps) { const [isActive, setIsactive] = React.useState(false); const { colors } = useTheme(); + const [text, setText] = useState(""); const textColor = colors const backgroundColor = colors.elementDefaultColor; @@ -23,6 +27,10 @@ export default function SearchBar(props: SearchBarProps) { setIsactive(false) } } + if(props.onChangeText){ + props.onChangeText(text) + } + setText(text) } // cant apply the background color otherwise @@ -35,10 +43,10 @@ export default function SearchBar(props: SearchBarProps) { // changed styles.container to containerStyle return ( - + {isActive && - + {handleChange("")}}> } From e1efed5b2161cfcb25463b1842df031ac999d8bf Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Thu, 4 Jan 2024 21:47:10 +0100 Subject: [PATCH 05/45] changed name of SearchBar to TextInputBar --- components/common/{SearchBar.tsx => TextInputBar.tsx} | 4 ++-- components/index.tsx | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) rename components/common/{SearchBar.tsx => TextInputBar.tsx} (93%) diff --git a/components/common/SearchBar.tsx b/components/common/TextInputBar.tsx similarity index 93% rename from components/common/SearchBar.tsx rename to components/common/TextInputBar.tsx index 714a3d6..c276b68 100644 --- a/components/common/SearchBar.tsx +++ b/components/common/TextInputBar.tsx @@ -9,7 +9,7 @@ interface SearchBarProps extends ViewProps { onChangeText? : (text: string) => void | undefined } -export default function SearchBar(props: SearchBarProps) { +export default function TextInputBar(props: SearchBarProps) { const [isActive, setIsactive] = React.useState(false); const { colors } = useTheme(); const [text, setText] = useState(""); @@ -43,7 +43,7 @@ export default function SearchBar(props: SearchBarProps) { // changed styles.container to containerStyle return ( - + {isActive && {handleChange("")}}> diff --git a/components/index.tsx b/components/index.tsx index c717b9e..56dc86c 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -5,11 +5,13 @@ import ExpenseItem from "./home/expenseItem" //common import CustomCard from "./common/CustomCard" -import SearchBar from "./common/SearchBar" import NavigationButton from "./common/button" import LoadingSymbol from "./common/loadingSymbol" import Plus from "./common/plus" +import TextInputBar from "./common/TextInputBar" import AutoDecimalInput from "./common/AutoDecimalInput" +import CategorySelector from "./common/CategorySelector" +import CategorySelectorModal from "./common/CategorySelectorModal" //login import BudgetHeader from "./budget/budgetHeader" @@ -21,8 +23,8 @@ import TypeSelectorSwitch from "./budget/typeSelectorSwitch" import Input from "./login/input" export { - BudgetHeader, ButtonSetting, CategoryItem, CustomCard, CustomColorPicker, ExpenseItem, Input, - LoadingSymbol, NavigationButton, Plus, - SearchBar, ToggleSetting, TypeSelectorSwitch, Welcome, AutoDecimalInput + BudgetHeader, ButtonSetting, CustomCard, ExpenseItem, Input, + LoadingSymbol, Plus, + TextInputBar, ToggleSetting, Welcome, AutoDecimalInput } From 36679279c1bbfd81aa916bb8195eeb070516ef2a Mon Sep 17 00:00:00 2001 From: Jakob Stornig Date: Fri, 5 Jan 2024 00:13:56 +0100 Subject: [PATCH 06/45] feat: Add expense screen --- app/(tabs)/home/_layout.tsx | 4 +- app/(tabs)/home/addItem.tsx | 75 ++++++++++++++- app/(tabs)/home/index.tsx | 14 ++- components/budget/budgetHeader.tsx | 4 +- components/common/AutoDecimalInput.tsx | 9 +- components/common/CategoryListItem.tsx | 66 +++++++++++++ components/common/CustomCard.tsx | 5 +- components/common/RoundedButton.tsx | 32 +++++++ components/common/TextInputBar.tsx | 8 +- components/home/addItem/CategorySelector.tsx | 60 ++++++++++++ .../home/addItem/CategorySelectorModal.tsx | 93 +++++++++++++++++++ .../home/addItem/DateSelectorButton.tsx | 38 ++++++++ components/home/expenseItem.tsx | 51 +++++----- components/index.tsx | 30 +++++- package-lock.json | 9 ++ package.json | 3 +- services/DebugMenu.tsx | 8 +- types/dbItems.ts | 7 ++ 18 files changed, 459 insertions(+), 57 deletions(-) create mode 100644 components/common/CategoryListItem.tsx create mode 100644 components/common/RoundedButton.tsx create mode 100644 components/home/addItem/CategorySelector.tsx create mode 100644 components/home/addItem/CategorySelectorModal.tsx create mode 100644 components/home/addItem/DateSelectorButton.tsx create mode 100644 types/dbItems.ts diff --git a/app/(tabs)/home/_layout.tsx b/app/(tabs)/home/_layout.tsx index 42d452e..abe83bf 100644 --- a/app/(tabs)/home/_layout.tsx +++ b/app/(tabs)/home/_layout.tsx @@ -12,10 +12,10 @@ export default function _Layout() { initialRouteName="index" screenOptions={{ contentStyle: { - backgroundColor:colors.backgroundColor, + backgroundColor:colors.containerColor, }, headerStyle: { - backgroundColor: colors.backgroundColor + backgroundColor: colors.containerColor }, headerTintColor: colors.primaryText diff --git a/app/(tabs)/home/addItem.tsx b/app/(tabs)/home/addItem.tsx index d403a23..d7d24f2 100644 --- a/app/(tabs)/home/addItem.tsx +++ b/app/(tabs)/home/addItem.tsx @@ -1,26 +1,93 @@ -import { View, Text, StyleSheet, TextInput, NativeSyntheticEvent, TextInputKeyPressEventData, TouchableOpacity } from 'react-native' +import { View, Text, StyleSheet, Alert } from 'react-native' import React, { useRef, useState } from 'react' import { SIZES } from '../../../constants/theme' import { useTheme } from '../../contexts/ThemeContext' -import { AutoDecimalInput } from '../../../components' +import { AutoDecimalInput, CategorySelector, CategorySelectorModal, DateSelectorButton, RoundedButton, TextInputBar } from '../../../components' +import { Category } from '../../../types/dbItems' +import DateTimePicker from '@react-native-community/datetimepicker'; +import { addExpense } from '../../../services/database' +import { SimpleDate } from '../../../util/SimpleDate' +import { useRouter } from 'expo-router' export default function AddItem() { - const value = useRef(""); + const {colors} = useTheme(); + const router = useRouter(); + const [formatedValue, setFormatedValue] = useState(""); + const [selectorModalVisible, setSelecorModalVisible] = useState(false); + const [selectedCategory, setSelectedCategory] = useState() + const [expenseName, setExpenseName] = useState(""); + const [datePickerShown, setDatePickerShown] = useState(false); + const [selectedDate, setSelectedDate] = useState(new Date()) const handleValueChange = (formatedValue: string) => { setFormatedValue(formatedValue); } - console.log(formatedValue) + + const handleCategorySelect = (category : Category) => { + setSelecorModalVisible(false); + setSelectedCategory(category); + } + + const validateInput = ():boolean => { + if(formatedValue == "" || expenseName == "" || selectedCategory === undefined || selectedDate === null){ + return false; + } + return true; + } + + const submit = () => { + const insert = async () => { + await addExpense(expenseName, selectedCategory?.guid!, new SimpleDate(selectedDate).format("YYYY-MM-DD"), Number(formatedValue)) + } + if(validateInput()){ + insert(); + router.back(); + }else { + Alert.alert("Invalid input", "One of the Props is not properly defined") + } + + } + return ( + {setSelecorModalVisible(false)}} onCategoryTap={handleCategorySelect}> + {setSelecorModalVisible(true)}} selectedCategory={selectedCategory}/> + setExpenseName(text)}/> + {setDatePickerShown(true)}}/> + {datePickerShown && + { + setDatePickerShown(false); + if(date){ + setSelectedDate(date); + } + }} + />} + + Save + ) } const styles = StyleSheet.create({ + save: { + marginTop: 40, + padding: 10, + }, container: { margin: SIZES.normal, + display: "flex", + gap: 10 + }, + + submitText: { + fontSize: SIZES.large } + }) \ No newline at end of file diff --git a/app/(tabs)/home/index.tsx b/app/(tabs)/home/index.tsx index edd6794..5a0a5a8 100644 --- a/app/(tabs)/home/index.tsx +++ b/app/(tabs)/home/index.tsx @@ -1,9 +1,9 @@ import React, { useRef, useState, useMemo } from 'react'; import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native'; import { Calendar } from 'react-native-calendars'; -import { FlatList } from 'react-native-gesture-handler'; +import { FlatList, RefreshControl } from 'react-native-gesture-handler'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { ExpenseItem, LoadingSymbol, Plus, SearchBar, Welcome } from '../../../components'; +import { ExpenseItem, LoadingSymbol, Plus, TextInputBar, Welcome } from '../../../components'; import useFetch from '../../../hooks/useFetch'; import { addExpense, executeQuery } from "../../../services/database"; @@ -63,7 +63,7 @@ export default function Page() { } } - const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, c.guid AS category_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color, c.type AS category_type FROM expense e JOIN category c ON e.category_guid = c.guid;", args: []}); + const {data, isLoading, reFetch} = useFetch({sql: "SELECT e.guid AS expense_guid, c.guid AS category_guid, e.name AS expense_name, c.name AS category_name, e.datetime AS expense_datetime, e.amount AS expense_amount, c.color AS category_color, c.type AS category_type FROM expense e JOIN category c ON e.category_guid = c.guid ORDER BY expense_datetime desc;", args: []}); const expenseDates = useMemo(()=> constructMarkedDates(data) @@ -92,7 +92,7 @@ export default function Page() { ListHeaderComponent={ <> {router.push("/home/userSettings")}}/> - - + } renderItem = {({item}) => } @@ -114,6 +114,9 @@ export default function Page() { ItemSeparatorComponent={() => { return (); }} + refreshControl={ + + } onScroll={handleScroll} scrollEventThrottle={20} /> @@ -124,6 +127,7 @@ export default function Page() { const styles = StyleSheet.create({ safeAreaViewStyle: { flex: 1, + paddingHorizontal: 10 }, itemSeperatorStyle: { marginVertical: 5, diff --git a/components/budget/budgetHeader.tsx b/components/budget/budgetHeader.tsx index eed9624..7fe8f01 100644 --- a/components/budget/budgetHeader.tsx +++ b/components/budget/budgetHeader.tsx @@ -1,5 +1,5 @@ import { StyleSheet, Text, TouchableHighlight, View } from "react-native"; -import SearchBar from "../common/SearchBar"; +import TextInputBar from "../common/TextInputBar"; import { useTheme } from "../../app/contexts/ThemeContext"; type BudgetHeaderProperties = { @@ -34,7 +34,7 @@ const BudgetHeader = (properties: BudgetHeaderProperties) => { }} /> - + ); } diff --git a/components/common/AutoDecimalInput.tsx b/components/common/AutoDecimalInput.tsx index e56aaa8..52821fc 100644 --- a/components/common/AutoDecimalInput.tsx +++ b/components/common/AutoDecimalInput.tsx @@ -1,6 +1,5 @@ import { View, Text, TouchableOpacity, TextInput, StyleSheet, NativeSyntheticEvent, TextInputKeyPressEventData } from 'react-native' import React, {LegacyRef, MutableRefObject, useRef, useState} from 'react' -import colors from '../../constants/colors'; import { SIZES } from '../../constants/theme'; import { useTheme } from '../../app/contexts/ThemeContext'; @@ -69,17 +68,15 @@ const AutoDecimalInput: React.FC = ({onValueChange, label } const styles = StyleSheet.create({ - container: { - margin: SIZES.normal, - }, inputContainer: { + minHeight: 50, borderRadius: 20, flexDirection: "row", - justifyContent: 'space-between' + justifyContent: 'space-between', + alignItems: "center" }, text:{ fontSize: SIZES.large, - marginVertical: 12, marginHorizontal: 15, }, currency: { diff --git a/components/common/CategoryListItem.tsx b/components/common/CategoryListItem.tsx new file mode 100644 index 0000000..3d432a6 --- /dev/null +++ b/components/common/CategoryListItem.tsx @@ -0,0 +1,66 @@ +import { StyleSheet, Text, View, Pressable } from 'react-native' +import React from 'react' +import { Category } from '../../types/dbItems' +import CustomCard from './CustomCard'; +import { SIZES } from '../../constants/theme'; +import { useTheme } from '../../app/contexts/ThemeContext'; + +interface CategoryListItemProps{ + category: Category; + onPress?: (category: Category) =>void | undefined +} + + + +const CategoryListItem: React.FC = (props: CategoryListItemProps) => { + const {category, onPress} = props; + const {colors} = useTheme(); + + const handlePress = ()=>{ + if(onPress){ + onPress(category); + } + } + + return ( + + + + + {category.name ?? "#noData#"} + + + + + ) +} + +export default CategoryListItem + +const styles = StyleSheet.create({ + tile: { + height: 60, + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + borderRadius: 20 + }, + colorTip:{ + height: "100%", + width: 30, + borderTopLeftRadius:20, + borderBottomLeftRadius: 20 + }, + textWrapper: { + + }, + tileTail:{ + height: "100%", + width: 30, + borderTopRightRadius:20, + borderBottomRightRadius: 20 + }, + text: { + fontSize: SIZES.large, + } +}) \ No newline at end of file diff --git a/components/common/CustomCard.tsx b/components/common/CustomCard.tsx index 5358d51..a866d8c 100644 --- a/components/common/CustomCard.tsx +++ b/components/common/CustomCard.tsx @@ -39,10 +39,7 @@ export default function CustomCard(props : ViewProps) { const styles = StyleSheet.create({ container:{ - flexDirection: "row", - alignItems: "stretch", - alignContent: "space-between", - borderRadius: 10, + borderRadius: 20, marginHorizontal: 10, }, boxShadow: {}, diff --git a/components/common/RoundedButton.tsx b/components/common/RoundedButton.tsx new file mode 100644 index 0000000..1852212 --- /dev/null +++ b/components/common/RoundedButton.tsx @@ -0,0 +1,32 @@ +import { StyleSheet, Text, View, ViewProps, TouchableOpacity } from 'react-native' +import React from 'react' +import { SIZES } from '../../constants/theme'; + +interface RoundedButtonProps extends ViewProps{ + onPress?: ()=> void | undefined; + color: string; +} + +const RoundedButton: React.FC = (props: RoundedButtonProps) => { + const {onPress, color, style, ...restProps} = props; + return ( + + + {restProps.children} + + + ) +} + +export default RoundedButton + +const styles = StyleSheet.create({ + btn:{ + justifyContent: 'center', + alignItems: 'center', + borderRadius: 80 + }, + text: { + fontSize: SIZES.normal + } +}) \ No newline at end of file diff --git a/components/common/TextInputBar.tsx b/components/common/TextInputBar.tsx index c276b68..a2ffa84 100644 --- a/components/common/TextInputBar.tsx +++ b/components/common/TextInputBar.tsx @@ -42,12 +42,12 @@ export default function TextInputBar(props: SearchBarProps) { //TODO: Handle textCancel // changed styles.container to containerStyle return ( - - + + handleChange(text)} onEndEditing={()=>setIsactive(false)}/> {isActive && {handleChange("")}}> - + } @@ -56,8 +56,6 @@ export default function TextInputBar(props: SearchBarProps) { const styles = StyleSheet.create({ container: { - marginHorizontal: 10, - marginBottom: 20, flexDirection: 'row', justifyContent: "center", alignItems: "center", diff --git a/components/home/addItem/CategorySelector.tsx b/components/home/addItem/CategorySelector.tsx new file mode 100644 index 0000000..27d5cfe --- /dev/null +++ b/components/home/addItem/CategorySelector.tsx @@ -0,0 +1,60 @@ +import { StyleSheet, Text, View, TouchableOpacity } from 'react-native' +import React, { useState } from 'react' +import { useTheme } from '../../../app/contexts/ThemeContext' +import { SIZES } from '../../../constants/theme'; +import { Category } from '../../../types/dbItems'; +import CategorySelectorModal from './CategorySelectorModal'; + +interface CategorySelectorProps { + onPress? : () => void | undefined; + selectedCategory? : Category; +} + +const CategorySelector: React.FC = (props : CategorySelectorProps) => { + const {colors} = useTheme(); + + + return ( + <> + + + + + + {props.selectedCategory?.name ?? "Tap to select Categroy"} + + + + + ) +} + +export default CategorySelector + +const styles = StyleSheet.create({ + tile: { + height: 60, + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + borderRadius: 20 + }, + colorTip:{ + height: "100%", + width: 30, + borderTopLeftRadius:20, + borderBottomLeftRadius: 20 + }, + textWrapper: { + + }, + tileTail:{ + height: "100%", + width: 30, + borderTopRightRadius:20, + borderBottomRightRadius: 20 + }, + text: { + fontSize: SIZES.large, + } +}) \ No newline at end of file diff --git a/components/home/addItem/CategorySelectorModal.tsx b/components/home/addItem/CategorySelectorModal.tsx new file mode 100644 index 0000000..fb61c10 --- /dev/null +++ b/components/home/addItem/CategorySelectorModal.tsx @@ -0,0 +1,93 @@ +import { Modal, NativeSyntheticEvent, StyleSheet, Text, View, FlatList } from 'react-native' +import React, { useEffect, useMemo, useState } from 'react' +import { Category } from '../../../types/dbItems'; +import { useTheme } from '../../../app/contexts/ThemeContext'; +import CategoryListItem from '../../common/CategoryListItem'; +import { SIZES } from '../../../constants/theme'; +import useFetch from '../../../hooks/useFetch'; +import TextInputBar from '../../common/TextInputBar'; + + +interface CategorySelectorModalProps{ + visible: boolean; + onCategoryTap?: (category : Category) => void | undefined; + selectMulitple?: boolean; + onRequestClose?: ((event: NativeSyntheticEvent) => void) | undefined; +} + +//TODO: select Multiple + +const CategorySelectorModal: React.FC = (props : CategorySelectorModalProps) => { + const {visible, onCategoryTap, selectMulitple} = props; + const {data, reFetch} = useFetch({sql: "SELECT * FROM category;", args:[]}); + const {colors} = useTheme(); + const [searchtext, setSearchtext] = useState(""); + + const handleSearchText = (text : string) => { + setSearchtext(text); + } + + const categories = useMemo(()=>{ + return data.map((elem) => { + return {name: elem["name"], color: elem["color"], guid: elem["guid"]} + }) + }, [data]) + + const filteredCategories = categories.filter((category) => category.name?.toLowerCase().includes(searchtext.toLowerCase())) + + useEffect(()=>{ + if(visible){ + //reFetch(); Uncomment if newly added categories do not appear + handleSearchText(""); + } + }, [visible]) + + return ( + + + + + {selectMulitple ? "Categories" : "Category"} + + + item.guid!} + renderItem={({item})=> } + ItemSeparatorComponent={() => } + ListFooterComponent={() => } + keyboardShouldPersistTaps="always" + > + + + + + + ) +} + +export default CategorySelectorModal + +const styles = StyleSheet.create({ + main: { + backgroundColor: 'rgba(0, 0, 0, 0.5)', + height: "100%", + width: "100%", + alignItems: "center", + }, + modal: { + height: "70%", + width: "90%", + top: "10%", + borderRadius: 30, + paddingVertical: 20, + paddingHorizontal: 20 + }, + heading: { + fontSize: SIZES.xlarge, + fontWeight: "bold" + }, + itemSeperatorStyle: { + paddingBottom: 10 + } +}) \ No newline at end of file diff --git a/components/home/addItem/DateSelectorButton.tsx b/components/home/addItem/DateSelectorButton.tsx new file mode 100644 index 0000000..4f07297 --- /dev/null +++ b/components/home/addItem/DateSelectorButton.tsx @@ -0,0 +1,38 @@ +import { View, Text, StyleSheet, TouchableOpacity, TouchableOpacityProps } from 'react-native' +import React, { useState } from 'react' +import { useTheme } from '../../../app/contexts/ThemeContext'; +import { SIZES } from '../../../constants/theme'; +import { SimpleDate } from '../../../util/SimpleDate'; +import { ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes'; + +interface DateSelectorProps extends ViewProps { + onPress?: ()=>void | undefined; + selectedDate: Date +} + +const DateSelectorButton:React.FC = (props: DateSelectorProps) => { + const {onPress, selectedDate, ...restProps} = props; + const {colors} = useTheme(); + + return ( + + Date: + {new SimpleDate(selectedDate).format("DD.MM.YYYY")} + + ) +} +const styles = StyleSheet.create({ + inputContainer: { + minHeight: 50, + borderRadius: 20, + flexDirection: "row", + justifyContent: 'space-between', + alignItems: "center" + }, + text:{ + fontSize: SIZES.large, + marginHorizontal: 15, + }, +}) + +export default DateSelectorButton \ No newline at end of file diff --git a/components/home/expenseItem.tsx b/components/home/expenseItem.tsx index d541d48..ade5ccd 100644 --- a/components/home/expenseItem.tsx +++ b/components/home/expenseItem.tsx @@ -9,28 +9,29 @@ export default function ExpenseItem(itemProps : ExpenseItemProps) { const { colors } = useTheme(); return ( - - - {itemProps.category} - {itemProps.title} - {itemProps.date} - - - {itemProps.value} - - + + + + {itemProps.category} + {itemProps.title} + {itemProps.date} + + + {itemProps.value} + + ) @@ -42,6 +43,12 @@ const styles = StyleSheet.create({ borderTopLeftRadius: 20, borderBottomLeftRadius: 20, }, + + tile: { + flexDirection: "row", + alignItems: "stretch", + alignContent: "space-between", + }, textSection: { flexDirection: "column", diff --git a/components/index.tsx b/components/index.tsx index 56dc86c..b20c5fd 100644 --- a/components/index.tsx +++ b/components/index.tsx @@ -3,6 +3,11 @@ import { ButtonSetting, ToggleSetting } from "./home/Setting" import Welcome from "./home/Welcome" import ExpenseItem from "./home/expenseItem" +//home/addItem +import CategorySelector from "./home/addItem/CategorySelector" +import CategorySelectorModal from "./home/addItem/CategorySelectorModal" +import DateSelectorButton from "./home/addItem/DateSelectorButton" + //common import CustomCard from "./common/CustomCard" import NavigationButton from "./common/button" @@ -10,8 +15,7 @@ import LoadingSymbol from "./common/loadingSymbol" import Plus from "./common/plus" import TextInputBar from "./common/TextInputBar" import AutoDecimalInput from "./common/AutoDecimalInput" -import CategorySelector from "./common/CategorySelector" -import CategorySelectorModal from "./common/CategorySelectorModal" +import RoundedButton from "./common/RoundedButton" //login import BudgetHeader from "./budget/budgetHeader" @@ -23,8 +27,24 @@ import TypeSelectorSwitch from "./budget/typeSelectorSwitch" import Input from "./login/input" export { - BudgetHeader, ButtonSetting, CustomCard, ExpenseItem, Input, - LoadingSymbol, Plus, - TextInputBar, ToggleSetting, Welcome, AutoDecimalInput + BudgetHeader, + ButtonSetting, + CustomCard, + ExpenseItem, + Input, + LoadingSymbol, + Plus, + TextInputBar, + ToggleSetting, + Welcome, + AutoDecimalInput, + CategorySelector, + CategorySelectorModal, + DateSelectorButton, + RoundedButton, + CategoryItem, + TypeSelectorSwitch, + NavigationButton, + CustomColorPicker } diff --git a/package-lock.json b/package-lock.json index 16b6032..aab706f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@expo/vector-icons": "^13.0.0", "@react-native-async-storage/async-storage": "1.18.2", + "@react-native-community/datetimepicker": "7.2.0", "@react-navigation/native": "^6.0.2", "expo": "~49.0.15", "expo-font": "~11.4.0", @@ -6275,6 +6276,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/@react-native-community/datetimepicker": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-7.2.0.tgz", + "integrity": "sha512-dO1sQy83M/EvnHE2egto05iwXZX7EYn5f/VDMp6afZFRFXRiRo7CzB3VFg4B55gJRJMNBv06NYMLPM3SlpnEGQ==", + "dependencies": { + "invariant": "^2.2.4" + } + }, "node_modules/@react-native/assets-registry": { "version": "0.72.0", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.72.0.tgz", diff --git a/package.json b/package.json index abb4142..2e627b2 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "react-native-screens": "~3.22.0", "react-native-uuid": "^2.0.1", "react-native-web": "~0.19.6", - "reanimated-color-picker": "^2.4.2" + "reanimated-color-picker": "^2.4.2", + "@react-native-community/datetimepicker": "7.2.0" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/services/DebugMenu.tsx b/services/DebugMenu.tsx index 896a667..29ce4e8 100644 --- a/services/DebugMenu.tsx +++ b/services/DebugMenu.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { View, Button, Alert } from 'react-native'; -import { addCategory, addExpense, deleteExpenses, deleteCategories, DEV_populateDatabase } from './database'; +import { addCategory, addExpense, deleteExpenses, deleteCategories, DEV_populateDatabase, deleteDatabase } from './database'; import uuid from 'react-native-uuid'; const randomColors = ["red", "blue", "green", "purple", "yellow"]; @@ -19,6 +19,11 @@ const getRandomNumber = () => { const DebugMenu = () => { + const deleteDBFile = () => { + console.warn("Deleting DB. App Restart is required") + return deleteDatabase(); + } + const handleNukeDatabase = () => { return deleteExpenses(), deleteCategories() }; @@ -60,6 +65,7 @@ const DebugMenu = () => { return ( +