Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display order for accounts and tags #113

Merged
merged 7 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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