Skip to content

Commit

Permalink
feat: transactions screen tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
Joaomlg committed Feb 15, 2023
1 parent 93eb8ac commit d1dbd2a
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 123 deletions.
2 changes: 1 addition & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"expo": {
"name": "Finans",
"slug": "finance-app",
"version": "1.1.4",
"version": "1.1.5",
"orientation": "portrait",
"icon": "./src/assets/icon.png",
"userInterfaceStyle": "automatic",
Expand Down
42 changes: 40 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "finance-app",
"version": "1.1.4",
"version": "1.1.5",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
Expand Down Expand Up @@ -38,10 +38,12 @@
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.18.0",
"react-native-svg": "13.4.0",
"react-native-tab-view": "^3.4.0",
"react-native-toast-message": "^2.1.5",
"react-native-web": "~0.18.7",
"react-native-webview": "11.23.1",
"styled-components": "^5.3.6"
"styled-components": "^5.3.6",
"react-native-pager-view": "6.0.1"
},
"devDependencies": {
"@babel/core": "^7.19.3",
Expand Down
29 changes: 18 additions & 11 deletions src/contexts/AppContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ export type AppContextValue = {
totalBalance: number;
totalInvoice: number;
totalInvestment: number;
incomeTransactions: Transaction[];
totalIncomes: number;
expenseTransactions: Transaction[];
totalExpenses: number;
};

Expand Down Expand Up @@ -99,23 +101,26 @@ export const AppContextProvider: React.FC<{ children: React.ReactNode }> = ({ ch
[investments],
);

const incomeTransactions = useMemo(
() => transactions.filter(({ type }) => type === 'CREDIT'),
[transactions],
);

const totalIncomes = useMemo(
() =>
transactions.reduce(
(total, transaction) =>
transaction.type === 'CREDIT' ? total + Math.abs(transaction.amount) : total,
0,
),
incomeTransactions.reduce((total, transaction) => total + Math.abs(transaction.amount), 0),
[incomeTransactions],
);

const expenseTransactions = useMemo(
() => transactions.filter(({ type }) => type === 'DEBIT'),
[transactions],
);

const totalExpenses = useMemo(
() =>
transactions.reduce(
(total, transaction) =>
transaction.type === 'DEBIT' ? total + Math.abs(transaction.amount) : total,
0,
),
[transactions],
expenseTransactions.reduce((total, transaction) => total + Math.abs(transaction.amount), 0),
[expenseTransactions],
);

const storeItem = useCallback(
Expand Down Expand Up @@ -352,7 +357,9 @@ export const AppContextProvider: React.FC<{ children: React.ReactNode }> = ({ ch
totalBalance,
totalInvoice,
totalInvestment,
incomeTransactions,
totalIncomes,
expenseTransactions,
totalExpenses,
}}
>
Expand Down
99 changes: 99 additions & 0 deletions src/pages/Transactions/TransactionList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import moment from 'moment';
import React, { useCallback, useRef } from 'react';
import { ListRenderItemInfo, RefreshControl } from 'react-native';
import { useTheme } from 'styled-components/native';
import Divider from '../../../components/Divider';
import Money from '../../../components/Money';
import Text from '../../../components/Text';
import { Transaction } from '../../../services/pluggy';
import {
ListHeaderContainer,
ListSeparatorContainer,
ListSeparatorDate,
StyledFlatList,
StyledTransactionListItem,
} from './styles';

export interface TransactionListProps {
transactions: Transaction[];
reducedValue: number;
isLoading?: boolean;
onRefresh?: () => void;
}

const TransactionList: React.FC<TransactionListProps> = ({
transactions,
reducedValue,
isLoading,
onRefresh,
}) => {
const theme = useTheme();

const ItemDividerPreviousDateRef = useRef(moment(0));

const renderHeaderComponent = useCallback(() => {
return (
<ListHeaderContainer>
<Text variant="light" color="textLight">
{transactions.length} transações
</Text>
<Money variant="heading" value={reducedValue} />
</ListHeaderContainer>
);
}, [transactions, reducedValue]);

const renderListItemSeparator = useCallback((transaction: Transaction, index: number) => {
const date = moment(transaction.date).startOf('day');

const component =
index === 0 || date.isBefore(ItemDividerPreviousDateRef.current, 'day') ? (
<ListSeparatorContainer>
<ListSeparatorDate>
<Text variant="title" color="textLight">
{date.format('DD')}
</Text>
<Text variant="light" color="textLight">
{date.format('MMM')}
</Text>
</ListSeparatorDate>
<Divider />
</ListSeparatorContainer>
) : (
<></>
);

ItemDividerPreviousDateRef.current = date;

return component;
}, []);

const renderItem = useCallback(
({ item, index }: ListRenderItemInfo<Transaction>) => {
return (
<>
{renderListItemSeparator(item, index)}
<StyledTransactionListItem key={index} item={item} />
</>
);
},
[renderListItemSeparator],
);

return (
<StyledFlatList
refreshControl={
<RefreshControl
refreshing={isLoading || false}
onRefresh={onRefresh}
colors={[theme.colors.primary]}
/>
}
data={transactions}
renderItem={renderItem}
keyExtractor={(item) => item.id}
ListHeaderComponent={renderHeaderComponent}
/>
);
};

export default React.memo(TransactionList);
35 changes: 35 additions & 0 deletions src/pages/Transactions/TransactionList/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { FlatList, FlatListProps } from 'react-native';
import styled from 'styled-components/native';
import FlexContainer from '../../../components/FlexContainer';
import TransactionListItem from '../../../components/TransactionListItem';
import { Transaction } from '../../../services/pluggy';

export const StyledFlatList = styled(
FlatList as new (props: FlatListProps<Transaction>) => FlatList<Transaction>,
).attrs(() => ({
contentContainerStyle: {
padding: 24,
},
}))`
flex: 1;
background-color: ${({ theme }) => theme.colors.backgroundWhite};
`;

export const ListHeaderContainer = styled(FlexContainer).attrs({ gap: 4 })`
margin-bottom: 12px;
`;

export const ListSeparatorContainer = styled.View`
flex-direction: row;
align-items: center;
margin: 12px 0;
`;

export const ListSeparatorDate = styled.View`
align-items: center;
margin-right: 12px;
`;

export const StyledTransactionListItem = styled(TransactionListItem)`
padding: 12px 0;
`;
55 changes: 55 additions & 0 deletions src/pages/Transactions/TransactionTabs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { useState } from 'react';
import { TabBar, TabBarProps, TabView } from 'react-native-tab-view';
import { useTheme } from 'styled-components/native';
import Text from '../../../components/Text';
import { TabLazyPlaceholder } from './styles';

type TransactionTabsRouteKey = 'default' | 'incomes' | 'expenses';

export type TransactionTabsRoute = {
key: TransactionTabsRouteKey;
title: string;
};

export interface TransactionTabsProps {
renderScene: (props: { route: TransactionTabsRoute }) => React.ReactNode;
}

const TransactionTabs: React.FC<TransactionTabsProps> = ({ renderScene }) => {
const theme = useTheme();

const [index, setIndex] = useState(0);

const routes: TransactionTabsRoute[] = [
{ key: 'default', title: 'Tudo' },
{ key: 'incomes', title: 'Entradas' },
{ key: 'expenses', title: 'Saídas' },
];

const renderTabBar = (props: TabBarProps<TransactionTabsRoute>) => (
<TabBar
{...props}
pressColor={theme.colors.primary}
style={{ backgroundColor: theme.colors.primary }}
indicatorStyle={{ backgroundColor: theme.colors.secondary, height: 3 }}
renderLabel={({ route, color }) => (
<Text variant="default-bold" style={{ color }}>
{route.title}
</Text>
)}
/>
);

return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
renderTabBar={renderTabBar}
lazy={({ route }) => route.key !== 'default'}
renderLazyPlaceholder={() => <TabLazyPlaceholder />}
/>
);
};

export default TransactionTabs;
6 changes: 6 additions & 0 deletions src/pages/Transactions/TransactionTabs/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import styled from 'styled-components/native';

export const TabLazyPlaceholder = styled.View`
flex: 1;
background-color: ${({ theme }) => theme.colors.backgroundWhite};
`;
Loading

0 comments on commit d1dbd2a

Please sign in to comment.