Skip to content

Commit 11a7c30

Browse files
authored
Merge pull request #613 from wger-project/ingredient-details
popup ingredient details in typeahead suggestions and recently used
2 parents 5bf903c + dc1f220 commit 11a7c30

24 files changed

+3038
-404
lines changed

lib/models/nutrition/ingredient.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,27 @@ part 'ingredient.g.dart';
2424

2525
@JsonSerializable()
2626
class Ingredient {
27+
// fields returned by django api that we ignore here:
28+
// uuid, last_updated, last_imported, weight_units, language
29+
// most license fields
30+
2731
@JsonKey(required: true)
2832
final int id;
2933

34+
// some ingredients don't have these 3 fields set. E.g. USDA entries that
35+
// have been removed upstream, or manually added ingredients.
36+
@JsonKey(required: true, name: 'remote_id')
37+
final String? remoteId;
38+
39+
@JsonKey(required: true, name: 'source_name')
40+
final String? sourceName;
41+
42+
@JsonKey(required: true, name: 'source_url')
43+
final String? sourceUrl;
44+
45+
@JsonKey(required: true, name: 'license_object_url')
46+
final String? licenseObjectURl;
47+
3048
/// Barcode of the product
3149
@JsonKey(required: true)
3250
final String? code;
@@ -73,6 +91,10 @@ class Ingredient {
7391
IngredientImage? image;
7492

7593
Ingredient({
94+
required this.remoteId,
95+
required this.sourceName,
96+
required this.sourceUrl,
97+
this.licenseObjectURl,
7698
required this.id,
7799
required this.code,
78100
required this.name,

lib/models/nutrition/ingredient.g.dart

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/models/nutrition/nutritional_values.dart

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

19+
import 'package:wger/models/nutrition/nutritional_goals.dart';
20+
1921
class NutritionalValues {
2022
double energy = 0;
2123
double protein = 0;
@@ -114,6 +116,19 @@ class NutritionalValues {
114116
return 'e: $energy, p: $protein, c: $carbohydrates, cS: $carbohydratesSugar, f: $fat, fS: $fatSaturated, fi: $fiber, s: $sodium';
115117
}
116118

119+
NutritionalGoals toGoals() {
120+
return NutritionalGoals(
121+
energy: energy,
122+
protein: protein,
123+
carbohydrates: carbohydrates,
124+
carbohydratesSugar: carbohydratesSugar,
125+
fat: fat,
126+
fatSaturated: fatSaturated,
127+
fiber: fiber,
128+
sodium: sodium,
129+
);
130+
}
131+
117132
@override
118133
//ignore: avoid_equals_and_hash_code_on_mutable_classes
119134
int get hashCode => Object.hash(

lib/providers/nutrition.dart

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,9 @@ class NutritionPlansProvider with ChangeNotifier {
3838
static const _nutritionalPlansInfoPath = 'nutritionplaninfo';
3939
static const _mealPath = 'meal';
4040
static const _mealItemPath = 'mealitem';
41-
static const _ingredientPath = 'ingredient';
41+
static const _ingredientInfoPath = 'ingredientinfo';
4242
static const _ingredientSearchPath = 'ingredient/search';
4343
static const _nutritionDiaryPath = 'nutritiondiary';
44-
static const _ingredientImagePath = 'ingredient-image';
4544

4645
final WgerBaseProvider baseProvider;
4746
List<NutritionalPlan> _plans = [];
@@ -131,6 +130,7 @@ class NutritionPlansProvider with ChangeNotifier {
131130
try {
132131
plan = findById(planId);
133132
} on NoSuchEntryException {
133+
// TODO: remove this useless call, because we will fetch all details below
134134
plan = await fetchAndSetPlanSparse(planId);
135135
}
136136

@@ -144,6 +144,7 @@ class NutritionPlansProvider with ChangeNotifier {
144144
final List<MealItem> mealItems = [];
145145
final meal = Meal.fromJson(mealData);
146146

147+
// TODO: we should add these ingredients to the ingredient cache
147148
for (final mealItemData in mealData['meal_items']) {
148149
final mealItem = MealItem.fromJson(mealItemData);
149150

@@ -298,7 +299,7 @@ class NutritionPlansProvider with ChangeNotifier {
298299
// Get ingredient from the server and save to cache
299300
} on StateError {
300301
final data = await baseProvider.fetch(
301-
baseProvider.makeUrl(_ingredientPath, id: ingredientId),
302+
baseProvider.makeUrl(_ingredientInfoPath, id: ingredientId),
302303
);
303304
ingredient = Ingredient.fromJson(data);
304305
_ingredients.add(ingredient);
@@ -370,14 +371,15 @@ class NutritionPlansProvider with ChangeNotifier {
370371

371372
// Send the request
372373
final data = await baseProvider.fetch(
373-
baseProvider.makeUrl(_ingredientPath, query: {'code': code}),
374+
baseProvider.makeUrl(_ingredientInfoPath, query: {'code': code}),
374375
);
375376

376377
if (data['count'] == 0) {
377378
return null;
378-
} else {
379-
return Ingredient.fromJson(data['results'][0]);
380379
}
380+
// TODO we should probably add it to ingredient cache.
381+
// TODO: we could also use the ingredient cache for code searches
382+
return Ingredient.fromJson(data['results'][0]);
381383
}
382384

383385
/// Log meal to nutrition diary

lib/screens/home_tabs_screen.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
9494
exercisesProvider.fetchAndSetInitialData(),
9595
]);
9696
} catch (e) {
97-
log('fire! fire!');
97+
log('Exception loading base data');
9898
log(e.toString());
9999
}
100100

@@ -109,7 +109,7 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
109109
measurementProvider.fetchAndSetAllCategoriesAndEntries(),
110110
]);
111111
} catch (e) {
112-
log('fire! fire!');
112+
log('Exception loading plans, weight, measurements and gallery');
113113
log(e.toString());
114114
}
115115

@@ -121,7 +121,7 @@ class _HomeTabsScreenState extends State<HomeTabsScreen> with SingleTickerProvid
121121
await nutritionPlansProvider.fetchAndSetPlanFull(plan.id!);
122122
}
123123
} catch (e) {
124-
log('fire! fire!');
124+
log('Exception loading current nutritional plan');
125125
log(e.toString());
126126
}
127127

lib/widgets/nutrition/forms.dart

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ class IngredientFormState extends State<IngredientForm> {
353353
child: Column(
354354
children: [
355355
Text(
356-
'Macros preview',
356+
'Macros preview', // TODO fix l10n
357357
style: Theme.of(context).textTheme.titleMedium,
358358
),
359359
FutureBuilder<Ingredient>(
@@ -430,24 +430,42 @@ class IngredientFormState extends State<IngredientForm> {
430430
itemCount: suggestions.length,
431431
shrinkWrap: true,
432432
itemBuilder: (context, index) {
433+
void select() {
434+
final ingredient = suggestions[index].ingredient;
435+
selectIngredient(
436+
ingredient.id,
437+
ingredient.name,
438+
suggestions[index].amount,
439+
);
440+
}
441+
433442
return Card(
434443
child: ListTile(
435-
onTap: () {
436-
final ingredient = suggestions[index].ingredient;
437-
selectIngredient(
438-
ingredient.id,
439-
ingredient.name,
440-
suggestions[index].amount,
441-
);
442-
},
444+
onTap: select,
443445
title: Text(
444446
'${suggestions[index].ingredient.name} (${suggestions[index].amount.toStringAsFixed(0)}$unit)',
445447
),
446448
subtitle: Text(getShortNutritionValues(
447449
suggestions[index].ingredient.nutritionalValues,
448450
context,
449451
)),
450-
trailing: const Icon(Icons.copy),
452+
trailing: Row(
453+
mainAxisSize: MainAxisSize.min,
454+
children: [
455+
IconButton(
456+
icon: const Icon(Icons.info_outline),
457+
onPressed: () {
458+
showIngredientDetails(
459+
context,
460+
suggestions[index].ingredient.id,
461+
select: select,
462+
);
463+
},
464+
),
465+
const SizedBox(width: 5),
466+
const Icon(Icons.copy),
467+
],
468+
),
451469
),
452470
);
453471
},

lib/widgets/nutrition/helpers.dart

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

19-
import 'package:flutter/cupertino.dart';
2019
import 'package:flutter/material.dart';
2120
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
21+
import 'package:provider/provider.dart';
22+
import 'package:wger/models/nutrition/ingredient.dart';
2223
import 'package:wger/models/nutrition/meal.dart';
2324
import 'package:wger/models/nutrition/nutritional_values.dart';
25+
import 'package:wger/providers/nutrition.dart';
2426
import 'package:wger/widgets/core/core.dart';
27+
import 'package:wger/widgets/nutrition/ingredient_dialogs.dart';
2528

2629
List<String> getNutritionColumnNames(BuildContext context) => [
2730
AppLocalizations.of(context).energy,
@@ -95,3 +98,15 @@ String getKcalConsumedVsPlanned(Meal meal, BuildContext context) {
9598

9699
return '${consumed.toStringAsFixed(0)} / ${planned.toStringAsFixed(0)} ${loc.kcal}';
97100
}
101+
102+
void showIngredientDetails(BuildContext context, int id, {void Function()? select}) {
103+
showDialog(
104+
context: context,
105+
builder: (context) => FutureBuilder<Ingredient>(
106+
future: Provider.of<NutritionPlansProvider>(context, listen: false).fetchIngredient(id),
107+
builder: (BuildContext context, AsyncSnapshot<Ingredient> snapshot) {
108+
return IngredientDetails(snapshot, select: select);
109+
},
110+
),
111+
);
112+
}

0 commit comments

Comments
 (0)