Skip to content

Commit 90bf02c

Browse files
committed
fix #57
1 parent 838c9d4 commit 90bf02c

File tree

14 files changed

+388
-34
lines changed

14 files changed

+388
-34
lines changed

assets/l10n/en_US.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"account": "Account",
6464
"account.name": "Account name",
6565
"account.balance": "Balance",
66+
"account.balance.upcomingDescription": "Upcoming transactions don't affect current balance",
6667
"account.excludeFromTotalBalance": "Exclude from balance",
6768
"account.excludeFromTotalBalance.description": "If you check this option, this account's balance will not be included in the total balance. Useful for savings or non-personal accounts.",
6869
"account.updateBalance": "Update balance",
@@ -71,6 +72,7 @@
7172
"account.edit": "Edit account",
7273
"account.edit.selectCurrency": "Select a currency",
7374
"account.transactions": "Transactions",
75+
"account.transactions.title": "\"{account}\" transactions",
7476
"account.delete": "Delete account",
7577
"account.delete.warning": "Deleting this account will also delete {transactionCount} transactions associated. This action is irreversible!",
7678
"account.noAccounts": "You don't have any accounts!",
@@ -93,6 +95,9 @@
9395
"transaction.transfer.to.title": "To {account}",
9496
"transaction.transfer.fromToTitle": "From {from} to {to}",
9597

98+
"transactions.all": "All transactions",
99+
"transactions.upcoming": "Upcoming transactions",
100+
96101
"category": "Category",
97102
"category.name": "Category name",
98103
"category.new": "Add a category",
@@ -133,7 +138,7 @@
133138
"tabs.home.noTransactions.allTime": "You don't have any transactions",
134139
"tabs.home.noTransactions.last7Days": "No transactions for the last 7 days",
135140
"tabs.home.noTransactions.addSome": "Click on (+) button below to add a new transaction",
136-
"tabs.home.upcomingTransactions": "Upcoming {count}",
141+
"tabs.home.upcomingTransactions": "Upcoming ({count})",
137142
"tabs.home.upcomingTransactions.seeAll": "See all",
138143
"tabs.home.transactionsCount": "{count} transactions",
139144
"tabs.home.last7days": "Last 7 days",

assets/l10n/mn_MN.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"account": "Данс",
6464
"account.name": "Дансны нэр",
6565
"account.balance": "Үлдэгдэл",
66+
"account.balance.upcomingDescription": "Төлөвлөсөн гүйлгээнүүд одоогийн дансны үлдэгдэлд нөлөөлөхгүй",
6667
"account.excludeFromTotalBalance": "Нийт үлдэгдэлд тооцохгүй",
6768
"account.excludeFromTotalBalance.description": "Энэ сонголтыг идэвхжүүлбэл энэ дансны үлдэгдэл нийт үлдэгдэлд бодогдохгүй. Хадгаламж, хувийн бус данс гэх мэт зүйлсд тохиромжтой.",
6869
"account.updateBalance": "Үлдэгдэл өөрчлөх",
@@ -71,6 +72,7 @@
7172
"account.edit": "Данс засварлах",
7273
"account.edit.selectCurrency": "Валют сонгох",
7374
"account.transactions": "Гүйлгээнүүд",
75+
"account.transactions.title": "\"{account}\"-н гүйлгээнүүд",
7476
"account.delete": "Дансыг устгах",
7577
"account.delete.warning": "Энэ дансыг устгавал холбоотой {transactionCount} гүйлгээг хамт устгах болно. Энэ үйлдлийг буцаах боломжгүй юм!",
7678
"account.noAccounts": "Танд үүсгэсэн данс алга байна!",
@@ -93,6 +95,9 @@
9395
"transaction.transfer.to.title": "{account}-руу",
9496
"transaction.transfer.fromToTitle": "{from}-с {to} руу",
9597

