Skip to content

Commit

Permalink
Merge pull request #37 from andreasalstrup/ExpensesSettle
Browse files Browse the repository at this point in the history
Expenses tab: Settle screen
  • Loading branch information
Bisgaard01 authored Nov 17, 2023
2 parents ed3dad2 + 9c67d52 commit 77609f7
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 44 deletions.
194 changes: 177 additions & 17 deletions app/(tabs)/expenses/index.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,192 @@
import { StyleSheet } from 'react-native';
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';
import Swipeable from 'react-native-gesture-handler/Swipeable';
import { calculateExpenses } from '../../../helpers/calculateExpenses';

import EditScreenInfo from '../../../components/EditScreenInfo';
import { Text, View } from '../../../components/Themed';

type Expense = {
user: string;
amount: number;
};

type Transaction = {
from: string;
to: string;
amount: number;
};

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<Transaction | null>(null);

const openModal = (item: Transaction) => {
setSelectedItem(item);
setModalVisible(true);
};

const closeModal = () => {
setSelectedItem(null);
setModalVisible(false);
};

const renderItem = ({ item, index }: { item: Transaction; index: number }) => {
return (
<Swipeable
friction={1.5}
overshootFriction={8}
renderLeftActions={leftSwipeAction}
onSwipeableOpen={() => openModal(item)}
>
<View style={[styles.container, { backgroundColor: index % 2 == 0 ? '#eeeeee' : '#D3D3D3' }]}>
<View style={styles.item}>
<Text style={styles.itemTextFrom}>{item.from}</Text>
<Text style={styles.itemAmount}>{item.amount.toFixed(2)} kr.</Text>
<Text style={styles.itemTextTo}>{item.to}</Text>
</View>
</View>
</Swipeable>
);
};

const leftSwipeAction = () => {
return (
<View style={styles.leftSwipe}>
<FontAwesome5
name="arrow-alt-circle-right"
size={32}
color="white"
style={styles.swipeIcon}
/>
</View>
);
};

const renderButton = (text: string, backgroundColor: string) => {
return (
<Pressable style={[styles.button, { backgroundColor }]} onPress={closeModal}>
<Text style={styles.buttonText}>{text}</Text>
</Pressable>
);
};

return (
<View style={styles.container}>
<Text style={styles.title}>Settle</Text>
<View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
<EditScreenInfo path="app/(tabs)/expenses/index.tsx" />
<View>
<FlatList
style={{marginTop: 48}}
data={calculatedExpenses}
renderItem={renderItem}
/>
<Modal
animationType="none"
transparent={true}
visible={modalVisible}
onRequestClose={closeModal}
>
<View style={styles.modalContainer}>
{selectedItem && (
<View style={styles.modalContent}>
<Text style={styles.modalTitleText}>Confirm Payment</Text>
<Text style={styles.modalContentText}>Have you sent {selectedItem.amount} kr. to {selectedItem.to}?</Text>
<View style={styles.buttonContainer}>
{renderButton('Yes', '#5CBCA9')}
{renderButton('No', '#E35F52')}
</View>
</View>
)}
</View>
</Modal>
</View>
);
}

const styles = StyleSheet.create({
container: {
marginTop: 48,
borderBottomColor: '#000000',
padding: 2,
},
item: {
backgroundColor: 'transparent',
borderBottomColor: '#000000',
borderBottomWidth: 0,
padding: 12,
flexDirection: 'row',
justifyContent: 'space-between'
},
itemTextFrom: {
fontSize: 24,
color: '#E35F52',
flexDirection: 'row',
},
itemTextTo: {
fontSize: 24,
color: '#5CBCA9',
flexDirection: 'row',
},
itemAmount: {
fontSize: 24,
color: 'black',
flexDirection: 'row',
justifyContent: 'flex-end'
},
leftSwipe: {
backgroundColor: '#5CBCA9',
justifyContent: 'center',
alignItems: 'flex-end'
},
swipeIcon: {
padding: 10,
},
longBoi: {
flexDirection: 'row',
justifyContent: 'flex-end'
},
modalContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalContent: {
backgroundColor: '#fff',
padding: 20,
borderRadius: 10,
height: "40%"
},
modalTitleText: {
fontSize: 30,
},
title: {
fontSize: 20,
fontWeight: 'bold',
modalContentText: {
fontSize: 16,
},
separator: {
marginVertical: 30,
height: 1,
width: '80%',
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,
}
});
54 changes: 27 additions & 27 deletions helpers/calculateExpenses.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type Expense = {
resident: string;
user: string;
amount: number;
};

Expand All @@ -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;
};

0 comments on commit 77609f7

Please sign in to comment.