Skip to content

Commit 53c674b

Browse files
authored
Merge pull request #594 from wger-project/recent-ingredient-suggestions-tweaks
Recent ingredient suggestions deduping and filtering
2 parents 68799b0 + 686fb44 commit 53c674b

File tree

6 files changed

+48
-21
lines changed

6 files changed

+48
-21
lines changed

lib/models/nutrition/nutritional_plan.dart

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
*/
1818

19+
import 'package:collection/collection.dart';
1920
import 'package:flutter/widgets.dart';
2021
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
2122
import 'package:json_annotation/json_annotation.dart';
@@ -191,23 +192,37 @@ class NutritionalPlan {
191192
return out;
192193
}
193194

194-
/// Helper that returns all meal items for the current plan
195-
///
196-
/// Duplicated ingredients are removed
197-
List<MealItem> get allMealItems {
195+
/// returns meal items across all meals
196+
/// deduped by the combination of amount and ingredient ID
197+
List<MealItem> get dedupMealItems {
198198
final List<MealItem> out = [];
199199
for (final meal in meals) {
200200
for (final mealItem in meal.mealItems) {
201-
final ingredientInList = out.where((e) => e.ingredientId == mealItem.ingredientId);
201+
final found = out.firstWhereOrNull(
202+
(e) => e.amount == mealItem.amount && e.ingredientId == mealItem.ingredientId);
202203

203-
if (ingredientInList.isEmpty) {
204+
if (found == null) {
204205
out.add(mealItem);
205206
}
206207
}
207208
}
208209
return out;
209210
}
210211

212+
/// returns diary entries
213+
/// deduped by the combination of amount and ingredient ID
214+
List<Log> get dedupDiaryEntries {
215+
final out = <Log>[];
216+
for (final log in diaryEntries) {
217+
final found =
218+
out.firstWhereOrNull((e) => e.amount == log.amount && e.ingredientId == log.ingredientId);
219+
if (found == null) {
220+
out.add(log);
221+
}
222+
}
223+
return out;
224+
}
225+
211226
Meal pseudoMealOthers(String name) {
212227
return Meal(
213228
id: PSEUDO_MEAL_ID,

lib/widgets/nutrition/forms.dart

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ Widget MealItemForm(Meal meal, List<MealItem> recent, [String? barcode, bool? te
131131

132132
Widget IngredientLogForm(NutritionalPlan plan) {
133133
return IngredientForm(
134-
recent: plan.diaryEntries,
134+
recent: plan.dedupDiaryEntries,
135135
onSave: (BuildContext context, MealItem mealItem, DateTime? dt) {
136136
Provider.of<NutritionPlansProvider>(context, listen: false)
137137
.logIngredientToDiary(mealItem, plan.id!, dt);
@@ -168,6 +168,7 @@ class IngredientFormState extends State<IngredientForm> {
168168
final _dateController = TextEditingController(); // optional
169169
final _timeController = TextEditingController(); // optional
170170
final _mealItem = MealItem.empty();
171+
var _searchQuery = ''; // copy from typeahead. for filtering suggestions
171172

172173
@override
173174
void initState() {
@@ -201,10 +202,18 @@ class IngredientFormState extends State<IngredientForm> {
201202
});
202203
}
203204

205+
void updateSearchQuery(String query) {
206+
setState(() {
207+
_searchQuery = query;
208+
});
209+
}
210+
204211
@override
205212
Widget build(BuildContext context) {
206213
final String unit = AppLocalizations.of(context).g;
207-
214+
final queryLower = _searchQuery.toLowerCase();
215+
final suggestions =
216+
widget.recent.where((e) => e.ingredient.name.toLowerCase().contains(queryLower)).toList();
208217
return Container(
209218
margin: const EdgeInsets.all(20),
210219
child: Form(
@@ -218,6 +227,7 @@ class IngredientFormState extends State<IngredientForm> {
218227
test: widget.test,
219228
selectIngredient: selectIngredient,
220229
unSelectIngredient: unSelectIngredient,
230+
updateSearchQuery: updateSearchQuery,
221231
),
222232
Row(
223233
children: [
@@ -370,27 +380,26 @@ class IngredientFormState extends State<IngredientForm> {
370380
Navigator.of(context).pop();
371381
},
372382
),
373-
if (widget.recent.isNotEmpty) const SizedBox(height: 10.0),
383+
if (suggestions.isNotEmpty) const SizedBox(height: 10.0),
374384
Container(
375385
padding: const EdgeInsets.all(10.0),
376386
child: Text(AppLocalizations.of(context).recentlyUsedIngredients),
377387
),
378388
Expanded(
379389
child: ListView.builder(
380-
itemCount: widget.recent.length,
390+
itemCount: suggestions.length,
381391
shrinkWrap: true,
382392
itemBuilder: (context, index) {
383393
return Card(
384394
child: ListTile(
385395
onTap: () {
386-
final ingredient = widget.recent[index].ingredient;
387-
selectIngredient(
388-
ingredient.id, ingredient.name, widget.recent[index].amount);
396+
final ingredient = suggestions[index].ingredient;
397+
selectIngredient(ingredient.id, ingredient.name, suggestions[index].amount);
389398
},
390399
title: Text(
391-
'${widget.recent[index].ingredient.name} (${widget.recent[index].amount.toStringAsFixed(0)}$unit)'),
400+
'${suggestions[index].ingredient.name} (${suggestions[index].amount.toStringAsFixed(0)}$unit)'),
392401
subtitle: Text(getShortNutritionValues(
393-
widget.recent[index].ingredient.nutritionalValues, context)),
402+
suggestions[index].ingredient.nutritionalValues, context)),
394403
trailing: const Icon(Icons.copy),
395404
),
396405
);

lib/widgets/nutrition/meal.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ enum viewMode {
3939

4040
class MealWidget extends StatefulWidget {
4141
final Meal _meal;
42-
final List<MealItem> _listMealItems;
42+
final List<MealItem> _recentMealItems;
4343

4444
const MealWidget(
4545
this._meal,
46-
this._listMealItems,
46+
this._recentMealItems,
4747
);
4848

4949
@override
@@ -108,7 +108,7 @@ class _MealWidgetState extends State<MealWidget> {
108108
FormScreen.routeName,
109109
arguments: FormScreenArguments(
110110
AppLocalizations.of(context).addIngredient,
111-
MealItemForm(widget._meal, widget._listMealItems),
111+
MealItemForm(widget._meal, widget._recentMealItems),
112112
hasListView: true,
113113
),
114114
);

lib/widgets/nutrition/nutritional_plan_detail.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ class NutritionalPlanDetailWidget extends StatelessWidget {
5656
const SizedBox(height: 10),
5757
..._nutritionalPlan.meals.map((meal) => MealWidget(
5858
meal,
59-
_nutritionalPlan.allMealItems,
59+
_nutritionalPlan.dedupMealItems,
6060
)),
6161
MealWidget(
6262
_nutritionalPlan.pseudoMealOthers('Other logs'),
63-
_nutritionalPlan.allMealItems,
63+
_nutritionalPlan.dedupMealItems,
6464
),
6565
if (!_nutritionalPlan.onlyLogging)
6666
Padding(

lib/widgets/nutrition/widgets.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class IngredientTypeahead extends StatefulWidget {
6565

6666
final Function(int id, String name, num? amount) selectIngredient;
6767
final Function() unSelectIngredient;
68+
final Function(String query) updateSearchQuery;
6869

6970
const IngredientTypeahead(
7071
this._ingredientIdController,
@@ -74,6 +75,7 @@ class IngredientTypeahead extends StatefulWidget {
7475
this.barcode = '',
7576
required this.selectIngredient,
7677
required this.unSelectIngredient,
78+
required this.updateSearchQuery,
7779
});
7880

7981
@override
@@ -125,6 +127,7 @@ class _IngredientTypeaheadState extends State<IngredientTypeahead> {
125127
return null;
126128
},
127129
onChanged: (value) {
130+
widget.updateSearchQuery(value);
128131
// unselect to start a new search
129132
widget.unSelectIngredient();
130133
},

test/nutrition/nutritional_plan_model_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ void main() {
101101
});
102102

103103
test('Test that the getter returns all meal items for a plan', () {
104-
expect(plan.allMealItems, plan.meals[0].mealItems + plan.meals[1].mealItems);
104+
expect(plan.dedupMealItems, plan.meals[0].mealItems + plan.meals[1].mealItems);
105105
});
106106
});
107107
}

0 commit comments

Comments
 (0)