98+
"transactions.all": "Бүх гүйлгээнүүд",
99+
"transactions.upcoming": "Төлөвлөсөн гүйлгээнүүд",
100+
96101
"category": "Ангилал",
97102
"category.name": "Нэр",
98103
"category.new": "Ангилал үүсгэх",
@@ -133,8 +138,8 @@
133138
"tabs.home.noTransactions.allTime": "Танд одоогоор гүйлгээ алга байна",
134139
"tabs.home.noTransactions.last7Days": "Сүүлийн долоо хоногт хийгдсэн гүйлгээ алга байна",
135140
"tabs.home.noTransactions.addSome": "Доор байрлах (+) товч дээр дарж гүйлгээ нэмээрэй",
136-
"tabs.home.upcomingTransactions": "Төлөвлөсөн {count}",
137-
"tabs.home.upcomingTransactions.seeAll": "Бүгдийг харах",
141+
"tabs.home.upcomingTransactions": "Төлөвлөсөн ({count})",
142+
"tabs.home.upcomingTransactions.seeAll": "Бүгд",
138143
"tabs.home.transactionsCount": "{count} гүйлгээ",
139144
"tabs.home.last7days": "Сүүлийн 7 хоног",
140145
"tabs.home.totalBalance": "Нийт үлдэгдэл",

lib/entity/account.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:flow/entity/_base.dart';
33
import 'package:flow/entity/transaction.dart';
44
import 'package:json_annotation/json_annotation.dart';
55
import 'package:material_symbols_icons/symbols.dart';
6+
import 'package:moment_dart/moment_dart.dart';
67
import 'package:objectbox/objectbox.dart';
78
import 'package:uuid/uuid.dart';
89

