Start migrating the ingredient cache to a local sqlite database

This commit is contained in:
Roland Geider
2024-09-26 21:18:54 +02:00
parent 15b6eb19b0
commit 8ce9029459
3 changed files with 57 additions and 38 deletions

View File

@@ -18,8 +18,6 @@ class ServiceLocator {
Future<void> _initDB() async { Future<void> _initDB() async {
ExerciseDatabase exerciseDB; ExerciseDatabase exerciseDB;
IngredientDatabase ingredientDB; IngredientDatabase ingredientDB;
// final exerciseDB = ExerciseDatabase();
// final ingredientDB = IngredientDatabase();
if (Platform.environment.containsKey('FLUTTER_TEST')) { if (Platform.environment.containsKey('FLUTTER_TEST')) {
exerciseDB = ExerciseDatabase.inMemory(NativeDatabase.memory()); exerciseDB = ExerciseDatabase.inMemory(NativeDatabase.memory());

View File

@@ -10,6 +10,7 @@ part 'ingredients_database.g.dart';
@DataClassName('IngredientTable') @DataClassName('IngredientTable')
class Ingredients extends Table { class Ingredients extends Table {
const Ingredients(); const Ingredients();
IntColumn get id => integer()(); IntColumn get id => integer()();
TextColumn get data => text()(); TextColumn get data => text()();
@@ -27,6 +28,14 @@ class IngredientDatabase extends _$IngredientDatabase {
@override @override
// TODO: implement schemaVersion // TODO: implement schemaVersion
int get schemaVersion => 1; int get schemaVersion => 1;
Future<void> deleteEverything() {
return transaction(() async {
for (final table in allTables) {
await delete(table).go();
}
});
}
} }
LazyDatabase _openConnection() { LazyDatabase _openConnection() {

View File

@@ -20,7 +20,8 @@ import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:wger/core/locator.dart';
import 'package:wger/database/ingredients/ingredients_database.dart';
import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/exceptions/http_exception.dart';
import 'package:wger/exceptions/no_such_entry_exception.dart'; import 'package:wger/exceptions/no_such_entry_exception.dart';
import 'package:wger/helpers/consts.dart'; import 'package:wger/helpers/consts.dart';
@@ -43,10 +44,15 @@ class NutritionPlansProvider with ChangeNotifier {
static const _nutritionDiaryPath = 'nutritiondiary'; static const _nutritionDiaryPath = 'nutritiondiary';
final WgerBaseProvider baseProvider; final WgerBaseProvider baseProvider;
late IngredientDatabase database;
List<NutritionalPlan> _plans = []; List<NutritionalPlan> _plans = [];
List<Ingredient> _ingredients = []; List<Ingredient> _ingredients = [];
NutritionPlansProvider(this.baseProvider, List<NutritionalPlan> entries) : _plans = entries; NutritionPlansProvider(this.baseProvider, List<NutritionalPlan> entries,
{IngredientDatabase? database})
: _plans = entries {
this.database = database ?? locator<IngredientDatabase>();
}
List<NutritionalPlan> get items { List<NutritionalPlan> get items {
return [..._plans]; return [..._plans];
@@ -286,6 +292,10 @@ class NutritionPlansProvider with ChangeNotifier {
} }
} }
Future<void> clearIngredientCaches() async {
await database.deleteEverything();
}
/// Fetch and return an ingredient /// Fetch and return an ingredient
/// ///
/// If the ingredient is not known locally, it is fetched from the server /// If the ingredient is not known locally, it is fetched from the server
@@ -294,46 +304,48 @@ class NutritionPlansProvider with ChangeNotifier {
try { try {
ingredient = _ingredients.firstWhere((e) => e.id == ingredientId); ingredient = _ingredients.firstWhere((e) => e.id == ingredientId);
return ingredient;
// Get ingredient from the server and save to cache
} on StateError { } on StateError {
final data = await baseProvider.fetch( final ingredientDb = await (database.select(database.ingredients)
baseProvider.makeUrl(_ingredientInfoPath, id: ingredientId), ..where((e) => e.id.equals(ingredientId)))
); .getSingleOrNull();
ingredient = Ingredient.fromJson(data);
_ingredients.add(ingredient);
final prefs = await SharedPreferences.getInstance(); // Try to fetch from local db
final ingredientData = json.decode(prefs.getString(PREFS_INGREDIENTS)!); if (ingredientDb != null) {
ingredientData['ingredients'].add(ingredient.toJson()); ingredient = Ingredient.fromJson(jsonDecode(ingredientDb.data));
prefs.setString(PREFS_INGREDIENTS, json.encode(ingredientData));
log("Saved ingredient '${ingredient.name}' to cache.");
return ingredient; // Prune old entries
} if (DateTime.now()
} .isAfter(ingredientDb.lastUpdate.add(const Duration(days: DAYS_TO_CACHE)))) {
(database.delete(database.ingredients)..where((i) => i.id.isValue(ingredientId))).go();
}
} else {
final data = await baseProvider.fetch(
baseProvider.makeUrl(_ingredientInfoPath, id: ingredientId),
);
ingredient = Ingredient.fromJson(data);
_ingredients.add(ingredient);
/// Loads the available ingredients from the local storage database.into(database.ingredients).insert(
Future<void> fetchIngredientsFromCache() async { IngredientsCompanion.insert(
// Load exercises from cache, if available id: ingredientId,
final prefs = await SharedPreferences.getInstance(); data: jsonEncode(data),
if (prefs.containsKey(PREFS_INGREDIENTS)) { lastUpdate: DateTime.now(),
final ingredientData = json.decode(prefs.getString(PREFS_INGREDIENTS)!); ),
if (DateTime.parse(ingredientData['expiresIn']).isAfter(DateTime.now())) { );
ingredientData['ingredients'].forEach((e) => _ingredients.add(Ingredient.fromJson(e))); log("Saved ingredient '${ingredient.name}' to db cache");
log("Read ${ingredientData['ingredients'].length} ingredients from cache. Valid till ${ingredientData['expiresIn']}");
return;
} }
} }
// Initialise an empty cache return ingredient;
final ingredientData = { }
'date': DateTime.now().toIso8601String(),
'expiresIn': DateTime.now().add(const Duration(days: DAYS_TO_CACHE)).toIso8601String(), /// Loads the available ingredients from the local cache
'ingredients': [], Future<void> fetchIngredientsFromCache() async {
}; final ingredientDb = await database.select(database.ingredients).get();
prefs.setString(PREFS_INGREDIENTS, json.encode(ingredientData)); log("Read ${ingredientDb.length} ingredients from db cache");
if (ingredientDb.isNotEmpty) {
ingredients = ingredientDb.map((e) => Ingredient.fromJson(jsonDecode(e.data))).toList();
}
} }
/// Searches for an ingredient /// Searches for an ingredient
@@ -432,7 +444,7 @@ class NutritionPlansProvider with ChangeNotifier {
baseProvider.makeUrl( baseProvider.makeUrl(
_nutritionDiaryPath, _nutritionDiaryPath,
query: { query: {
'plan': plan.id.toString(), 'plan': plan.id?.toString(),
'limit': '999', 'limit': '999',
'ordering': 'datetime', 'ordering': 'datetime',
}, },