Skip to content

Commit

Permalink
feat: Item management improvements (#213)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomBursch authored Aug 2, 2023
1 parent 499e5d1 commit 2ccdb71
Show file tree
Hide file tree
Showing 8 changed files with 314 additions and 58 deletions.
7 changes: 7 additions & 0 deletions lib/cubits/household_add_update/household_update_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ class HouseholdUpdateCubit
return res;
}

Future<bool> mergeCategory(Category category, Category other) async {
final res = await ApiService.getInstance().mergeCategories(category, other);
refresh();

return res;
}

Future<bool> deleteExpenseCategory(ExpenseCategory category) async {
final res = await ApiService.getInstance().deleteExpenseCategory(category);
refresh();
Expand Down
14 changes: 13 additions & 1 deletion lib/cubits/item_edit_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ class ItemEditCubit<T extends Item> extends Cubit<ItemEditState> {
description: state.description,
category: Nullable(state.category),
icon: Nullable(state.icon),
name: state.name,
)) as T);
}

return _item.copyWith(
category: Nullable(state.category),
icon: Nullable(state.icon),
name: state.name,
) as T;
}

Expand Down Expand Up @@ -67,7 +69,9 @@ class ItemEditCubit<T extends Item> extends Cubit<ItemEditState> {
}

bool hasChangedItem() {
return _item.category != state.category || _item.icon != state.icon;
return _item.category != state.category ||
_item.icon != state.icon ||
_item.name != state.name;
}

bool hasChangedDescription() {
Expand Down Expand Up @@ -100,6 +104,14 @@ class ItemEditCubit<T extends Item> extends Cubit<ItemEditState> {
return false;
}

Future<bool> mergeItem(Item other) async {
if (_item.id != null && other.id != null) {
return ApiService.getInstance().mergeItems(_item, other);
}

return false;
}

void setName(String name) {
emit(state.copyWith(name: name));
}
Expand Down
13 changes: 13 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
"@cancel": {},
"categories": "Categories",
"@categories": {},
"categoriesMerge": "Merge Categories",
"@categoriesMerge": {},
"category": "Category",
"@category": {},
"categoryDelete": "Delete Category",
Expand Down Expand Up @@ -227,6 +229,15 @@
"item": {}
}
},
"itemsMerge": "Merge Items",
"@itemsMerge": {},
"itemsMergeConfirmation": "Are you sure you want to merge {item} and {other}? This cannot be undone.",
"@itemsMergeConfirmation": {
"placeholders": {
"item": {},
"other": {}
}
},
"itemsOptional": "Optional Items",
"@itemsOptional": {},
"itemsRecent": "Recent Items",
Expand Down Expand Up @@ -281,6 +292,8 @@
"@memberRemove": {},
"members": "Members",
"@members": {},
"merge": "Merge",
"@merge": {},
"minutesAbbrev": "min",
"@minutesAbbrev": {},
"monthly": "Monthly",
Expand Down
132 changes: 99 additions & 33 deletions lib/pages/item_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:kitchenowl/kitchenowl.dart';
import 'package:kitchenowl/models/shoppinglist.dart';
import 'package:kitchenowl/models/update_value.dart';
import 'package:kitchenowl/pages/icon_selection_page.dart';
import 'package:kitchenowl/pages/item_search_page.dart';
import 'package:kitchenowl/widgets/recipe_item.dart';

class ItemPage<T extends Item> extends StatefulWidget {
Expand All @@ -37,6 +38,8 @@ class ItemPage<T extends Item> extends StatefulWidget {

enum _ItemAction {
changeIcon,
rename,
merge,
delete;
}

Expand Down Expand Up @@ -82,7 +85,11 @@ class _ItemPageState<T extends Item> extends State<ItemPage<T>> {
},
child: Scaffold(
appBar: AppBar(
title: Text(widget.item.name),
title: BlocBuilder<ItemEditCubit, ItemEditState>(
bloc: cubit,
buildWhen: (prev, curr) => prev.name != curr.name,
builder: (context, state) => Text(state.name),
),
actions: [
if (widget.item is! RecipeItem && !App.isOffline)
PopupMenuButton(
Expand All @@ -92,43 +99,22 @@ class _ItemPageState<T extends Item> extends State<ItemPage<T>> {
value: _ItemAction.changeIcon,
child: Text(AppLocalizations.of(context)!.changeIcon),
),
PopupMenuItem<_ItemAction>(
value: _ItemAction.rename,
child: Text(AppLocalizations.of(context)!.rename),
),
const PopupMenuDivider(),
if (widget.household != null)
PopupMenuItem<_ItemAction>(
value: _ItemAction.merge,
child: Text(AppLocalizations.of(context)!.merge),
),
PopupMenuItem<_ItemAction>(
value: _ItemAction.delete,
child: Text(AppLocalizations.of(context)!.delete),
),
],
onSelected: (value) async {
switch (value) {
case _ItemAction.changeIcon:
final icon = await Navigator.of(context)
.push<Nullable<String?>>(MaterialPageRoute(
builder: (context) => IconSelectionPage(
oldIcon: cubit.state.icon,
name: cubit.state.name,
),
));
if (icon != null) cubit.setIcon(icon.value);
break;
case _ItemAction.delete:
final confirmed = await askForConfirmation(
context: context,
title: Text(
AppLocalizations.of(context)!.itemDelete,
),
content: Text(
AppLocalizations.of(context)!
.itemDeleteConfirmation(widget.item.name),
),
);
if (confirmed) {
await cubit.deleteItem();
if (!mounted) return;
Navigator.of(context)
.pop(const UpdateValue<Item>(UpdateEnum.deleted));
}
break;
}
},
onSelected: _handleItemAction,
),
],
),
Expand Down Expand Up @@ -312,4 +298,84 @@ class _ItemPageState<T extends Item> extends State<ItemPage<T>> {
),
);
}

