mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Test how feasible it is to load exercises via powersync as well
This commit is contained in:
@@ -2,15 +2,40 @@ import 'package:drift/drift.dart';
|
||||
import 'package:drift_sqlite_async/drift_sqlite_async.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:powersync/powersync.dart' show uuid;
|
||||
import 'package:wger/database/weight/database.dart';
|
||||
import 'package:wger/models/body_weight/weight_entry.dart';
|
||||
import 'package:wger/models/exercises/category.dart';
|
||||
import 'package:wger/models/exercises/equipment.dart';
|
||||
import 'package:wger/models/exercises/exercise.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';
|
||||
|
||||
import 'powersync.dart';
|
||||
import 'tables/exercise.dart';
|
||||
import 'tables/language.dart';
|
||||
import 'tables/weight.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
@DriftDatabase(
|
||||
tables: [
|
||||
// Core
|
||||
LanguageTable,
|
||||
|
||||
// Exercises
|
||||
ExerciseTable,
|
||||
ExerciseTranslationTable,
|
||||
MuscleTable,
|
||||
ExerciseMuscleM2N,
|
||||
ExerciseSecondaryMuscleM2N,
|
||||
EquipmentTable,
|
||||
ExerciseCategoryTable,
|
||||
ExerciseImageTable,
|
||||
ExerciseVideoTable,
|
||||
|
||||
// User data
|
||||
WeightEntryTable,
|
||||
],
|
||||
//include: {'queries.drift'},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
114
lib/database/powersync/tables/exercise.dart
Normal file
114
lib/database/powersync/tables/exercise.dart
Normal file
@@ -0,0 +1,114 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:wger/models/exercises/category.dart';
|
||||
import 'package:wger/models/exercises/equipment.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
import 'package:wger/models/exercises/image.dart';
|
||||
import 'package:wger/models/exercises/muscle.dart';
|
||||
import 'package:wger/models/exercises/translation.dart';
|
||||
import 'package:wger/models/exercises/video.dart';
|
||||
|
||||
@UseRowClass(Exercise)
|
||||
class ExerciseTable extends Table {
|
||||
@override
|
||||
String get tableName => 'exercises_exercise';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
TextColumn get uuid => text()();
|
||||
IntColumn get variationId => integer().nullable().named('variation_id')();
|
||||
IntColumn get categoryId => integer().named('category_id')();
|
||||
DateTimeColumn get created => dateTime()();
|
||||
DateTimeColumn get lastUpdate => dateTime().named('last_update')();
|
||||
}
|
||||
|
||||
@UseRowClass(Translation)
|
||||
class ExerciseTranslationTable extends Table {
|
||||
@override
|
||||
String get tableName => 'exercises_translation';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
TextColumn get uuid => text()();
|
||||
IntColumn get exerciseId => integer().named('exercise_id')();
|
||||
IntColumn get languageId => integer().named('language_id')();
|
||||
TextColumn get name => text()();
|
||||
TextColumn get description => text()();
|
||||
DateTimeColumn get created => dateTime()();
|
||||
DateTimeColumn get lastUpdate => dateTime().named('last_update')();
|
||||
}
|
||||
|
||||
@UseRowClass(ExerciseCategory)
|
||||
class ExerciseCategoryTable extends Table {
|
||||
@override
|
||||
String get tableName => 'exercises_exercisecategory';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
TextColumn get name => text()();
|
||||
}
|
||||
|
||||
@UseRowClass(Equipment)
|
||||
class EquipmentTable extends Table {
|
||||
@override
|
||||
String get tableName => 'exercises_equipment';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
TextColumn get name => text()();
|
||||
}
|
||||
|
||||
@UseRowClass(Muscle)
|
||||
class MuscleTable extends Table {
|
||||
@override
|
||||
String get tableName => 'exercises_muscle';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
TextColumn get name => text()();
|
||||
TextColumn get nameEn => text().named('name_en')();
|
||||
BoolColumn get isFront => boolean().named('is_front')();
|
||||
}
|
||||
|
||||
class ExerciseMuscleM2N extends Table {
|
||||
@override
|
||||
String get tableName => 'exercises_exercise_muscles';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
IntColumn get exerciseId => integer().named('exercise_id')();
|
||||
IntColumn get muscleId => integer().named('muscle_id')();
|
||||
}
|
||||
|
||||
class ExerciseSecondaryMuscleM2N extends Table {
|
||||
@override
|
||||
String get tableName => 'exercises_exercise_muscles_secondary';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
IntColumn get exerciseId => integer().named('exercise_id')();
|
||||
IntColumn get muscleId => integer().named('muscle_id')();
|
||||
}
|
||||
|
||||
@UseRowClass(ExerciseImage)
|
||||
class ExerciseImageTable extends Table {
|
||||
@override
|
||||
String get tableName => 'exercises_exerciseimage';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
TextColumn get uuid => text()();
|
||||
IntColumn get exerciseId => integer().named('exercise_id')();
|
||||
TextColumn get url => text()();
|
||||
BoolColumn get isMain => boolean().named('is_main')();
|
||||
}
|
||||
|
||||
@UseRowClass(Video)
|
||||
class ExerciseVideoTable extends Table {
|
||||
@override
|
||||
String get tableName => 'exercises_exercisevideo';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
TextColumn get uuid => text()();
|
||||
IntColumn get exerciseId => integer().named('exercise_id')();
|
||||
TextColumn get url => text()();
|
||||
IntColumn get size => integer()();
|
||||
IntColumn get duration => integer()();
|
||||
IntColumn get width => integer()();
|
||||
IntColumn get height => integer()();
|
||||
TextColumn get codec => text()();
|
||||
TextColumn get codecLong => text().named('codec_long')();
|
||||
IntColumn get licenseId => integer().named('license_id')();
|
||||
TextColumn get licenseAuthor => text().named('license_author')();
|
||||
}
|
||||
12
lib/database/powersync/tables/language.dart
Normal file
12
lib/database/powersync/tables/language.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:wger/models/exercises/language.dart';
|
||||
|
||||
@UseRowClass(Language)
|
||||
class LanguageTable extends Table {
|
||||
@override
|
||||
String get tableName => 'core_language';
|
||||
|
||||
IntColumn get id => integer()();
|
||||
TextColumn get shortName => text().named('short_name')();
|
||||
TextColumn get fullName => text().named('full_name')();
|
||||
}
|
||||
@@ -38,10 +38,10 @@ class Exercise extends Equatable {
|
||||
final _logger = Logger('ExerciseModel');
|
||||
|
||||
@JsonKey(required: true)
|
||||
late final int? id;
|
||||
late final int id;
|
||||
|
||||
@JsonKey(required: true)
|
||||
late final String? uuid;
|
||||
late final String uuid;
|
||||
|
||||
@JsonKey(required: true, name: 'variations')
|
||||
late final int? variationId;
|
||||
@@ -95,12 +95,13 @@ class Exercise extends Equatable {
|
||||
List<String> authorsGlobal = [];
|
||||
|
||||
Exercise({
|
||||
this.id,
|
||||
this.uuid,
|
||||
required this.id,
|
||||
required this.uuid,
|
||||
this.created,
|
||||
this.lastUpdate,
|
||||
this.lastUpdateGlobal,
|
||||
// this.lastUpdateGlobal,
|
||||
this.variationId,
|
||||
required this.categoryId,
|
||||
List<Muscle>? muscles,
|
||||
List<Muscle>? musclesSecondary,
|
||||
List<Equipment>? equipment,
|
||||
@@ -142,6 +143,8 @@ class Exercise extends Equatable {
|
||||
}
|
||||
this.authors = authors ?? [];
|
||||
this.authorsGlobal = authorsGlobal ?? [];
|
||||
|
||||
lastUpdateGlobal = DateTime.now();
|
||||
}
|
||||
|
||||
bool get showPlateCalculator => equipment.map((e) => e.id).contains(ID_EQUIPMENT_BARBELL);
|
||||
@@ -160,7 +163,7 @@ class Exercise extends Equatable {
|
||||
|
||||
created = exerciseData.created;
|
||||
lastUpdate = exerciseData.lastUpdate;
|
||||
lastUpdateGlobal = exerciseData.lastUpdateGlobal;
|
||||
// lastUpdateGlobal = exerciseData.lastUpdateGlobal;
|
||||
|
||||
musclesSecondary = exerciseData.muscles;
|
||||
muscles = exerciseData.muscles;
|
||||
|
||||
@@ -23,16 +23,14 @@ Exercise _$ExerciseFromJson(Map<String, dynamic> json) {
|
||||
],
|
||||
);
|
||||
return Exercise(
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
uuid: json['uuid'] as String?,
|
||||
id: (json['id'] as num).toInt(),
|
||||
uuid: json['uuid'] as String,
|
||||
created: json['created'] == null ? null : DateTime.parse(json['created'] as String),
|
||||
lastUpdate: json['last_update'] == null
|
||||
? null
|
||||
: DateTime.parse(json['last_update'] as String),
|
||||
lastUpdateGlobal: json['last_update_global'] == null
|
||||
? null
|
||||
: DateTime.parse(json['last_update_global'] as String),
|
||||
variationId: (json['variations'] as num?)?.toInt(),
|
||||
categoryId: (json['category'] as num).toInt(),
|
||||
translations: (json['translations'] as List<dynamic>?)
|
||||
?.map((e) => Translation.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
@@ -42,7 +40,9 @@ Exercise _$ExerciseFromJson(Map<String, dynamic> json) {
|
||||
json['categories'] as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
..categoryId = (json['category'] as num).toInt()
|
||||
..lastUpdateGlobal = json['last_update_global'] == null
|
||||
? null
|
||||
: DateTime.parse(json['last_update_global'] as String)
|
||||
..musclesIds = (json['muscles'] as List<dynamic>).map((e) => (e as num).toInt()).toList()
|
||||
..musclesSecondaryIds = (json['muscles_secondary'] as List<dynamic>)
|
||||
.map((e) => (e as num).toInt())
|
||||
|
||||
@@ -55,8 +55,8 @@ class Video {
|
||||
@JsonKey(name: 'codec_long', required: true)
|
||||
final String codecLong;
|
||||
|
||||
@JsonKey(required: true)
|
||||
final int license;
|
||||
@JsonKey(required: true, name: 'license')
|
||||
final int licenseId;
|
||||
|
||||
@JsonKey(name: 'license_author', required: true)
|
||||
final String? licenseAuthor;
|
||||
@@ -72,7 +72,7 @@ class Video {
|
||||
required this.height,
|
||||
required this.codec,
|
||||
required this.codecLong,
|
||||
required this.license,
|
||||
required this.licenseId,
|
||||
required this.licenseAuthor,
|
||||
});
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ Video _$VideoFromJson(Map<String, dynamic> json) {
|
||||
height: (json['height'] as num).toInt(),
|
||||
codec: json['codec'] as String,
|
||||
codecLong: json['codec_long'] as String,
|
||||
license: (json['license'] as num).toInt(),
|
||||
licenseId: (json['license'] as num).toInt(),
|
||||
licenseAuthor: json['license_author'] as String?,
|
||||
);
|
||||
}
|
||||
@@ -51,6 +51,6 @@ Map<String, dynamic> _$VideoToJson(Video instance) => <String, dynamic>{
|
||||
'height': instance.height,
|
||||
'codec': instance.codec,
|
||||
'codec_long': instance.codecLong,
|
||||
'license': instance.license,
|
||||
'license': instance.licenseId,
|
||||
'license_author': instance.licenseAuthor,
|
||||
};
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// This file performs setup of the PowerSync database
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:powersync/powersync.dart';
|
||||
import 'package:wger/powersync/connector.dart';
|
||||
import 'package:wger/powersync/schema.dart';
|
||||
|
||||
final logger = Logger('powersync-django');
|
||||
|
||||
/// Global reference to the database
|
||||
late final PowerSyncDatabase db;
|
||||
|
||||
// Hacky flag to ensure the database is only initialized once, better to do this with listeners
|
||||
bool _dbInitialized = false;
|
||||
|
||||
Future<String> getDatabasePath() async {
|
||||
final dir = await getApplicationSupportDirectory();
|
||||
return join(dir.path, 'powersync-demo.db');
|
||||
}
|
||||
|
||||
// opens the database and connects if logged in
|
||||
Future<void> openDatabase(bool connect, String baseUrl, String powersyncUrl) async {
|
||||
// Open the local database
|
||||
if (!_dbInitialized) {
|
||||
db = PowerSyncDatabase(schema: schema, path: await getDatabasePath(), logger: attachedLogger);
|
||||
await db.initialize();
|
||||
_dbInitialized = true;
|
||||
}
|
||||
|
||||
if (connect) {
|
||||
// If the user is already logged in, connect immediately.
|
||||
// Otherwise, connect once logged in.
|
||||
|
||||
final currentConnector = DjangoConnector(baseUrl: baseUrl, powersyncUrl: powersyncUrl);
|
||||
db.connect(connector: currentConnector);
|
||||
|
||||
// TODO: should we respond to login state changing? like here:
|
||||
// https://www.powersync.com/blog/flutter-tutorial-building-an-offline-first-chat-app-with-supabase-and-powersync#implement-auth-methods
|
||||
}
|
||||
}
|
||||
|
||||
/// Explicit sign out - clear database and log out.
|
||||
Future<void> logout() async {
|
||||
await db.disconnectAndClear();
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class ApiClient {
|
||||
final prefs = PreferenceHelper.asyncPref;
|
||||
|
||||
final apiData = json.decode((await prefs.getString(PREFS_USER))!);
|
||||
_logger.info('posting our token "${apiData["token"]}" to $baseUrl/api/v2/powersync-token');
|
||||
// _logger.info('posting our token "${apiData["token"]}" to $baseUrl/api/v2/powersync-token');
|
||||
token = apiData['token'];
|
||||
final response = await http.get(
|
||||
Uri.parse('$baseUrl/api/v2/powersync-token'),
|
||||
@@ -41,9 +41,9 @@ class ApiClient {
|
||||
HttpHeaders.authorizationHeader: 'Token ${apiData["token"]}',
|
||||
},
|
||||
);
|
||||
_logger.info('response: status ${response.statusCode}, body ${response.body}');
|
||||
// _logger.info('response: status ${response.statusCode}, body ${response.body}');
|
||||
if (response.statusCode == 200) {
|
||||
_logger.log(Level.ALL, response.body);
|
||||
// _logger.fine(response.body);
|
||||
final result = json.decode(response.body);
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1,11 +1,52 @@
|
||||
import 'package:powersync/powersync.dart';
|
||||
|
||||
const tableMuscle = 'exercises_muscle';
|
||||
const tableBodyWeight = 'weight_weightentry';
|
||||
|
||||
Schema schema = const Schema([
|
||||
//
|
||||
// Core
|
||||
//
|
||||
Table(
|
||||
tableMuscle,
|
||||
'core_language',
|
||||
[
|
||||
Column.text('short_name'),
|
||||
Column.text('full_name'),
|
||||
],
|
||||
),
|
||||
|
||||
//
|
||||
// Exercises
|
||||
//
|
||||
Table(
|
||||
'exercises_exercise',
|
||||
[
|
||||
Column.text('uuid'),
|
||||
Column.integer('category_id'),
|
||||
Column.integer('variation_id'),
|
||||
Column.text('created'),
|
||||
Column.text('last_update'),
|
||||
],
|
||||
indexes: [
|
||||
Index('category', [IndexedColumn('category_id')]),
|
||||
Index('variation', [IndexedColumn('variation_id')]),
|
||||
],
|
||||
),
|
||||
Table(
|
||||
'exercises_translation',
|
||||
[
|
||||
Column.text('uuid'),
|
||||
Column.integer('language_id'),
|
||||
Column.integer('exercise_id'),
|
||||
Column.text('description'),
|
||||
Column.text('name'),
|
||||
Column.text('created'),
|
||||
Column.text('last_update'),
|
||||
],
|
||||
indexes: [
|
||||
Index('language', [IndexedColumn('language_id')]),
|
||||
Index('exercise', [IndexedColumn('exercise_id')]),
|
||||
],
|
||||
),
|
||||
Table(
|
||||
'exercises_muscle',
|
||||
[
|
||||
Column.text('name'),
|
||||
Column.text('name_en'),
|
||||
@@ -13,7 +54,70 @@ Schema schema = const Schema([
|
||||
],
|
||||
),
|
||||
Table(
|
||||
tableBodyWeight,
|
||||
'exercises_exercise_muscles',
|
||||
[
|
||||
Column.integer('exercise_id'),
|
||||
Column.integer('muscle_id'),
|
||||
],
|
||||
indexes: [
|
||||
Index('muscle', [IndexedColumn('muscle_id')]),
|
||||
Index('exercise', [IndexedColumn('exercise_id')]),
|
||||
],
|
||||
),
|
||||
Table(
|
||||
'exercises_exercise_muscles_secondary',
|
||||
[
|
||||
Column.integer('exercise_id'),
|
||||
Column.integer('muscle_id'),
|
||||
],
|
||||
indexes: [
|
||||
Index('muscle', [IndexedColumn('muscle_id')]),
|
||||
Index('exercise', [IndexedColumn('exercise_id')]),
|
||||
],
|
||||
),
|
||||
Table(
|
||||
'exercises_equipment',
|
||||
[
|
||||
Column.text('name'),
|
||||
],
|
||||
),
|
||||
Table(
|
||||
'exercises_exercisecategory',
|
||||
[
|
||||
Column.text('name'),
|
||||
],
|
||||
),
|
||||
Table(
|
||||
'exercises_exerciseimage',
|
||||
[
|
||||
Column.text('uuid'),
|
||||
Column.integer('exercise_id'),
|
||||
Column.text('url'),
|
||||
Column.integer('is_main'),
|
||||
],
|
||||
),
|
||||
Table(
|
||||
'exercises_exercisevideo',
|
||||
[
|
||||
Column.text('uuid'),
|
||||
Column.integer('exercise_id'),
|
||||
Column.text('url'),
|
||||
Column.integer('size'),
|
||||
Column.integer('duration'),
|
||||
Column.integer('width'),
|
||||
Column.integer('height'),
|
||||
Column.text('codec'),
|
||||
Column.text('codec_long'),
|
||||
Column.integer('license_id'),
|
||||
Column.text('license_author'),
|
||||
],
|
||||
),
|
||||
|
||||
//
|
||||
// User data
|
||||
//
|
||||
Table(
|
||||
'weight_weightentry',
|
||||
[
|
||||
Column.text('uuid'),
|
||||
Column.real('weight'),
|
||||
|
||||
14
lib/providers/core_data.dart
Normal file
14
lib/providers/core_data.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:wger/database/powersync/database.dart';
|
||||
import 'package:wger/models/exercises/language.dart';
|
||||
|
||||
part 'core_data.g.dart';
|
||||
|
||||
@riverpod
|
||||
final class LanguageNotifier extends _$LanguageNotifier {
|
||||
@override
|
||||
Stream<List<Language>> build() {
|
||||
final db = ref.read(driftPowerSyncDatabase);
|
||||
return db.select(db.languageTable).watch();
|
||||
}
|
||||
}
|
||||
55
lib/providers/core_data.g.dart
Normal file
55
lib/providers/core_data.g.dart
Normal file
@@ -0,0 +1,55 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'core_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(LanguageNotifier)
|
||||
const languageProvider = LanguageNotifierProvider._();
|
||||
|
||||
final class LanguageNotifierProvider
|
||||
extends $StreamNotifierProvider<LanguageNotifier, List<Language>> {
|
||||
const LanguageNotifierProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'languageProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$languageNotifierHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
LanguageNotifier create() => LanguageNotifier();
|
||||
}
|
||||
|
||||
String _$languageNotifierHash() => r'7945fdd6a4a80c381615b153209e4b70d9c81332';
|
||||
|
||||
abstract class _$LanguageNotifier extends $StreamNotifier<List<Language>> {
|
||||
Stream<List<Language>> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<AsyncValue<List<Language>>, List<Language>>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<List<Language>>, List<Language>>,
|
||||
AsyncValue<List<Language>>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
116
lib/providers/exercise_data.dart
Normal file
116
lib/providers/exercise_data.dart
Normal file
@@ -0,0 +1,116 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:wger/database/powersync/database.dart';
|
||||
import 'package:wger/models/exercises/equipment.dart';
|
||||
import 'package:wger/models/exercises/exercise.dart';
|
||||
|
||||
part 'exercise_data.g.dart';
|
||||
|
||||
@riverpod
|
||||
final class ExerciseNotifier extends _$ExerciseNotifier {
|
||||
final _logger = Logger('ExerciseNotifier');
|
||||
|
||||
@override
|
||||
Stream<List<Exercise>> build() {
|
||||
final db = ref.read(driftPowerSyncDatabase);
|
||||
|
||||
final primaryMuscleTable = db.alias(db.muscleTable, 'pm');
|
||||
final secondaryMuscleTable = db.alias(db.muscleTable, 'sm');
|
||||
|
||||
final joined = db.select(db.exerciseTable).join([
|
||||
// Translations
|
||||
leftOuterJoin(
|
||||
db.exerciseTranslationTable,
|
||||
db.exerciseTranslationTable.exerciseId.equalsExp(db.exerciseTable.id),
|
||||
),
|
||||
|
||||
// Exercise <-> Muscle
|
||||
leftOuterJoin(
|
||||
db.exerciseMuscleM2N,
|
||||
db.exerciseMuscleM2N.exerciseId.equalsExp(db.exerciseTable.id),
|
||||
),
|
||||
leftOuterJoin(
|
||||
primaryMuscleTable,
|
||||
primaryMuscleTable.id.equalsExp(db.exerciseMuscleM2N.muscleId),
|
||||
),
|
||||
|
||||
// Exercise <-> Secondary Muscle
|
||||
leftOuterJoin(
|
||||
db.exerciseSecondaryMuscleM2N,
|
||||
db.exerciseSecondaryMuscleM2N.exerciseId.equalsExp(db.exerciseTable.id),
|
||||
),
|
||||
leftOuterJoin(
|
||||
secondaryMuscleTable,
|
||||
secondaryMuscleTable.id.equalsExp(db.exerciseSecondaryMuscleM2N.muscleId),
|
||||
),
|
||||
|
||||
// Category
|
||||
leftOuterJoin(
|
||||
db.exerciseCategoryTable,
|
||||
db.exerciseCategoryTable.id.equalsExp(db.exerciseTable.categoryId),
|
||||
),
|
||||
|
||||
// Images
|
||||
leftOuterJoin(
|
||||
db.exerciseImageTable,
|
||||
db.exerciseImageTable.exerciseId.equalsExp(db.exerciseTable.id),
|
||||
),
|
||||
]);
|
||||
|
||||
return joined.watch().map((rows) {
|
||||
final Map<int, Exercise> map = {};
|
||||
|
||||
for (final row in rows) {
|
||||
final exercise = row.readTable(db.exerciseTable);
|
||||
final primaryMuscle = row.readTableOrNull(primaryMuscleTable);
|
||||
final secondaryMuscle = row.readTableOrNull(secondaryMuscleTable);
|
||||
final image = row.readTableOrNull(db.exerciseImageTable);
|
||||
final video = row.readTableOrNull(db.exerciseVideoTable);
|
||||
final translation = row.readTableOrNull(db.exerciseTranslationTable);
|
||||
final category = row.readTableOrNull(db.exerciseCategoryTable);
|
||||
|
||||
final entry = map.putIfAbsent(
|
||||
exercise.id,
|
||||
() => exercise,
|
||||
);
|
||||
|
||||
if (category != null) {
|
||||
entry.category = category;
|
||||
}
|
||||
|
||||
if (translation != null && !entry.translations.any((t) => t.id == translation.id)) {
|
||||
entry.translations.add(translation);
|
||||
}
|
||||
|
||||
if (image != null && !entry.images.any((t) => t.id == image.id)) {
|
||||
entry.images.add(image);
|
||||
}
|
||||
|
||||
if (video != null && !entry.videos.any((t) => t.id == video.id)) {
|
||||
entry.videos.add(video);
|
||||
}
|
||||
|
||||
if (primaryMuscle != null && !entry.muscles.any((m) => m.id == primaryMuscle.id)) {
|
||||
entry.muscles.add(primaryMuscle);
|
||||
}
|
||||
|
||||
if (secondaryMuscle != null &&
|
||||
!entry.musclesSecondary.any((m) => m.id == secondaryMuscle.id)) {
|
||||
entry.musclesSecondary.add(secondaryMuscle);
|
||||
}
|
||||
}
|
||||
|
||||
return map.values.toList();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
final class ExerciseEquipmentNotifier extends _$ExerciseEquipmentNotifier {
|
||||
@override
|
||||
Stream<List<Equipment>> build() {
|
||||
final db = ref.read(driftPowerSyncDatabase);
|
||||
return db.select(db.equipmentTable).watch();
|
||||
}
|
||||
}
|
||||
100
lib/providers/exercise_data.g.dart
Normal file
100
lib/providers/exercise_data.g.dart
Normal file
@@ -0,0 +1,100 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'exercise_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(ExerciseNotifier)
|
||||
const exerciseProvider = ExerciseNotifierProvider._();
|
||||
|
||||
final class ExerciseNotifierProvider
|
||||
extends $StreamNotifierProvider<ExerciseNotifier, List<Exercise>> {
|
||||
const ExerciseNotifierProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'exerciseProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$exerciseNotifierHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ExerciseNotifier create() => ExerciseNotifier();
|
||||
}
|
||||
|
||||
String _$exerciseNotifierHash() => r'4f6613f81e292dd3c74f9eebbbb20482b8da1e42';
|
||||
|
||||
abstract class _$ExerciseNotifier extends $StreamNotifier<List<Exercise>> {
|
||||
Stream<List<Exercise>> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<AsyncValue<List<Exercise>>, List<Exercise>>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<List<Exercise>>, List<Exercise>>,
|
||||
AsyncValue<List<Exercise>>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(ExerciseEquipmentNotifier)
|
||||
const exerciseEquipmentProvider = ExerciseEquipmentNotifierProvider._();
|
||||
|
||||
final class ExerciseEquipmentNotifierProvider
|
||||
extends $StreamNotifierProvider<ExerciseEquipmentNotifier, List<Equipment>> {
|
||||
const ExerciseEquipmentNotifierProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'exerciseEquipmentProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$exerciseEquipmentNotifierHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
ExerciseEquipmentNotifier create() => ExerciseEquipmentNotifier();
|
||||
}
|
||||
|
||||
String _$exerciseEquipmentNotifierHash() => r'581494c6fd4f58e210ea1962fa48bda613c5827f';
|
||||
|
||||
abstract class _$ExerciseEquipmentNotifier extends $StreamNotifier<List<Equipment>> {
|
||||
Stream<List<Equipment>> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build();
|
||||
final ref = this.ref as $Ref<AsyncValue<List<Equipment>>, List<Equipment>>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<List<Equipment>>, List<Equipment>>,
|
||||
AsyncValue<List<Equipment>>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user