@@ -55,10 +56,12 @@ class Account implements EntityBase {
5556
@Transient()
5657
@JsonKey(includeFromJson: false, includeToJson: false)
5758
double get balance {
58-
return transactions.fold<double>(
59-
0,
60-
(previousValue, element) => previousValue + element.amount,
61-
);
59+
return transactions
60+
.where((element) => element.transactionDate.isPast)
61+
.fold<double>(
62+
0,
63+
(previousValue, element) => previousValue + element.amount,
64+
);
6265
}
6366

6467
Account({

lib/objectbox/actions.dart

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -353,21 +353,13 @@ extension TransactionListActions on Iterable<Transaction> {
353353
/// relative to [anchor] will be grouped into the same group
354354
Map<DateTime, List<Transaction>> groupByDate({
355355
DateTime? anchor,
356-
bool mergeFutureTransactions = false,
357356
}) {
358-
final DateTime future = DateTime(9999, 12, 31);
359-
360357
anchor ??= DateTime.now();
361358

362359
final Map<DateTime, List<Transaction>> value = {};
363360

364361
for (final transaction in this) {
365-
final bool mergeIntoFuture =
366-
mergeFutureTransactions && transaction.transactionDate >= anchor;
367-
368-
final DateTime date = mergeIntoFuture
369-
? future
370-
: transaction.transactionDate.toLocal().startOfDay();
362+
final DateTime date = transaction.transactionDate.toLocal().startOfDay();
371363

372364
value[date] ??= [];
373365
value[date]!.add(transaction);

lib/routes.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import 'package:flow/routes/utils/crop_square_image_page.dart';
2727
import 'package:flow/sync/export/mode.dart';
2828
import 'package:flow/sync/import/import_v1.dart';
2929
import 'package:flow/utils/utils.dart';
30+
import 'package:flow/widgets/general/info_text.dart';
31+
import 'package:flutter/widgets.dart';
3032
import 'package:go_router/go_router.dart';
3133

3234
final router = GoRouter(
@@ -52,6 +54,26 @@ final router = GoRouter(
5254
transactionId: int.tryParse(state.pathParameters["id"]!) ?? -1,
5355
),
5456
),
57+
GoRoute(
58+
path: '/transactions',
59+
builder: (context, state) => TransactionsPage.all(
60+
title: "transactions.all".t(context),
61+
),
62+
),
63+
GoRoute(
64+
path: '/transactions/upcoming',
65+
builder: (context, state) => TransactionsPage.upcoming(
66+
title: "transactions.upcoming".t(context),
67+
header: InfoText(
68+
singleLine: true,
69+
child: Text(
70+
"account.balance.upcomingDescription".t(context),
71+
maxLines: 1,
72+
overflow: TextOverflow.ellipsis,
73+
),
74+
),
75+
),
76+
),
5577
GoRoute(
5678
path: '/account/new',
5779
builder: (context, state) => const AccountPage.create(),

lib/routes/home/home_tab.dart

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import 'package:flow/entity/transaction.dart';
22
import 'package:flow/objectbox.dart';
33
import 'package:flow/objectbox/actions.dart';
44
import 'package:flow/objectbox/objectbox.g.dart';
5+
import 'package:flow/widgets/general/wavy_divider.dart';
56
import 'package:flow/widgets/home/home/no_transactions.dart';
67
import 'package:flow/widgets/home/greetings_bar.dart';
78
import 'package:flow/widgets/grouped_transaction_list.dart';
9+
import 'package:flow/widgets/home/home/upcoming_transactions_list.dart';
810
import 'package:flow/widgets/home/transactions_date_header.dart';
911
import 'package:flutter/material.dart';
1012
import 'package:moment_dart/moment_dart.dart';
@@ -80,18 +82,42 @@ class _HomeTabState extends State<HomeTab> with AutomaticKeepAliveClientMixin {
8082
BuildContext context,
8183
List<Transaction> transactions,
8284
) {
83-
final Map<DateTime, List<Transaction>> grouped =
84-
transactions.groupByDate(mergeFutureTransactions: true);
85+
final Map<DateTime, List<Transaction>> grouped = transactions
86+
.where((element) => element.transactionDate.isPast)
87+
.groupByDate();
88+
8589
final List<Widget> headers = grouped.keys
86-
.map((date) =>
87-
TransactionListDateHeader(transactions: grouped[date]!, date: date))
90+
.map(
91+
(date) => TransactionListDateHeader(
92+
transactions: grouped[date]!,
93+
date: date,
94+
),
95+
)
96+
.toList();
97+
98+
final List<Transaction> upcoming = transactions
99+
.where((element) => element.transactionDate.isFuture)
88100
.toList();
89101

102+
final Widget? header = upcoming.isEmpty
103+
? null
104+
: Column(
105+
children: [
106+
UpcomingTransactionsList(
107+
transactions: upcoming,
108+
shouldCombineTransferIfNeeded: true,
109+
),
110+
const SizedBox(height: 16.0),
111+
const WavyDivider(),
112+
],
113+
);
114+
90115
return GroupedTransactionList(
91116
controller: widget.scrollController,
92117
transactions: grouped.values.toList(),
93118
shouldCombineTransferIfNeeded: true,
94119
headers: headers,
120+
header: header,
95121
listPadding: const EdgeInsets.only(
96122
top: 0,
97123
bottom: 80.0,

lib/routes/transactions_page.dart

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,21 @@ class TransactionsPage extends StatefulWidget {
1111
final QueryBuilder<Transaction> query;
1212
final String? title;
1313

14-
const TransactionsPage({super.key, required this.query, this.title});
14+
final Widget? header;
1515

16-
factory TransactionsPage.account(
17-
{Key? key, required int accountId, String? title}) {
16+
const TransactionsPage({
17+
super.key,
18+
required this.query,
19+
this.title,
20+
this.header,
21+
});
22+
23+
factory TransactionsPage.account({
24+
Key? key,
25+
required int accountId,
26+
String? title,
27+
Widget? header,
28+
}) {
1829
final QueryBuilder<Transaction> queryBuilder = ObjectBox()
1930
.box<Transaction>()
2031
.query(Transaction_.account.equals(accountId))
@@ -24,6 +35,46 @@ class TransactionsPage extends StatefulWidget {
2435
query: queryBuilder,
2536
key: key,
2637
title: title,
38+
header: header,
39+
);
40+
}
41+
42+
factory TransactionsPage.all({
43+
Key? key,
44+
String? title,
45+
Widget? header,
46+
}) {
47+
final QueryBuilder<Transaction> queryBuilder = ObjectBox()
48+
.box<Transaction>()
49+
.query()
50+
.order(Transaction_.transactionDate, flags: Order.descending);
51+
52+
return TransactionsPage(
53+
query: queryBuilder,
54+
key: key,
55+
title: title,
56+
header: header,
57+
);
58+
}
59+
60+
factory TransactionsPage.upcoming({
61+
Key? key,
62+
DateTime? anchor,
63+
String? title,
64+
Widget? header,
65+
}) {
66+
anchor ??= DateTime.now();
67+
68+
final QueryBuilder<Transaction> queryBuilder = ObjectBox()
69+
.box<Transaction>()
70+
.query(Transaction_.transactionDate.greaterThanDate(anchor))
71+
.order(Transaction_.transactionDate, flags: Order.descending);
72+
73+
return TransactionsPage(
74+
query: queryBuilder,
75+
key: key,
76+
title: title,
77+
header: header,
2778
);
2879
}
2980

@@ -55,6 +106,7 @@ class _TransactionsPageState extends State<TransactionsPage> {
55106
return GroupedTransactionList(
56107
transactions: grouped.values.toList(),
57108
headers: headers,
109+
header: widget.header,
58110
);
59111
},
60112
),

lib/utils/extensions/iterables.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,29 @@ extension Iterables<E> on Iterable<E> {
1010
}
1111
return null;
1212
}
13+
14+
/// Returns a list with items alternating from [this] and [other].
15+
///
16+
/// Both iterables must have the same length.
17+
///
18+
/// Example:
19+
/// ```dart
20+
/// List<Object> list1 = [1, 2, 3];
21+
/// List<Object> list2 = ['a', 'b', 'c'];
22+
/// list1.alternate(list2); // [1, 'a', 2, 'b', 3, 'c']
23+
/// ```
24+
List<E> alternate(Iterable<E> other) {
25+
if (length != other.length) {
26+
throw ArgumentError('Both iterables must have the same length');
27+
}
28+
29+
List<E> result = [];
30+
31+
for (int i = 0; i < length; i++) {
32+
result.add(elementAt(i));
33+
result.add(other.elementAt(i));
34+
}
35+
36+
return result;
37+
}
1338
}

lib/widgets/account_card.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ class AccountCard extends StatelessWidget {
137137
),
138138
CupertinoContextMenuAction(
139139
onPressed: () => context.push(
140-
"/account/${account.id}/transactions?title=${account.name}"),
140+
"/account/${account.id}/transactions?title=${"account.transactions.title".t(context, account.name)}"),
141141
isDefaultAction: true,
142142
trailingIcon: CupertinoIcons.square_list,
143143
child: Text("account.transactions".t(context)),

lib/widgets/general/wavy_divider.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import 'package:flow/widgets/general/wavy_divider/wavy_divider_painter.dart';
2+
import 'package:flutter/material.dart';
3+
4+
class WavyDivider extends StatelessWidget {
5+
/// Height of the divider
6+
///
7+
/// Visually, will be less than [height] due to usage of bezier curves
8+
final double height;
9+
10+
/// Width of a single wave
11+
final double waveWidth;
12+
13+
/// Color of the divider, defaults to theme's `dividerColor`
14+
final Color? color;
15+
16+
/// Width of the stroke
17+
final double strokeWidth;
18+
19+
const WavyDivider({
20+
super.key,
21+
this.height = 16.0,
22+
this.waveWidth = 16.0,
23+
this.strokeWidth = 2.0,
24+
this.color,
25+
});
26+
27+
@override
28+
Widget build(BuildContext context) {
29+
return SizedBox(
30+
width: double.infinity,
31+
height: height,
32+
child: ClipRect(
33+
child: CustomPaint(
34+
painter: WavyDividerPainter(
35+
color: color ?? Theme.of(context).dividerColor,
36+
height: height,
37+
waveWidth: waveWidth,
38+
strokeWidth: strokeWidth,
39+
),
40+
isComplex: false,
41+
willChange: false,
42+
),
43+
),
44+
);
45+
}
46+
}

0 commit comments

Comments
 (0)