Skip to content
This repository was archived by the owner on Sep 14, 2024. It is now read-only.

Commit e41c15d

Browse files
committed
add recipe delete
1 parent 8f87bec commit e41c15d

File tree

8 files changed

+114
-6
lines changed

8 files changed

+114
-6
lines changed

assets/i18n/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@
135135
"button": "Save",
136136
"errors": {
137137
"update_failed": "Update Failed {error_msg}"
138+
},
139+
"delete": {
140+
"title": "Delete Recipe",
141+
"dialog": "Are you sure you want to permanently delete {recipe}?"
138142
}
139143
},
140144
"recipe_create": {

lib/src/blocs/recipe/recipe_bloc.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class RecipeBloc extends Bloc<RecipeEvent, RecipeState> {
1414
on<RecipeUpdated>(_mapRecipeUpdatedToState);
1515
on<RecipeImported>(_mapRecipeImportedToState);
1616
on<RecipeCreated>(_mapRecipeCreatedToState);
17+
on<RecipeDeleted>(_mapRecipeDeletedToState);
1718
}
1819

1920
Future<void> _mapRecipeLoadedToState(
@@ -81,4 +82,22 @@ class RecipeBloc extends Bloc<RecipeEvent, RecipeState> {
8182
);
8283
}
8384
}
85+
86+
Future<void> _mapRecipeDeletedToState(
87+
RecipeDeleted recipeDeleted,
88+
Emitter<RecipeState> emit,
89+
) async {
90+
try {
91+
emit(RecipeState(status: RecipeStatus.deleteInProgress));
92+
await dataRepository.deleteRecipe(recipeDeleted.recipe);
93+
emit(RecipeState(status: RecipeStatus.delteSuccess));
94+
} catch (e) {
95+
emit(
96+
RecipeState(
97+
status: RecipeStatus.deleteFailure,
98+
error: e.toString(),
99+
),
100+
);
101+
}
102+
}
84103
}

lib/src/blocs/recipe/recipe_event.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,12 @@ class RecipeImported extends RecipeEvent {
4242
@override
4343
List<Object> get props => [url];
4444
}
45+
46+
class RecipeDeleted extends RecipeEvent {
47+
final Recipe recipe;
48+
49+
const RecipeDeleted(this.recipe);
50+
51+
@override
52+
List<Object> get props => [recipe];
53+
}

lib/src/blocs/recipe/recipe_state.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
part of 'recipe_bloc.dart';
22

