@@ -20,13 +20,16 @@ import 'package:collection/collection.dart';
20
20
import 'package:flutter/widgets.dart' ;
21
21
import 'package:flutter_gen/gen_l10n/app_localizations.dart' ;
22
22
import 'package:json_annotation/json_annotation.dart' ;
23
+ import 'package:powersync/sqlite3.dart' as sqlite;
23
24
import 'package:wger/helpers/consts.dart' ;
24
25
import 'package:wger/helpers/json.dart' ;
25
26
import 'package:wger/models/nutrition/log.dart' ;
26
27
import 'package:wger/models/nutrition/meal.dart' ;
27
28
import 'package:wger/models/nutrition/meal_item.dart' ;
28
29
import 'package:wger/models/nutrition/nutritional_goals.dart' ;
29
30
import 'package:wger/models/nutrition/nutritional_values.dart' ;
31
+ import 'package:wger/models/schema.dart' ;
32
+ import 'package:wger/powersync.dart' ;
30
33
31
34
part 'nutritional_plan.g.dart' ;
32
35
@@ -82,6 +85,55 @@ class NutritionalPlan {
82
85
this .diaryEntries = diaryEntries ?? [];
83
86
}
84
87
88
+ factory NutritionalPlan .fromRow (sqlite.Row row) {
89
+ return NutritionalPlan (
90
+ id: int .parse (row['id' ]),
91
+ description: row['description' ],
92
+ creationDate: DateTime .parse (row['creation_date' ]),
93
+ onlyLogging: row['only_logging' ] == 1 ,
94
+ goalEnergy: row['goal_energy' ],
95
+ goalProtein: row['goal_protein' ],
96
+ goalCarbohydrates: row['goal_carbohydrates' ],
97
+ goalFat: row['goal_fat' ],
98
+ goalFiber: row['goal_fiber' ],
99
+ );
100
+ }
101
+
102
+ NutritionalPlan copyWith ({
103
+ int ? id,
104
+ String ? description,
105
+ DateTime ? creationDate,
106
+ bool ? onlyLogging,
107
+ num ? goalEnergy,
108
+ num ? goalProtein,
109
+ num ? goalCarbohydrates,
110
+ num ? goalFat,
111
+ num ? goalFiber,
112
+ List <Meal >? meals,
113
+ List <Log >? diaryEntries,
114
+ }) {
115
+ return NutritionalPlan (
116
+ id: id ?? this .id,
117
+ description: description ?? this .description,
118
+ creationDate: creationDate ?? this .creationDate,
119
+ onlyLogging: onlyLogging ?? this .onlyLogging,
120
+ goalEnergy: goalEnergy ?? this .goalEnergy,
121
+ goalProtein: goalProtein ?? this .goalProtein,
122
+ goalCarbohydrates: goalCarbohydrates ?? this .goalCarbohydrates,
123
+ goalFat: goalFat ?? this .goalFat,
124
+ goalFiber: goalFiber ?? this .goalFiber,
125
+ meals: meals ?? this .meals,
126
+ diaryEntries: diaryEntries ?? this .diaryEntries,
127
+ );
128
+ }
129
+
130
+ Future <NutritionalPlan > loadChildren () async {
131
+ return copyWith (
132
+ diaryEntries: await Log .readByPlanId (id! ),
133
+ meals: await Meal .readByPlanId (id! ),
134
+ );
135
+ }
136
+
85
137
NutritionalPlan .empty () {
86
138
creationDate = DateTime .now ();
87
139
description = '' ;
@@ -246,4 +298,65 @@ class NutritionalPlan {
246
298
diaryEntries: diaryEntries.where ((e) => e.mealId == null ).toList (),
247
299
);
248
300
}
301
+
302
+ static Future <NutritionalPlan > read (int id) async {
303
+ final row = await db.get ('SELECT * FROM $tableNutritionPlans WHERE id = ?' , [id]);
304
+ return NutritionalPlan .fromRow (row).loadChildren ();
305
+ }
306
+
307
+ // this is a bit complicated.
308
+ // what we need at the end of the day, is a stream of List<NutritionPlan>, where
309
+ // a new value is emitted any time a plan is changed. But the plan is not just the plan record
310
+ // we need to load data for Logs and Meals corresponding to the plan also.
311
+ // so our options are:
312
+ // 1) db.watch with a select query on plans; and extra dart code to load the logs/meals stuff,
313
+ // but this only triggers for updates on the plans table, and misses logs/meals updates
314
+ // 2) db.watch with a huge join query across all tables from which we need info,
315
+ // so we have all the data in our resultset to create the datastructures with, but:
316
+ // - this creates long rows with lots of duplicated data (e.g. all the plan data) for every row
317
+ // which would only differ for e.g. the meal or the log item
318
+ // - it would probably get a bit messy to parse the resultset into the datastructures
319
+ // 3) the best of both worlds: load the data we need in dart at runtime, but explicitly
320
+ // trigger our code execution when *any* of the relevant tables changes
321
+ //
322
+ static Stream <List <NutritionalPlan >> watchNutritionPlans () {
323
+ return db.onChange ([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap ((event) async {
324
+ final data = await db.getAll ('SELECT * FROM $tableNutritionPlans ORDER BY creation_date' );
325
+ final futures = Future .wait (data.map ((row) => NutritionalPlan .fromRow (row).loadChildren ()));
326
+ return (await futures).toList (growable: false );
327
+ });
328
+ }
329
+
330
+ static Stream <NutritionalPlan > watchNutritionPlan (int id) {
331
+ return db.onChange ([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap ((event) async {
332
+ final row = await db.get ('SELECT * FROM $tableNutritionPlans WHERE id = ?' , [id]);
333
+ return NutritionalPlan .fromRow (row).loadChildren ();
334
+ });
335
+ }
336
+
337
+ static Stream <NutritionalPlan > watchNutritionPlanLast () {
338
+ return db.onChange ([tableNutritionPlans, tableLogItems, tableMeals]).asyncMap ((event) async {
339
+ final row =
340
+ await db.get ('SELECT * FROM $tableNutritionPlans ORDER BY creation_date DESC LIMIT 1' );
341
+ return NutritionalPlan .fromRow (row).loadChildren ();
342
+ });
343
+ }
344
+ /*
345
+ static Stream<List<NutritionalPlan>> watchNutritionPlan(int id) {
346
+ return db
347
+ .watch('SELECT * FROM $tableNutritionPlans WHERE id = ?', parameters: [id]).map((results) {
348
+ return results.map(NutritionalPlan.fromRow).toList(growable: false);
349
+ });
350
+ }
351
+
352
+ static Stream<List<NutritionalPlan>> watchNutritionPlans() {
353
+ return db.watch('SELECT * FROM $tableNutritionPlans ORDER BY creation_date').map((results) {
354
+ return results.map(NutritionalPlan.fromRow).toList(growable: false);
355
+ });
356
+ }
357
+ */
358
+
359
+ Future <void > delete () async {
360
+ await db.execute ('DELETE FROM $tableNutritionPlans WHERE id = ?' , [id]);
361
+ }
249
362
}
0 commit comments