Skip to content

Commit

Permalink
Merge pull request #113 from enrique-lozano/feat/account-display-order
Browse files Browse the repository at this point in the history
Display order for accounts and tags
  • Loading branch information
enrique-lozano authored Feb 5, 2024
2 parents d3483c8 + 283d4f0 commit fa3aad2
Show file tree
Hide file tree
Showing 22 changed files with 545 additions and 244 deletions.
7 changes: 6 additions & 1 deletion assets/sql/migrations/v6.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
DELETE FROM userSettings WHERE settingKey = 'transactionMobileMode';

ALTER TABLE accounts ADD COLUMN color TEXT;
ALTER TABLE accounts ADD COLUMN color TEXT;
ALTER TABLE accounts ADD COLUMN displayOrder INTEGER NOT NULL DEFAULT 0;

ALTER TABLE tags ADD COLUMN displayOrder INTEGER NOT NULL DEFAULT 0;

ALTER TABLE categories ADD COLUMN displayOrder INTEGER NOT NULL DEFAULT 0;
3 changes: 2 additions & 1 deletion lib/app/accounts/account_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import 'package:uuid/uuid.dart';

@RoutePage()
class AccountFormPage extends StatefulWidget {
const AccountFormPage({Key? key, this.account}) : super(key: key);
const AccountFormPage({super.key, this.account});

/// Account UUID to edit (if any)
final Account? account;
Expand Down Expand Up @@ -86,6 +86,7 @@ class _AccountFormPageState extends State<AccountFormPage> {
Account accountToSubmit = Account(
id: _accountToEdit?.id ?? const Uuid().v4(),
name: _nameController.text,
displayOrder: 10,
iniValue: newBalance,
date: _openingDate,
closingDate: _closeDate,
Expand Down
145 changes: 94 additions & 51 deletions lib/app/accounts/all_accounts_page.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import 'package:auto_route/auto_route.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:monekin/app/accounts/monekin_reorderable_list.dart';
import 'package:monekin/core/database/services/account/account_service.dart';
import 'package:monekin/core/presentation/app_colors.dart';
import 'package:monekin/core/presentation/widgets/tappable.dart';
import 'package:monekin/core/routes/app_router.dart';
import 'package:monekin/i18n/translations.g.dart';

Expand All @@ -22,62 +26,101 @@ class AllAccountsPage extends StatelessWidget {
onPressed: () => context.pushRoute(AccountFormRoute()),
),
body: StreamBuilder(
stream: AccountService.instance.getAccounts(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const LinearProgressIndicator();
}
stream: AccountService.instance.getAccounts(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const LinearProgressIndicator();
}

final accounts = snapshot.data!;
final accounts = snapshot.data!;

if (accounts.isEmpty) {
return Column(
children: [
Expanded(
child: EmptyIndicator(
title: t.general.empty_warn,
description: t.account.no_accounts)),
],
);
}
if (accounts.isEmpty) {
return Column(
children: [
Expanded(
child: EmptyIndicator(
title: t.general.empty_warn,
description: t.account.no_accounts)),
],
);
}

return ListView.separated(
padding: EdgeInsets.zero,
itemCount: accounts.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (context, index) {
return const Divider(indent: 56);
},
itemBuilder: (context, index) {
final account = accounts[index];
return MonekinReorderableList(
totalItemCount: accounts.length,
itemBuilder: (context, index) {
final account = accounts.elementAt(index);

return ListTile(
title: Row(
children: [
Flexible(
child: Text(
account.name,
softWrap: false,
overflow: TextOverflow.fade,
),
return Tappable(
onTap: () => context.pushRoute(
AccountDetailsRoute(account: account),
),
bgColor: AppColors.of(context).light,
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
borderRadius: 12,
child: ListTile(
trailing: accounts.length > 1
? ReorderableDragIcon(index: index)
: null,
title: Row(
children: [
Flexible(
child: Text(
account.name,
softWrap: false,
overflow: TextOverflow.fade,
),
const SizedBox(width: 4),
if (account.isClosed)
const Icon(Icons.archive_outlined,
color: Colors.amber, size: 16)
],
),
leading: Hero(
tag: 'account-icon-${account.id}',
child: account.displayIcon(context),
),
subtitle: Text(account.type.title(context)),
onTap: () => context
.pushRoute(AccountDetailsRoute(account: account)),
);
});
}),
),
const SizedBox(width: 4),
if (account.isClosed)
const Icon(Icons.archive_outlined,
color: Colors.amber, size: 16)
],
),
leading: Hero(
tag: 'account-icon-${account.id}',
child: account.displayIcon(context),
),
subtitle: Text(
account.type.title(context),
),
),
);
},
onReorder: (from, to) async {
if (to > from) to--;

final item = accounts.removeAt(from);
accounts.insert(to, item);

Future.wait(
accounts.mapIndexed(
(index, element) => AccountService.instance.updateAccount(
element.copyWith(displayOrder: index),
),
),
);
},
);
},
),
);
}
}