// ignore: long-method
Future<void> _handleItemAction(_ItemAction action) async {
switch (action) {
case _ItemAction.changeIcon:
final icon = await Navigator.of(context)
.push<Nullable<String?>>(MaterialPageRoute(
builder: (context) => IconSelectionPage(
oldIcon: cubit.state.icon,
name: cubit.state.name,
),
));
if (icon != null) cubit.setIcon(icon.value);
break;
case _ItemAction.rename:
final res = await showDialog<String>(
context: context,
builder: (BuildContext context) {
return TextDialog(
title: AppLocalizations.of(context)!.categoryEdit,
doneText: AppLocalizations.of(context)!.rename,
hintText: AppLocalizations.of(context)!.name,
initialText: cubit.state.name,
isInputValid: (s) => s.trim().isNotEmpty && s != cubit.state.name,
);
},
);
if (res != null) cubit.setName(res);
break;
case _ItemAction.merge:
final items = await Navigator.of(
context,
rootNavigator: true,
).push<List<Item>>(MaterialPageRoute(
builder: (context) => ItemSearchPage(
household: widget.household!,
multiple: false,
title: AppLocalizations.of(context)!.itemsMerge,
),
)) ??
[];
if (items.length == 1 && items.first.id != widget.item.id) {
final confirmed = await askForConfirmation(
context: context,
title: Text(
AppLocalizations.of(context)!.itemsMerge,
),
confirmText: AppLocalizations.of(context)!.merge,
content: Text(
AppLocalizations.of(context)!.itemsMergeConfirmation(
widget.item.name,
items.first.name,
),
),
);
if (confirmed) {
await cubit.mergeItem(items.first);
}
}
break;
case _ItemAction.delete:
final confirmed = await askForConfirmation(
context: context,
title: Text(
AppLocalizations.of(context)!.itemDelete,
),
content: Text(
AppLocalizations.of(context)!
.itemDeleteConfirmation(widget.item.name),
),
);
if (confirmed) {
await cubit.deleteItem();
if (!mounted) return;
Navigator.of(context)
.pop(const UpdateValue<Item>(UpdateEnum.deleted));
}
break;
}
}
}
11 changes: 11 additions & 0 deletions lib/services/api/category.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ extension CategoryApi on ApiService {
return res.statusCode == 200;
}

Future<bool> mergeCategories(Category category, Category other) async {
final res = await post(
'$baseRoute/${category.id}',
jsonEncode({
"merge_category_id": other.id,
}),
);

return res.statusCode == 200;
}

Future<bool> deleteCategory(Category category) async {
final res = await delete(
baseRoute,
Expand Down
11 changes: 11 additions & 0 deletions lib/services/api/item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,15 @@ extension ItemApi on ApiService {

return res.statusCode == 200;
}

Future<bool> mergeItems(Item item, Item other) async {
final res = await post(
'$baseRoute/${item.id}',
jsonEncode({
"merge_item_id": other.id,
}),
);

return res.statusCode == 200;
}
}
8 changes: 4 additions & 4 deletions lib/widgets/select_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';

class SelectDialog extends StatelessWidget {
final List<SelectDialogOption> options;
class SelectDialog<T> extends StatelessWidget {
final List<SelectDialogOption<T>> options;
final String title;
final String cancelText;

Expand Down Expand Up @@ -47,8 +47,8 @@ class SelectDialog extends StatelessWidget {
}
}

class SelectDialogOption {
final int id;
class SelectDialogOption<T> {
final T id;
final String name;
final IconData? icon;

Expand Down
Loading

0 comments on commit 2ccdb71

Please sign in to comment.