feat(domain): fondation domain du score chronométré (ticket #25)

Étend le modèle Drift (tables.dart, app_database.dart/.g.dart), les
entités du domaine, les ports et use cases pour poser les fondations
du score chronométré. build_runner OK, flutter analyze propre, 39/39
tests verts, build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 01:10:14 +02:00
parent c88c4e876a
commit d521bdffb6
10 changed files with 3628 additions and 15 deletions

View File

@ -8,6 +8,7 @@ part 'app_database.g.dart';
@DriftDatabase(
tables: [
ActiveRestStates,
ActiveScoreStopwatchStates,
ActiveSetResults,
ActiveWorkoutSessions,
ChangeLogEntries,
@ -35,7 +36,7 @@ final class AppDatabase extends _$AppDatabase {
}
@override
int get schemaVersion => 2;
int get schemaVersion => 3;
@override
MigrationStrategy get migration {
@ -56,6 +57,10 @@ final class AppDatabase extends _$AppDatabase {
"'skipped'))",
);
}
if (from < 3) {
await _migrateToSchema3(migrator);
}
await _createIndexes();
},
beforeOpen: (details) async {
await customStatement('PRAGMA foreign_keys = ON');
@ -95,6 +100,10 @@ final class AppDatabase extends _$AppDatabase {
'CREATE INDEX IF NOT EXISTS idx_active_set_results_session_id '
'ON active_set_results (active_workout_session_id)',
);
await customStatement(
'CREATE INDEX IF NOT EXISTS idx_active_score_stopwatch_states_session_id '
'ON active_score_stopwatch_states (active_workout_session_id)',
);
await customStatement(
'CREATE INDEX IF NOT EXISTS idx_active_rest_states_session_id '
'ON active_rest_states (active_workout_session_id)',
@ -131,6 +140,7 @@ final class AppDatabase extends _$AppDatabase {
const _syncableTableNames = [
'active_rest_states',
'active_score_stopwatch_states',
'active_set_results',
'active_workout_sessions',
'exercises',
@ -143,3 +153,53 @@ const _syncableTableNames = [
'workout_template_programs',
'workout_templates',
];
extension on AppDatabase {
Future<void> _migrateToSchema3(Migrator migrator) async {
await customStatement(
'ALTER TABLE exercises ADD COLUMN score_input_mode TEXT NOT NULL '
"DEFAULT 'manual' CHECK (score_input_mode IN ('manual', 'stopwatch'))",
);
await customStatement(
'ALTER TABLE program_exercises ADD COLUMN score_input_mode_snapshot TEXT '
"NOT NULL DEFAULT 'manual' CHECK (score_input_mode_snapshot IN "
"('manual', 'stopwatch'))",
);
await customStatement(
'ALTER TABLE program_exercises ADD COLUMN target_score_time_ms INTEGER '
'CHECK (target_score_time_ms IS NULL OR target_score_time_ms > 0)',
);
await customStatement(
'ALTER TABLE workout_template_exercise_overrides ADD COLUMN '
'target_score_time_ms_override INTEGER CHECK '
'(target_score_time_ms_override IS NULL OR '
'target_score_time_ms_override > 0)',
);
await customStatement(
'ALTER TABLE active_set_results ADD COLUMN actual_score_time_ms INTEGER '
'CHECK (actual_score_time_ms IS NULL OR actual_score_time_ms >= 0)',
);
await customStatement(
'ALTER TABLE active_set_results ADD COLUMN score_input_mode_snapshot '
"TEXT NOT NULL DEFAULT 'manual' CHECK (score_input_mode_snapshot IN "
"('manual', 'stopwatch'))",
);
await customStatement(
'ALTER TABLE workout_history_set_results ADD COLUMN '
'score_input_mode_snapshot TEXT NOT NULL DEFAULT '
"'manual' CHECK (score_input_mode_snapshot IN ('manual', 'stopwatch'))",
);
await customStatement(
'ALTER TABLE workout_history_set_results ADD COLUMN '
'target_score_time_ms_snapshot INTEGER CHECK '
'(target_score_time_ms_snapshot IS NULL OR '
'target_score_time_ms_snapshot > 0)',
);
await customStatement(
'ALTER TABLE workout_history_set_results ADD COLUMN '
'actual_score_time_ms INTEGER CHECK (actual_score_time_ms IS NULL OR '
'actual_score_time_ms >= 0)',
);
await migrator.createTable(activeScoreStopwatchStates);
}
}