From fad2f5242e18fed879b8a24fcd532c4b2e89de23 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Fri, 10 Nov 2023 14:26:26 +0100 Subject: [PATCH 1/6] Initial commit for showing the calculateexpneses --- app/(tabs)/expenses/index.tsx | 127 +++++++++++++++++++++++++++++----- helpers/calculateExpenses.tsx | 54 +++++++-------- 2 files changed, 135 insertions(+), 46 deletions(-) diff --git a/app/(tabs)/expenses/index.tsx b/app/(tabs)/expenses/index.tsx index 4e3daad..0afc748 100644 --- a/app/(tabs)/expenses/index.tsx +++ b/app/(tabs)/expenses/index.tsx @@ -1,32 +1,121 @@ import { StyleSheet } from 'react-native'; +import { Text, View, } from '../../../components/Themed'; +import { Component } from 'react'; +import { FlatList } from 'react-native-gesture-handler'; +import { FontAwesome5 } from '@expo/vector-icons'; +import Swipeable from 'react-native-gesture-handler/Swipeable'; +import { calculateExpenses } from '../../../helpers/calculateExpenses'; -import EditScreenInfo from '../../../components/EditScreenInfo'; -import { Text, View } from '../../../components/Themed'; -export default function SettleScreen() { +type Expense = { + user: string; + amount: number; +}; + +type Transaction = { + from: string; + to: string; + amount: number; +}; + +const DATA: Expense[] = [ + { user: 'user 1', amount: 90 }, + { user: 'user 2', amount: 30 }, + { user: 'user 3', amount: 0 }, +]; + +const calculatedExpenses = calculateExpenses(DATA); + +function renderItem({item, index}: { item: Transaction, index: number}) { return ( - - Settle - - + + + + {item.from} + {item.amount} kr. + {item.to} + + + + ) +} + +function leftSwipeAction() { + return ( + + - ); + ) +} + +function swipeHandler(dir: 'left' | 'right') { + if (dir == "left") { + console.log(dir); + } else { + console.log(dir); + } +} + +export default class SettleScreen extends Component { + render() { + return ( + + ); + } } const styles = StyleSheet.create({ container: { - marginTop: 48, - flex: 1, - alignItems: 'center', - justifyContent: 'center', + borderBottomColor: '#000000', + padding: 2, }, - title: { - fontSize: 20, + item: { + backgroundColor: 'transparent', + borderBottomColor: '#000000', + borderBottomWidth: 0, + padding: 8, + flexDirection: 'row', + justifyContent: 'space-between' + }, + itemText: { + fontSize: 35, + color: 'black', + flexDirection: 'row', + }, + itemAmount: { + fontSize: 35, + color: 'black', + flexDirection: 'row', + justifyContent: 'flex-end' + }, + infoText: { + fontSize: 12, fontWeight: 'bold', + color: 'black', + marginLeft: 10, + marginBottom: 5, + marginTop: -5, }, - separator: { - marginVertical: 30, - height: 1, - width: '80%', + leftSwipe: { + backgroundColor: '#5CBCA9', + justifyContent: 'center', + alignItems: 'flex-end' }, -}); + swipeIcon: { + padding: 20, + } +}); \ No newline at end of file diff --git a/helpers/calculateExpenses.tsx b/helpers/calculateExpenses.tsx index c8babe3..c84ce79 100644 --- a/helpers/calculateExpenses.tsx +++ b/helpers/calculateExpenses.tsx @@ -1,5 +1,5 @@ type Expense = { - resident: string; + user: string; amount: number; }; @@ -10,34 +10,34 @@ type Transaction = { }; export const calculateExpenses = (expenses: Expense[]): Transaction[] => { - // Calculate total spent by each resident - const totalSpent: { [name: string]: number } = {}; - expenses.forEach((expense) => { - totalSpent[expense.resident] = (totalSpent[expense.resident] || 0) + expense.amount; - }); +// Calculate total spent by each user +const totalSpent: { [name: string]: number } = {}; +expenses.forEach((expense) => { + totalSpent[expense.user] = (totalSpent[expense.user] || 0) + expense.amount; +}); - // Calculate the average amount spent by all residents - const numResidents = Object.keys(totalSpent).length; - const totalAmountSpent = Object.values(totalSpent).reduce((total, amount) => total + amount, 0); - const averageAmount = totalAmountSpent / numResidents; +// Calculate the average amount spent by all users +const numUsers = Object.keys(totalSpent).length; +const totalAmountSpent = Object.values(totalSpent).reduce((total, amount) => total + amount, 0); +const averageAmount = totalAmountSpent / numUsers; - // Calculate who owes or is owed how much - const transactions: Transaction[] = []; - Object.keys(totalSpent).forEach((creditor) => { - let amountOwed = totalSpent[creditor] - averageAmount; +// Calculate who owes or is owed how much +const transactions: Transaction[] = []; +Object.keys(totalSpent).forEach((creditor) => { + let amountOwed = totalSpent[creditor] - averageAmount; - if (amountOwed > 0) { - Object.keys(totalSpent).forEach((debitor) => { - if (creditor !== debitor && totalSpent[debitor] < averageAmount) { - const settleAmount = Math.min(amountOwed, averageAmount - totalSpent[debitor]); - transactions.push({ to: creditor, from: debitor, amount: settleAmount }); - totalSpent[creditor] -= settleAmount; - totalSpent[debitor] += settleAmount; - amountOwed -= settleAmount; - } - }); - } - }); + if (amountOwed > 0) { + Object.keys(totalSpent).forEach((debitor) => { + if (creditor !== debitor && totalSpent[debitor] < averageAmount && averageAmount != totalSpent[creditor]) { + const settleAmount = Math.min(amountOwed, averageAmount - totalSpent[debitor]); + transactions.push({ to: creditor, from: debitor, amount: settleAmount }); + totalSpent[creditor] -= settleAmount; + totalSpent[debitor] += settleAmount; + amountOwed -= settleAmount; + } + }); + } +}); - return transactions; +return transactions; }; \ No newline at end of file From 46ab3663ea7a213484ceaab6a4847a2d1c2faf18 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Fri, 10 Nov 2023 15:03:37 +0100 Subject: [PATCH 2/6] Small adjustments --- app/(tabs)/expenses/index.tsx | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/app/(tabs)/expenses/index.tsx b/app/(tabs)/expenses/index.tsx index 0afc748..14c22c0 100644 --- a/app/(tabs)/expenses/index.tsx +++ b/app/(tabs)/expenses/index.tsx @@ -19,9 +19,9 @@ type Transaction = { }; const DATA: Expense[] = [ - { user: 'user 1', amount: 90 }, - { user: 'user 2', amount: 30 }, - { user: 'user 3', amount: 0 }, + { user: 'Martin', amount: 90 }, + { user: 'Andreas', amount: 30 }, + { user: 'Bisgaard', amount: 0 }, ]; const calculatedExpenses = calculateExpenses(DATA); @@ -87,35 +87,31 @@ const styles = StyleSheet.create({ backgroundColor: 'transparent', borderBottomColor: '#000000', borderBottomWidth: 0, - padding: 8, + padding: 12, flexDirection: 'row', justifyContent: 'space-between' }, itemText: { - fontSize: 35, + fontSize: 24, color: 'black', flexDirection: 'row', }, itemAmount: { - fontSize: 35, + fontSize: 24, color: 'black', flexDirection: 'row', justifyContent: 'flex-end' }, - infoText: { - fontSize: 12, - fontWeight: 'bold', - color: 'black', - marginLeft: 10, - marginBottom: 5, - marginTop: -5, - }, leftSwipe: { backgroundColor: '#5CBCA9', justifyContent: 'center', alignItems: 'flex-end' }, swipeIcon: { - padding: 20, + padding: 10, + }, + longBoi: { + flexDirection: 'row', + justifyContent: 'flex-end' } }); \ No newline at end of file From d81f1a46034b3fed1bedb6d8b5ac34ebc67dce6f Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Tue, 14 Nov 2023 11:08:41 +0100 Subject: [PATCH 3/6] Changed colors to indicate a "from to" relations --- app/(tabs)/expenses/index.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/(tabs)/expenses/index.tsx b/app/(tabs)/expenses/index.tsx index 14c22c0..4d1bef6 100644 --- a/app/(tabs)/expenses/index.tsx +++ b/app/(tabs)/expenses/index.tsx @@ -36,9 +36,9 @@ function renderItem({item, index}: { item: Transaction, index: number}) { - {item.from} + {item.from} {item.amount} kr. - {item.to} + {item.to} @@ -91,9 +91,14 @@ const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'space-between' }, - itemText: { + itemTextFrom: { fontSize: 24, - color: 'black', + color: '#E35F52', + flexDirection: 'row', + }, + itemTextTo: { + fontSize: 24, + color: '#5CBCA9', flexDirection: 'row', }, itemAmount: { From af1aa4dc4ffec9236f82f853eab79278b3c23435 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Wed, 15 Nov 2023 09:18:49 +0100 Subject: [PATCH 4/6] Testing Modal Functionality --- app/(tabs)/expenses/index.tsx | 80 +++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/app/(tabs)/expenses/index.tsx b/app/(tabs)/expenses/index.tsx index 4d1bef6..4a1be4b 100644 --- a/app/(tabs)/expenses/index.tsx +++ b/app/(tabs)/expenses/index.tsx @@ -1,4 +1,5 @@ -import { StyleSheet } from 'react-native'; +import { StyleSheet, Modal, Pressable, Alert } from 'react-native'; +import React, {useState} from 'react'; import { Text, View, } from '../../../components/Themed'; import { Component } from 'react'; import { FlatList } from 'react-native-gesture-handler'; @@ -26,6 +27,38 @@ const DATA: Expense[] = [ const calculatedExpenses = calculateExpenses(DATA); +function renderModal() { + const [modalVisible, setModalVisible] = useState(false); + return ( + + { + Alert.alert('Modal has been closed.'); + setModalVisible(!modalVisible); + }}> + + + Hello World! + setModalVisible(!modalVisible)}> + Hide Modal + + + + + setModalVisible(true)}> + Show Modal + + + ); +}; + function renderItem({item, index}: { item: Transaction, index: number}) { return ( ); } @@ -118,5 +151,46 @@ const styles = StyleSheet.create({ longBoi: { flexDirection: 'row', justifyContent: 'flex-end' - } + }, + centeredView: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + marginTop: 22, + }, + modalView: { + margin: 20, + backgroundColor: 'white', + borderRadius: 20, + padding: 35, + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 2, + }, + shadowOpacity: 0.25, + shadowRadius: 4, + elevation: 5, + }, + button: { + borderRadius: 20, + padding: 10, + elevation: 2, + }, + buttonOpen: { + backgroundColor: '#F194FF', + }, + buttonClose: { + backgroundColor: '#2196F3', + }, + textStyle: { + color: 'white', + fontWeight: 'bold', + textAlign: 'center', + }, + modalText: { + marginBottom: 15, + textAlign: 'center', + }, }); \ No newline at end of file From 8c4ef82305bc3cb3ebb26cc617ebe04e224e8ad8 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Wed, 15 Nov 2023 10:14:19 +0100 Subject: [PATCH 5/6] Template modal now opens when swiping --- app/(tabs)/expenses/index.tsx | 167 ++++++++++++---------------------- 1 file changed, 59 insertions(+), 108 deletions(-) diff --git a/app/(tabs)/expenses/index.tsx b/app/(tabs)/expenses/index.tsx index 4a1be4b..494acaf 100644 --- a/app/(tabs)/expenses/index.tsx +++ b/app/(tabs)/expenses/index.tsx @@ -1,7 +1,6 @@ -import { StyleSheet, Modal, Pressable, Alert } from 'react-native'; +import { StyleSheet, Modal, TouchableOpacity } from 'react-native'; import React, {useState} from 'react'; import { Text, View, } from '../../../components/Themed'; -import { Component } from 'react'; import { FlatList } from 'react-native-gesture-handler'; import { FontAwesome5 } from '@expo/vector-icons'; import Swipeable from 'react-native-gesture-handler/Swipeable'; @@ -27,88 +26,70 @@ const DATA: Expense[] = [ const calculatedExpenses = calculateExpenses(DATA); -function renderModal() { +export default function SettleScreen() { const [modalVisible, setModalVisible] = useState(false); - return ( - - { - Alert.alert('Modal has been closed.'); - setModalVisible(!modalVisible); - }}> - - - Hello World! - setModalVisible(!modalVisible)}> - Hide Modal - + + const openModal = () => { + setModalVisible(true); + }; + + const closeModal = () => { + setModalVisible(false); + }; + + const renderItem = ({ item, index }: { item: Transaction; index: number }) => { + return ( + openModal()} + > + + + {item.from} + {item.amount} kr. + {item.to} - - setModalVisible(true)}> - Show Modal - - - ); -}; + + ); + }; -function renderItem({item, index}: { item: Transaction, index: number}) { - return ( - - - - {item.from} - {item.amount} kr. - {item.to} - + const leftSwipeAction = () => { + return ( + + - - ) -} + ); + }; -function leftSwipeAction() { return ( - - - - ) -} - -function swipeHandler(dir: 'left' | 'right') { - if (dir == "left") { - console.log(dir); - } else { - console.log(dir); - } -} -export default class SettleScreen extends Component { - render() { - return ( + - ); - } + + + + Test123 + + + + + ); } const styles = StyleSheet.create({ @@ -152,45 +133,15 @@ const styles = StyleSheet.create({ flexDirection: 'row', justifyContent: 'flex-end' }, - centeredView: { + modalContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', - marginTop: 22, - }, - modalView: { - margin: 20, - backgroundColor: 'white', - borderRadius: 20, - padding: 35, - alignItems: 'center', - shadowColor: '#000', - shadowOffset: { - width: 0, - height: 2, - }, - shadowOpacity: 0.25, - shadowRadius: 4, - elevation: 5, - }, - button: { - borderRadius: 20, - padding: 10, - elevation: 2, - }, - buttonOpen: { - backgroundColor: '#F194FF', - }, - buttonClose: { - backgroundColor: '#2196F3', - }, - textStyle: { - color: 'white', - fontWeight: 'bold', - textAlign: 'center', + backgroundColor: 'rgba(0, 0, 0, 0.5)', }, - modalText: { - marginBottom: 15, - textAlign: 'center', + modalContent: { + backgroundColor: '#fff', + padding: 20, + borderRadius: 10, }, }); \ No newline at end of file From 9c67d5291ec7913885de81a17cf8619d32c4ff08 Mon Sep 17 00:00:00 2001 From: Mike Jensen Date: Wed, 15 Nov 2023 11:52:16 +0100 Subject: [PATCH 6/6] Modal styling to match Figma --- app/(tabs)/expenses/index.tsx | 61 ++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/app/(tabs)/expenses/index.tsx b/app/(tabs)/expenses/index.tsx index 494acaf..b80bb18 100644 --- a/app/(tabs)/expenses/index.tsx +++ b/app/(tabs)/expenses/index.tsx @@ -1,5 +1,5 @@ -import { StyleSheet, Modal, TouchableOpacity } from 'react-native'; -import React, {useState} from 'react'; +import { StyleSheet, Modal, Pressable, Dimensions } from 'react-native'; +import React, { useState, useRef } from 'react'; import { Text, View, } from '../../../components/Themed'; import { FlatList } from 'react-native-gesture-handler'; import { FontAwesome5 } from '@expo/vector-icons'; @@ -22,18 +22,22 @@ const DATA: Expense[] = [ { user: 'Martin', amount: 90 }, { user: 'Andreas', amount: 30 }, { user: 'Bisgaard', amount: 0 }, + { user: 'Mike', amount: 85 }, ]; const calculatedExpenses = calculateExpenses(DATA); export default function SettleScreen() { const [modalVisible, setModalVisible] = useState(false); + const [selectedItem, setSelectedItem] = useState(null); - const openModal = () => { + const openModal = (item: Transaction) => { + setSelectedItem(item); setModalVisible(true); }; const closeModal = () => { + setSelectedItem(null); setModalVisible(false); }; @@ -43,12 +47,12 @@ export default function SettleScreen() { friction={1.5} overshootFriction={8} renderLeftActions={leftSwipeAction} - onSwipeableOpen={() => openModal()} + onSwipeableOpen={() => openModal(item)} > {item.from} - {item.amount} kr. + {item.amount.toFixed(2)} kr. {item.to} @@ -69,6 +73,14 @@ export default function SettleScreen() { ); }; + const renderButton = (text: string, backgroundColor: string) => { + return ( + + {text} + + ); + }; + return ( - - Test123 - + {selectedItem && ( + + Confirm Payment + Have you sent {selectedItem.amount} kr. to {selectedItem.to}? + + {renderButton('Yes', '#5CBCA9')} + {renderButton('No', '#E35F52')} + + + )} @@ -143,5 +162,31 @@ const styles = StyleSheet.create({ backgroundColor: '#fff', padding: 20, borderRadius: 10, + height: "40%" + }, + modalTitleText: { + fontSize: 30, + }, + modalContentText: { + fontSize: 16, + }, + buttonContainer: { + flexDirection: 'row', + justifyContent: 'space-evenly', + padding: 10, + marginTop: '40%', + }, + button: { + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 12, + paddingHorizontal: 24, + borderRadius: 4, + elevation: 3, + width: "40%", }, + buttonText: { + color: 'white', + fontSize: 16, + } }); \ No newline at end of file