From 432d1c8426c27670ec307753d4ffbd3bbef8053f Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Fri, 17 Nov 2023 09:51:02 +0100 Subject: [PATCH 1/8] Balance screen done + new helper function --- app/(tabs)/expenses/balance.tsx | 72 +++++++++++++++++++++++++-------- helpers/calculateBalance.tsx | 29 +++++++++++++ 2 files changed, 84 insertions(+), 17 deletions(-) create mode 100644 helpers/calculateBalance.tsx diff --git a/app/(tabs)/expenses/balance.tsx b/app/(tabs)/expenses/balance.tsx index 96f3a02..a92913e 100644 --- a/app/(tabs)/expenses/balance.tsx +++ b/app/(tabs)/expenses/balance.tsx @@ -1,32 +1,70 @@ import { StyleSheet } from 'react-native'; +import { Text, View, } from '../../../components/Themed'; +import { FlatList } from 'react-native-gesture-handler'; +import { calculateBalance } from '../../../helpers/calculateBalance'; + + +type Expense = { + user: string; + amount: number; +}; + +const DATA: Expense[] = [ + { user: 'Martin', amount: 90 }, + { user: 'Andreas', amount: 30 }, + { user: 'Bisgaard', amount: 0 }, + { user: 'Mike', amount: 85 }, +]; + +const calculatedBalances = calculateBalance(DATA); -import EditScreenInfo from '../../../components/EditScreenInfo'; -import { Text, View } from '../../../components/Themed'; export default function BalanceScreen() { + + const renderItem = ({ item, index }: { item: Expense; index: number }) => { + return ( + + + {item.user} + {item.amount.toFixed(2)} kr. + + + ); + }; + return ( - - Balance - - + + ); } const styles = StyleSheet.create({ container: { - marginTop: 48, - flex: 1, - alignItems: 'center', - justifyContent: 'center', + borderBottomColor: '#000000', + padding: 2, + }, + item: { + backgroundColor: 'transparent', + borderBottomColor: '#000000', + borderBottomWidth: 0, + padding: 12, + flexDirection: 'row', + justifyContent: 'space-between' }, - title: { - fontSize: 20, - fontWeight: 'bold', + itemText: { + fontSize: 24, + color: 'black', + flexDirection: 'row', }, - separator: { - marginVertical: 30, - height: 1, - width: '80%', + itemAmount: { + fontSize: 24, + color: 'black', + flexDirection: 'row', + justifyContent: 'flex-end' }, }); diff --git a/helpers/calculateBalance.tsx b/helpers/calculateBalance.tsx new file mode 100644 index 0000000..69184b2 --- /dev/null +++ b/helpers/calculateBalance.tsx @@ -0,0 +1,29 @@ +type Expense = { + user: string; + amount: number; +}; + +export const calculateBalance = (expenses: Expense[]) => { + const totalSpent: { [name: string]: number } = {}; + const userCount: { [name: string]: number } = {}; + expenses.forEach((expense) => { + totalSpent[expense.user] = (totalSpent[expense.user] || 0) + expense.amount; + userCount[expense.user] = (userCount[expense.user] || 0) + 1; + }); + + const users = Object.keys(totalSpent); + const totalUsers = users.length; + const averageAmount = users.reduce((acc, user) => acc + (totalSpent[user] / userCount[user]), 0) / totalUsers; + + const calculatedBalances: { [name: string]: number } = {}; + users.forEach((user) => { + calculatedBalances[user] = parseFloat(((totalSpent[user] / userCount[user]) - averageAmount).toFixed(2)); + }); + + const calculatedBalancesArray: Expense[] = Object.entries(calculatedBalances).map(([user, amount]) => ({ + user, + amount, + })); + + return calculatedBalancesArray; +}; \ No newline at end of file From 753f4444d1b4343cc5696343ea34a94b275e876a Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Fri, 17 Nov 2023 10:37:48 +0100 Subject: [PATCH 2/8] Fixed display styling for the numbers green if positive, red if negative, black if zero. Also "+" is added for positive numbers. --- app/(tabs)/expenses/balance.tsx | 5 +++-- helpers/calculateBalance.tsx | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/(tabs)/expenses/balance.tsx b/app/(tabs)/expenses/balance.tsx index a92913e..331eb75 100644 --- a/app/(tabs)/expenses/balance.tsx +++ b/app/(tabs)/expenses/balance.tsx @@ -26,7 +26,9 @@ export default function BalanceScreen() { {item.user} - {item.amount.toFixed(2)} kr. + = 0 ? (item.amount == 0 ? 'black' : '#5CBCA9') : '#E35F52' }]}> + {item.amount > 0 ? "+" + item.amount.toFixed(2) : item.amount.toFixed(2)} kr. + ); @@ -63,7 +65,6 @@ const styles = StyleSheet.create({ }, itemAmount: { fontSize: 24, - color: 'black', flexDirection: 'row', justifyContent: 'flex-end' }, diff --git a/helpers/calculateBalance.tsx b/helpers/calculateBalance.tsx index 69184b2..ea2b227 100644 --- a/helpers/calculateBalance.tsx +++ b/helpers/calculateBalance.tsx @@ -6,9 +6,10 @@ type Expense = { export const calculateBalance = (expenses: Expense[]) => { const totalSpent: { [name: string]: number } = {}; const userCount: { [name: string]: number } = {}; - expenses.forEach((expense) => { - totalSpent[expense.user] = (totalSpent[expense.user] || 0) + expense.amount; - userCount[expense.user] = (userCount[expense.user] || 0) + 1; + + expenses.forEach(({ user, amount }) => { + totalSpent[user] = (totalSpent[user] || 0) + amount; + userCount[user] = (userCount[user] || 0) + 1; }); const users = Object.keys(totalSpent); @@ -24,6 +25,6 @@ export const calculateBalance = (expenses: Expense[]) => { user, amount, })); - + return calculatedBalancesArray; }; \ No newline at end of file From c9252d74a6766fbff04c61e1f2e16ead24b4f043 Mon Sep 17 00:00:00 2001 From: UnluckyBird Date: Fri, 17 Nov 2023 13:27:41 +0100 Subject: [PATCH 3/8] Added add and remove items to list view - Add items - Remove items - Small compatibility with dark mode changes --- app/(tabs)/(index)/_layout.tsx | 6 +- app/(tabs)/(index)/index.tsx | 294 ++++- app/(tabs)/_layout.tsx | 6 +- app/(tabs)/expenses/_layout.tsx | 6 +- app/(tabs)/notice.tsx | 5 +- babel.config.js | 1 + constants/Colors.ts | 4 + package-lock.json | 2032 ++++++++++++++++++++++++++++--- package.json | 9 +- 9 files changed, 2133 insertions(+), 230 deletions(-) diff --git a/app/(tabs)/(index)/_layout.tsx b/app/(tabs)/(index)/_layout.tsx index 7623a81..1ba923a 100644 --- a/app/(tabs)/(index)/_layout.tsx +++ b/app/(tabs)/(index)/_layout.tsx @@ -3,14 +3,14 @@ import { useColorScheme } from "react-native"; import Colors from "../../../constants/Colors"; export default function ListTabLayout() { - const colorScheme = useColorScheme(); + const colorScheme = useColorScheme() ?? 'light'; return ( diff --git a/app/(tabs)/(index)/index.tsx b/app/(tabs)/(index)/index.tsx index 95bc2a0..eed50d6 100644 --- a/app/(tabs)/(index)/index.tsx +++ b/app/(tabs)/(index)/index.tsx @@ -1,56 +1,53 @@ -import { StyleSheet } from 'react-native'; +import { Button, Pressable, StyleSheet, TextInput, useColorScheme } from 'react-native'; +import Modal from "react-native-modal"; import { Text, View, } from '../../../components/Themed'; -import { Component } from 'react'; +import { useState } from 'react'; import { FlatList } from 'react-native-gesture-handler'; import { FontAwesome5 } from '@expo/vector-icons'; +import CheckBox from 'expo-checkbox'; import Swipeable from 'react-native-gesture-handler/Swipeable'; +import Colors from '../../../constants/Colors'; +import ActionButton from 'react-native-action-button'; +import { MultiSelect } from 'react-native-element-dropdown'; +import moment from 'moment'; +import { LogBox } from 'react-native'; -const DATA = [ +LogBox.ignoreLogs(['Warning: componentWillReceiveProps has been renamed']); + +const usersDropdown = [ + { label: 'Test bruger', value: 'Test bruger' }, + { label: 'Andreas', value: 'Andreas' }, + { label: 'Mike', value: 'Mike' }, + { label: 'Emil', value: 'Emil' }, + { label: 'Martin', value: 'Martin' }, +]; + +const seedData : ListData[] = [ { name: 'Bananas', - data: [{user: "Test bruger", date: "2020.11.01", price: 3}], + data: {added: {user: "Test bruger", date: "2020.11.01"}, users: [], bought: {user: "Test bruger", date: "2020.11.01", price: 3}}, }, { name: 'Chorizo', - data: [{user: "Andreas", date: "2020.11.01", price: 35}], + data: {added: {user: "Andreas", date: "2020.11.01"}, users: [], bought: {user: "Andreas", date: "2020.11.01", price: 35}}, }, { name: 'Beans', - data: [{user: "Mike", date: "2020.11.01", price: 2}], + data: {added: {user: "Mike", date: "2020.11.01"}, users: [], bought: {user: "Mike", date: "2020.11.01", price: 2}}, }, { name: 'Apple', - data: [{user: "Emil", date: "2020.11.01", price: 2.5}], + data: {added: {user: "Emil", date: "2020.11.01"}, users: [], bought: {user: "Emil", date: "2020.11.01", price: 2.5}}, }, { name: 'Chocolate', - data: [{user: "Martin", date: "2020.11.01", price: 13.95}], + data: {added: {user: "Martin", date: "2020.11.01"}, users: []}, }, ] type ListData = { - name: string, - data: { user: string, date: string, price: number }[] -} - -function renderItem({item, index}: { item: ListData, index: number}) { - return ( - - - - {item.name} - {item.data[0].price} kr. - - Added by {item.data[0].user} {item.data[0].date} - - - ) + name: string, + data: {added: { user: string, date: string}, users: { name: string }[], bought?: { user: string, date: string, price: number}} } function leftSwipeAction() { @@ -79,24 +76,187 @@ function rightSwipeAction() { ) } -function swipeHandler(dir: 'left' | 'right') { - if (dir == "left") { - console.log(dir); - } else { - console.log(dir); +export default function ToBeBoughtScreen() { + const colorScheme = useColorScheme() ?? 'light'; + const [isModalAddItemVisible, setIsModalAddItemVisible] = useState(false); + const [isModalDeleteVisible, setIsModalDeleteVisible] = useState(false); + const [products, setProducts] = useState(seedData) + + const [productName, setProductName] = useState(''); + const [selectedUsers, setSelectedUsers] = useState(usersDropdown.map((user) => user.value)); + const [alreadyBought, setAlreadyBought] = useState(false); + const [price, setPrice] = useState('0'); + + const [itemToDelete, setItemToDelete] = useState(0) + + const swipeableRows : Swipeable[] = [] + + const handleModalAddItem = () => setIsModalAddItemVisible(() => !isModalAddItemVisible); + const handleModalDelete = () => setIsModalDeleteVisible(() => !isModalDeleteVisible); + const handleCheckbox = () => setAlreadyBought(() => !alreadyBought); + const clearProduct = () => { + setProductName('') + setSelectedUsers(usersDropdown.map((user) => user.value)) + setAlreadyBought(false) + setPrice('0') + } + const addProduct = () => { + const time = moment().format('YYYY.MM.DD'); + const newProduct = { + name: productName, + data: { + added: {user: "Me", date: time}, + users: selectedUsers.map(user => ({name: user})), + bought: alreadyBought ? {user: "Me", date: time, price: Number(price)} : undefined + } + } + if(newProduct.data.bought === undefined){ + //Add to GUN here + console.log(newProduct.name + ' added') + } + else{ + //Move to GUN bought list here + console.log(newProduct.name + ' bought by ' + newProduct.data.bought.user) + } + setProducts(products => [...products, newProduct]) + + handleModalAddItem() + clearProduct() + } + + function swipeHandler(dir: 'left' | 'right', index: number) { + if (dir == "left") { + console.log(dir); + } else { + setItemToDelete(index) + handleModalDelete() + } } -} -export default class ToBeBoughtScreen extends Component { - render() { + const renderButton = (text: 'Yes' | 'No') => { return ( + { + if (text === 'Yes') + { + //Remove from GUN here + setProducts(prevState => prevState.filter((_,i) => i !== itemToDelete)) + swipeableRows[itemToDelete].reset() + } + else + { + swipeableRows[itemToDelete].close() + } + setItemToDelete(0) + handleModalDelete() + }}> + {text} + + ); + }; + + function renderItem({item, index}: { item: ListData, index: number}) { + return ( + ref != null ? swipeableRows[index] = ref : undefined} + friction={1.5} + overshootFriction={8} + renderLeftActions={leftSwipeAction} + renderRightActions={rightSwipeAction} + onSwipeableOpen={(dir) => swipeHandler(dir, index)}> + + + {item.name} + + Added by {item.data.added.user} {item.data.added.date} + + + ) + } + + return ( + - ); - } + + + Product + setProductName(text)} + editable + /> + Split expenses between + { + setSelectedUsers(item); + }} + visibleSelectedItem={false} + /> + + setAlreadyBought(newValue)} + /> + Already bought + + {alreadyBought ? Amount paid : null} + {alreadyBought ? setPrice(text)} + editable + /> : null} + +