33
enum RecipeStatus {
4-
failure,
5-
success,
64
loadSuccess,
75
loadFailure,
86
loadInProgress,
@@ -12,6 +10,9 @@ enum RecipeStatus {
1210
createFailure,
1311
createSuccess,
1412
createInProgress,
13+
deleteInProgress,
14+
deleteFailure,
15+
delteSuccess,
1516
importSuccess,
1617
importFailure,
1718
importInProgress;
@@ -34,18 +35,19 @@ class RecipeState extends Equatable {
3435
case RecipeStatus.updateInProgress:
3536
case RecipeStatus.createInProgress:
3637
case RecipeStatus.importInProgress:
38+
case RecipeStatus.deleteInProgress:
39+
case RecipeStatus.delteSuccess:
3740
assert(error == null && recipe == null && recipeId == null);
3841
break;
3942
case RecipeStatus.createSuccess:
4043
case RecipeStatus.updateSuccess:
4144
assert(error == null && recipe == null && recipeId != null);
4245
break;
43-
case RecipeStatus.success:
4446
case RecipeStatus.loadSuccess:
4547
case RecipeStatus.importSuccess:
4648
assert(error == null && recipe != null && recipeId == null);
4749
break;
48-
case RecipeStatus.failure:
50+
case RecipeStatus.deleteFailure:
4951
case RecipeStatus.loadFailure:
5052
case RecipeStatus.updateFailure:
5153
case RecipeStatus.createFailure:

lib/src/screens/recipe_edit_screen.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import 'package:flutter_spinkit/flutter_spinkit.dart';
44
import 'package:flutter_translate/flutter_translate.dart';
55
import 'package:flutter_typeahead/flutter_typeahead.dart';
66
import 'package:nc_cookbook_api/nc_cookbook_api.dart';
7+
import 'package:nextcloud_cookbook_flutter/src/blocs/categories/categories_bloc.dart';
78
import 'package:nextcloud_cookbook_flutter/src/blocs/recipe/recipe_bloc.dart';
89
import 'package:nextcloud_cookbook_flutter/src/screens/recipe_screen.dart';
910
import 'package:nextcloud_cookbook_flutter/src/services/services.dart';
1011
import 'package:nextcloud_cookbook_flutter/src/util/theme_data.dart';
12+
import 'package:nextcloud_cookbook_flutter/src/widget/alerts/recipe_delete_alert.dart';
1113
import 'package:nextcloud_cookbook_flutter/src/widget/alerts/recipe_edit_alert.dart';
1214
import 'package:nextcloud_cookbook_flutter/src/widget/input/duration_form_field.dart';
1315
import 'package:nextcloud_cookbook_flutter/src/widget/input/integer_text_form_field.dart';
@@ -53,6 +55,19 @@ class _RecipeEditScreenState extends State<RecipeEditScreen> {
5355
String get translationKey =>
5456
widget.recipe != null ? 'recipe_edit' : 'recipe_create';
5557

58+
Future<void> onDelete() async {
59+
final decision = await showDialog<bool>(
60+
context: context,
61+
builder: (context) => DeleteRecipeAlert(recipe: widget.recipe!),
62+
);
63+
64+
if (decision ?? false) {
65+
FocusManager.instance.primaryFocus?.unfocus();
66+
// ignore: use_build_context_synchronously
67+
BlocProvider.of<RecipeBloc>(context).add(RecipeDeleted(widget.recipe!));
68+
}
69+
}
70+
5671
void onSubmit() {
5772
if (_formKey.currentState?.validate() ?? false) {
5873
_formKey.currentState?.save();
@@ -93,6 +108,16 @@ class _RecipeEditScreenState extends State<RecipeEditScreen> {
93108
return Scaffold(
94109
appBar: AppBar(
95110
title: Text(translate('$translationKey.title')),
111+
actions: [
112+
if (widget.recipe != null) ...[
113+
IconButton(
114+
icon: const Icon(Icons.delete),
115+
tooltip: translate("recipe_edit.delete.title").toLowerCase(),
116+
color: Theme.of(context).colorScheme.error,
117+
onPressed: onDelete,
118+
),
119+
],
120+
],
96121
),
97122
body: BlocConsumer<RecipeBloc, RecipeState>(
98123
builder: builder,
@@ -309,6 +334,7 @@ class _RecipeEditScreenState extends State<RecipeEditScreen> {
309334
switch (state.status) {
310335
case RecipeStatus.createFailure:
311336
case RecipeStatus.updateFailure:
337+
case RecipeStatus.deleteFailure:
312338
ScaffoldMessenger.of(context).showSnackBar(
313339
SnackBar(
314340
content: Text(
@@ -326,6 +352,11 @@ class _RecipeEditScreenState extends State<RecipeEditScreen> {
326352
BlocProvider.of<RecipeBloc>(context).add(RecipeLoaded(state.recipeId!));
327353
Navigator.pop(context);
328354
break;
355+
case RecipeStatus.delteSuccess:
356+
BlocProvider.of<CategoriesBloc>(context).add(const CategoriesLoaded());
357+
Navigator.pop(context);
358+
Navigator.pop(context);
359+
break;
329360
case RecipeStatus.createSuccess:
330361
Navigator.pushReplacement(
331362
context,

lib/src/screens/recipe_screen.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class RecipeScreen extends StatelessWidget {
3737
return RecipeScreenBody(recipe: state.recipe!);
3838
case RecipeStatus.loadFailure:
3939
case RecipeStatus.createFailure:
40+
case RecipeStatus.deleteFailure:
4041
case RecipeStatus.updateFailure:
4142
case RecipeStatus.importFailure:
4243
return Scaffold(

lib/src/services/data_repository.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,17 @@ class DataRepository {
4242
Future<String?> updateRecipe(Recipe recipe) async {
4343
final response =
4444
await api.recipeApi.updateRecipe(id: recipe.id!, recipe: recipe);
45-
return response.data?.toString();
45+
return response.data;
4646
}
4747

4848
Future<String?> createRecipe(Recipe recipe) async {
4949
final response = await api.recipeApi.newRecipe(recipe: recipe);
50-
return response.data?.toString();
50+
return response.data;
51+
}
52+
53+
Future<String?> deleteRecipe(Recipe recipe) async {
54+
final response = await api.recipeApi.deleteRecipe(id: recipe.id!);
55+
return response.data;
5156
}
5257

5358
Future<Recipe?> importRecipe(String url) async {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_translate/flutter_translate.dart';
3+
import 'package:nc_cookbook_api/nc_cookbook_api.dart';
4+
5+
class DeleteRecipeAlert extends StatelessWidget {
6+
const DeleteRecipeAlert({
7+
required this.recipe,
8+
super.key,
9+
});
10+
11+
final Recipe recipe;
12+
13+
@override
14+
Widget build(BuildContext context) {
15+
return AlertDialog(
16+
icon: const Icon(Icons.delete_forever),
17+
iconColor: Theme.of(context).colorScheme.error,
18+
title: Text(translate("recipe_edit.delete.title")),
19+
content: Text(
20+
translate(
21+
"recipe_edit.delete.dialog",
22+
args: {"recipe": recipe.name},
23+
),
24+
),
25+
actions: [
26+
TextButton(
27+
onPressed: Navigator.of(context).pop,
28+
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
29+
),
30+
TextButton(
31+
onPressed: () => Navigator.of(context).pop(true),
32+
child: Text(MaterialLocalizations.of(context).deleteButtonTooltip),
33+
),
34+
],
35+
);
36+
}
37+
}

0 commit comments

Comments
 (0)