@@ -20,7 +20,8 @@ import 'dart:convert';
20
20
import 'dart:developer' ;
21
21
22
22
import 'package:flutter/material.dart' ;
23
- import 'package:shared_preferences/shared_preferences.dart' ;
23
+ import 'package:wger/core/locator.dart' ;
24
+ import 'package:wger/database/ingredients/ingredients_database.dart' ;
24
25
import 'package:wger/exceptions/http_exception.dart' ;
25
26
import 'package:wger/exceptions/no_such_entry_exception.dart' ;
26
27
import 'package:wger/helpers/consts.dart' ;
@@ -43,10 +44,15 @@ class NutritionPlansProvider with ChangeNotifier {
43
44
static const _nutritionDiaryPath = 'nutritiondiary' ;
44
45
45
46
final WgerBaseProvider baseProvider;
47
+ late IngredientDatabase database;
46
48
List <NutritionalPlan > _plans = [];
47
49
List <Ingredient > _ingredients = [];
48
50
49
- NutritionPlansProvider (this .baseProvider, List <NutritionalPlan > entries) : _plans = entries;
51
+ NutritionPlansProvider (this .baseProvider, List <NutritionalPlan > entries,
52
+ {IngredientDatabase ? database})
53
+ : _plans = entries {
54
+ this .database = database ?? locator <IngredientDatabase >();
55
+ }
50
56
51
57
List <NutritionalPlan > get items {
52
58
return [..._plans];
@@ -286,6 +292,10 @@ class NutritionPlansProvider with ChangeNotifier {
286
292
}
287
293
}
288
294
295
+ Future <void > clearIngredientCaches () async {
296
+ await database.deleteEverything ();
297
+ }
298
+
289
299
/// Fetch and return an ingredient
290
300
///
291
301
/// If the ingredient is not known locally, it is fetched from the server
@@ -294,46 +304,48 @@ class NutritionPlansProvider with ChangeNotifier {
294
304
295
305
try {
296
306
ingredient = _ingredients.firstWhere ((e) => e.id == ingredientId);
297
- return ingredient;
298
-
299
- // Get ingredient from the server and save to cache
300
307
} on StateError {
301
- final data = await baseProvider.fetch (
302
- baseProvider.makeUrl (_ingredientInfoPath, id: ingredientId),
303
- );
304
- ingredient = Ingredient .fromJson (data);
305
- _ingredients.add (ingredient);
306
-
307
- final prefs = await SharedPreferences .getInstance ();
308
- final ingredientData = json.decode (prefs.getString (PREFS_INGREDIENTS )! );
309
- ingredientData['ingredients' ].add (ingredient.toJson ());
310
- prefs.setString (PREFS_INGREDIENTS , json.encode (ingredientData));
311
- log ("Saved ingredient '${ingredient .name }' to cache." );
312
-
313
- return ingredient;
308
+ final ingredientDb = await (database.select (database.ingredients)
309
+ ..where ((e) => e.id.equals (ingredientId)))
310
+ .getSingleOrNull ();
311
+
312
+ // Try to fetch from local db
313
+ if (ingredientDb != null ) {
314
+ ingredient = Ingredient .fromJson (jsonDecode (ingredientDb.data));
315
+
316
+ // Prune old entries
317
+ if (DateTime .now ()
318
+ .isAfter (ingredientDb.lastUpdate.add (const Duration (days: DAYS_TO_CACHE )))) {
319
+ (database.delete (database.ingredients)..where ((i) => i.id.isValue (ingredientId))).go ();
320
+ }
321
+ } else {
322
+ final data = await baseProvider.fetch (
323
+ baseProvider.makeUrl (_ingredientInfoPath, id: ingredientId),
324
+ );
325
+ ingredient = Ingredient .fromJson (data);
326
+ _ingredients.add (ingredient);
327
+
328
+ database.into (database.ingredients).insert (
329
+ IngredientsCompanion .insert (
330
+ id: ingredientId,
331
+ data: jsonEncode (data),
332
+ lastUpdate: DateTime .now (),
333
+ ),
334
+ );
335
+ log ("Saved ingredient '${ingredient .name }' to db cache" );
336
+ }
314
337
}
338
+
339
+ return ingredient;
315
340
}
316
341
317
- /// Loads the available ingredients from the local storage
342
+ /// Loads the available ingredients from the local cache
318
343
Future <void > fetchIngredientsFromCache () async {
319
- // Load exercises from cache, if available
320
- final prefs = await SharedPreferences .getInstance ();
321
- if (prefs.containsKey (PREFS_INGREDIENTS )) {
322
- final ingredientData = json.decode (prefs.getString (PREFS_INGREDIENTS )! );
323
- if (DateTime .parse (ingredientData['expiresIn' ]).isAfter (DateTime .now ())) {
324
- ingredientData['ingredients' ].forEach ((e) => _ingredients.add (Ingredient .fromJson (e)));
325
- log ("Read ${ingredientData ['ingredients' ].length } ingredients from cache. Valid till ${ingredientData ['expiresIn' ]}" );
326
- return ;
327
- }
344
+ final ingredientDb = await database.select (database.ingredients).get ();
345
+ log ("Read ${ingredientDb .length } ingredients from db cache" );
346
+ if (ingredientDb.isNotEmpty) {
347
+ ingredients = ingredientDb.map ((e) => Ingredient .fromJson (jsonDecode (e.data))).toList ();
328
348
}
329
-
330
- // Initialise an empty cache
331
- final ingredientData = {
332
- 'date' : DateTime .now ().toIso8601String (),
333
- 'expiresIn' : DateTime .now ().add (const Duration (days: DAYS_TO_CACHE )).toIso8601String (),
334
- 'ingredients' : [],
335
- };
336
- prefs.setString (PREFS_INGREDIENTS , json.encode (ingredientData));
337
349
}
338
350
339
351
/// Searches for an ingredient
@@ -432,7 +444,7 @@ class NutritionPlansProvider with ChangeNotifier {
432
444
baseProvider.makeUrl (
433
445
_nutritionDiaryPath,
434
446
query: {
435
- 'plan' : plan.id.toString (),
447
+ 'plan' : plan.id? .toString (),
436
448
'limit' : '999' ,
437
449
'ordering' : 'datetime' ,
438
450
},
0 commit comments