class ReorderableDragIcon extends StatelessWidget {
const ReorderableDragIcon({super.key, required this.index});

final int index;

@override
Widget build(BuildContext context) {
return ReorderableDragStartListener(
index: index,
child: Container(
padding: const EdgeInsets.fromLTRB(14, 4, 2, 4),
// Padding to increase the dragabble area
//color: Colors.red,
child: const Icon(Icons.drag_handle_rounded)),
);
}
}
55 changes: 55 additions & 0 deletions lib/app/accounts/monekin_reorderable_list.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class MonekinReorderableList extends StatefulWidget {
const MonekinReorderableList({
super.key,
required this.itemBuilder,
required this.onReorder,
required this.totalItemCount,
});

final Widget Function(BuildContext context, int index) itemBuilder;
final void Function(int from, int to) onReorder;

final int totalItemCount;

@override
State<MonekinReorderableList> createState() => _MonekinReorderableListState();
}

class _MonekinReorderableListState extends State<MonekinReorderableList> {
int? isOrderingItem;

@override
Widget build(BuildContext context) {
return ReorderableListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => Opacity(
key: Key(index.toString()),
opacity: isOrderingItem == null || isOrderingItem == index ? 1 : 0.4,
child: ReorderableDelayedDragStartListener(
index: index,
enabled: widget.totalItemCount > 1,
child: widget.itemBuilder(context, index),
),
),
buildDefaultDragHandles: false,
itemCount: widget.totalItemCount,
onReorder: (from, to) => widget.onReorder(from, to),
onReorderStart: (index) {
HapticFeedback.lightImpact();

setState(() {
isOrderingItem = index;
});
},
onReorderEnd: (index) {
setState(() {
isOrderingItem = null;
});
},
);
}
}
4 changes: 4 additions & 0 deletions lib/app/categories/form/category_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class _CategoryFormPageState extends State<CategoryFormPage> {
categoryToEdit = Category(
id: categoryToEdit!.id,
name: _nameController.text,
displayOrder: categoryToEdit!.displayOrder,
iconId: _icon.id,
color: _color,
parentCategory: categoryToEdit!.parentCategory,
Expand Down Expand Up @@ -112,6 +113,7 @@ class _CategoryFormPageState extends State<CategoryFormPage> {
id: const Uuid().v4(),
name: _nameController.text,
iconId: _icon.id,
displayOrder: 10,
type: _type,
color: _color))
.then((value) {
Expand Down Expand Up @@ -390,6 +392,7 @@ class _CategoryFormPageState extends State<CategoryFormPage> {
CategoryService.instance.updateCategory(
CategoryInDB(
id: subcategory.id,
displayOrder: 10,
name: name,
iconId: icon.id,
parentCategoryID:
Expand All @@ -413,6 +416,7 @@ class _CategoryFormPageState extends State<CategoryFormPage> {
CategoryService.instance.insertCategory(
CategoryInDB(
id: const Uuid().v4(),
displayOrder: 10,
name: name,
iconId: icon.id,
parentCategoryID: categoryToEdit!.id));
Expand Down
1 change: 1 addition & 0 deletions lib/app/settings/import_csv.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ class _ImportCSVPageState extends State<ImportCSVPage> {
id: accountID,
name: row[accountColumn!].toString(),
iniValue: 0,
displayOrder: 10,
date: DateTime.now(),
type: AccountType.normal,
iconId: SupportedIconService.instance.defaultSupportedIcon.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class _ChartByCategoriesState extends State<ChartByCategories> {
category: Category(
id: 'Other',
name: 'Other',
displayOrder: 1000,
iconId: 'iconId',
type: CategoryType.B,
color: 'DEDEDE'));
Expand Down
Loading

0 comments on commit fa3aad2

Please sign in to comment.