diff --git a/lib/database/exercises/exercise_database.dart b/lib/database/exercises/exercise_database.dart index fa3969c6..64082ff3 100644 --- a/lib/database/exercises/exercise_database.dart +++ b/lib/database/exercises/exercise_database.dart @@ -21,6 +21,11 @@ class Exercises extends Table { // TextColumn get data => text().map(const ExerciseBaseConverter())(); DateTimeColumn get lastUpdate => dateTime()(); + + /// The date when the exercise was last fetched from the API. While we know + /// when the exercise itself was last updated in `lastUpdate`, we can save + /// ourselves a lot of requests if we don't check too often + DateTimeColumn get lastFetched => dateTime()(); } @DataClassName('MuscleTable') diff --git a/lib/database/exercises/exercise_database.g.dart b/lib/database/exercises/exercise_database.g.dart index 4c5ea73d..4109167e 100644 --- a/lib/database/exercises/exercise_database.g.dart +++ b/lib/database/exercises/exercise_database.g.dart @@ -21,8 +21,13 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise late final GeneratedColumn lastUpdate = GeneratedColumn( 'last_update', aliasedName, false, type: DriftSqlType.dateTime, requiredDuringInsert: true); + static const VerificationMeta _lastFetchedMeta = const VerificationMeta('lastFetched'); @override - List get $columns => [id, data, lastUpdate]; + late final GeneratedColumn lastFetched = GeneratedColumn( + 'last_fetched', aliasedName, false, + type: DriftSqlType.dateTime, requiredDuringInsert: true); + @override + List get $columns => [id, data, lastUpdate, lastFetched]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -49,6 +54,12 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise } else if (isInserting) { context.missing(_lastUpdateMeta); } + if (data.containsKey('last_fetched')) { + context.handle(_lastFetchedMeta, + lastFetched.isAcceptableOrUnknown(data['last_fetched']!, _lastFetchedMeta)); + } else if (isInserting) { + context.missing(_lastFetchedMeta); + } return context; } @@ -62,6 +73,8 @@ class $ExercisesTable extends Exercises with TableInfo<$ExercisesTable, Exercise data: attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}data'])!, lastUpdate: attachedDatabase.typeMapping .read(DriftSqlType.dateTime, data['${effectivePrefix}last_update'])!, + lastFetched: attachedDatabase.typeMapping + .read(DriftSqlType.dateTime, data['${effectivePrefix}last_fetched'])!, ); } @@ -75,13 +88,20 @@ class ExerciseTable extends DataClass implements Insertable { final int id; final String data; final DateTime lastUpdate; - const ExerciseTable({required this.id, required this.data, required this.lastUpdate}); + + /// The date when the exercise was last fetched from the API. While we know + /// when the exercise itself was last updated in `lastUpdate`, we can save + /// ourselves a lot of requests if we don't check too often + final DateTime lastFetched; + const ExerciseTable( + {required this.id, required this.data, required this.lastUpdate, required this.lastFetched}); @override Map toColumns(bool nullToAbsent) { final map = {}; map['id'] = Variable(id); map['data'] = Variable(data); map['last_update'] = Variable(lastUpdate); + map['last_fetched'] = Variable(lastFetched); return map; } @@ -90,6 +110,7 @@ class ExerciseTable extends DataClass implements Insertable { id: Value(id), data: Value(data), lastUpdate: Value(lastUpdate), + lastFetched: Value(lastFetched), ); } @@ -99,6 +120,7 @@ class ExerciseTable extends DataClass implements Insertable { id: serializer.fromJson(json['id']), data: serializer.fromJson(json['data']), lastUpdate: serializer.fromJson(json['lastUpdate']), + lastFetched: serializer.fromJson(json['lastFetched']), ); } @override @@ -108,74 +130,90 @@ class ExerciseTable extends DataClass implements Insertable { 'id': serializer.toJson(id), 'data': serializer.toJson(data), 'lastUpdate': serializer.toJson(lastUpdate), + 'lastFetched': serializer.toJson(lastFetched), }; } - ExerciseTable copyWith({int? id, String? data, DateTime? lastUpdate}) => ExerciseTable( + ExerciseTable copyWith({int? id, String? data, DateTime? lastUpdate, DateTime? lastFetched}) => + ExerciseTable( id: id ?? this.id, data: data ?? this.data, lastUpdate: lastUpdate ?? this.lastUpdate, + lastFetched: lastFetched ?? this.lastFetched, ); @override String toString() { return (StringBuffer('ExerciseTable(') ..write('id: $id, ') ..write('data: $data, ') - ..write('lastUpdate: $lastUpdate') + ..write('lastUpdate: $lastUpdate, ') + ..write('lastFetched: $lastFetched') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(id, data, lastUpdate); + int get hashCode => Object.hash(id, data, lastUpdate, lastFetched); @override bool operator ==(Object other) => identical(this, other) || (other is ExerciseTable && other.id == this.id && other.data == this.data && - other.lastUpdate == this.lastUpdate); + other.lastUpdate == this.lastUpdate && + other.lastFetched == this.lastFetched); } class ExercisesCompanion extends UpdateCompanion { final Value id; final Value data; final Value lastUpdate; + final Value lastFetched; final Value rowid; const ExercisesCompanion({ this.id = const Value.absent(), this.data = const Value.absent(), this.lastUpdate = const Value.absent(), + this.lastFetched = const Value.absent(), this.rowid = const Value.absent(), }); ExercisesCompanion.insert({ required int id, required String data, required DateTime lastUpdate, + required DateTime lastFetched, this.rowid = const Value.absent(), }) : id = Value(id), data = Value(data), - lastUpdate = Value(lastUpdate); + lastUpdate = Value(lastUpdate), + lastFetched = Value(lastFetched); static Insertable custom({ Expression? id, Expression? data, Expression? lastUpdate, + Expression? lastFetched, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (data != null) 'data': data, if (lastUpdate != null) 'last_update': lastUpdate, + if (lastFetched != null) 'last_fetched': lastFetched, if (rowid != null) 'rowid': rowid, }); } ExercisesCompanion copyWith( - {Value? id, Value? data, Value? lastUpdate, Value? rowid}) { + {Value? id, + Value? data, + Value? lastUpdate, + Value? lastFetched, + Value? rowid}) { return ExercisesCompanion( id: id ?? this.id, data: data ?? this.data, lastUpdate: lastUpdate ?? this.lastUpdate, + lastFetched: lastFetched ?? this.lastFetched, rowid: rowid ?? this.rowid, ); } @@ -192,6 +230,9 @@ class ExercisesCompanion extends UpdateCompanion { if (lastUpdate.present) { map['last_update'] = Variable(lastUpdate.value); } + if (lastFetched.present) { + map['last_fetched'] = Variable(lastFetched.value); + } if (rowid.present) { map['rowid'] = Variable(rowid.value); } @@ -204,6 +245,7 @@ class ExercisesCompanion extends UpdateCompanion { ..write('id: $id, ') ..write('data: $data, ') ..write('lastUpdate: $lastUpdate, ') + ..write('lastFetched: $lastFetched, ') ..write('rowid: $rowid') ..write(')')) .toString(); diff --git a/lib/helpers/misc.dart b/lib/helpers/misc.dart index 26c34cc2..b880e8c2 100644 --- a/lib/helpers/misc.dart +++ b/lib/helpers/misc.dart @@ -92,6 +92,15 @@ extension TimeOfDayExtension on TimeOfDay { } } +extension DateTimeExtension on DateTime { + bool isSameDayAs(DateTime other) { + final thisDay = DateTime(year, month, day); + final otherDay = DateTime(other.year, other.month, other.day); + + return thisDay.isAtSameMomentAs(otherDay); + } +} + void launchURL(String url, BuildContext context) async { final scaffoldMessenger = ScaffoldMessenger.of(context); final launched = await launchUrl(Uri.parse(url)); diff --git a/lib/models/exercises/exercise.dart b/lib/models/exercises/exercise.dart index e8247b09..3adecd42 100644 --- a/lib/models/exercises/exercise.dart +++ b/lib/models/exercises/exercise.dart @@ -21,7 +21,9 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/models/exercises/category.dart'; import 'package:wger/models/exercises/equipment.dart'; +import 'package:wger/models/exercises/exercise_api.dart'; import 'package:wger/models/exercises/image.dart'; +import 'package:wger/models/exercises/language.dart'; import 'package:wger/models/exercises/muscle.dart'; import 'package:wger/models/exercises/translation.dart'; import 'package:wger/models/exercises/video.dart'; @@ -31,22 +33,22 @@ part 'exercise.g.dart'; @JsonSerializable(explicitToJson: true) class Exercise extends Equatable { @JsonKey(required: true) - final int? id; + late final int? id; @JsonKey(required: true) - final String? uuid; + late final String? uuid; @JsonKey(required: true, name: 'variations') - final int? variationId; + late final int? variationId; @JsonKey(required: true, name: 'created') - final DateTime? created; + late final DateTime? created; @JsonKey(required: true, name: 'last_update') - final DateTime? lastUpdate; + late final DateTime? lastUpdate; @JsonKey(required: true, name: 'last_update_global') - final DateTime? lastUpdateGlobal; + late final DateTime? lastUpdateGlobal; @JsonKey(required: true, name: 'category') late int categoryId; @@ -81,6 +83,12 @@ class Exercise extends Equatable { @JsonKey(includeFromJson: false, includeToJson: false) List