chore(wip): consolidation intermédiaire multi-tickets (sprints Statistiques, UI, Bug resolution, Serveur-client)
Regroupe l'état de travail en cours réalisé dans un même worktree sur plusieurs tickets/sprints (#85, #136, #145, #155-160, #162-164), mélangeant des tickets QA et inProgress. Ne constitue pas une feature terminée : commit de sauvegarde avant triage/split par ticket en branches feature/* dédiées. Exclut les dossiers d'environnement de build locaux et le heap dump parasite (.gitignore mis à jour). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -28,6 +28,8 @@ part 'app_database.g.dart';
|
||||
RemoteResourceMappings,
|
||||
ShareInboxItems,
|
||||
SyncMetadataEntries,
|
||||
WorkoutTelemetryAggregates,
|
||||
WorkoutTelemetrySamples,
|
||||
WorkoutHistories,
|
||||
WorkoutHistorySetResults,
|
||||
WorkoutHistoryStepResults,
|
||||
@ -49,7 +51,7 @@ final class AppDatabase extends _$AppDatabase {
|
||||
}
|
||||
|
||||
@override
|
||||
int get schemaVersion => 22;
|
||||
int get schemaVersion => 24;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
@ -129,6 +131,12 @@ final class AppDatabase extends _$AppDatabase {
|
||||
if (from < 22) {
|
||||
await _migrateToSchema22();
|
||||
}
|
||||
if (from < 23) {
|
||||
await _migrateToSchema23();
|
||||
}
|
||||
if (from < 24) {
|
||||
await _migrateToSchema24(migrator);
|
||||
}
|
||||
await _createIndexes();
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
@ -138,6 +146,9 @@ final class AppDatabase extends _$AppDatabase {
|
||||
}
|
||||
|
||||
Future<void> _createIndexes() async {
|
||||
if (!await _hasTable(_syncableTableNames.first)) {
|
||||
return;
|
||||
}
|
||||
for (final tableName in _syncableTableNames) {
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_${tableName}_deleted_at '
|
||||
@ -250,6 +261,14 @@ final class AppDatabase extends _$AppDatabase {
|
||||
'ON workout_history (completed, started_at) '
|
||||
'WHERE deleted_at IS NULL',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_workout_telemetry_samples_session '
|
||||
'ON workout_telemetry_samples (session_id, captured_at)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_workout_telemetry_aggregates_session '
|
||||
'ON workout_telemetry_aggregates (session_id, scope)',
|
||||
);
|
||||
await customStatement(
|
||||
'CREATE INDEX IF NOT EXISTS idx_workout_history_set_results_history_id '
|
||||
'ON workout_history_set_results (workout_history_id)',
|
||||
@ -816,6 +835,127 @@ CREATE TABLE IF NOT EXISTS active_set_timer_states (
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _migrateToSchema23() async {
|
||||
await customStatement('''
|
||||
CREATE TABLE IF NOT EXISTS share_inbox_items_v23 (
|
||||
share_id TEXT NOT NULL PRIMARY KEY,
|
||||
sender_user_id TEXT NOT NULL,
|
||||
resource_type TEXT NOT NULL CHECK (
|
||||
resource_type IN ('program', 'workoutTemplate', 'pack')
|
||||
),
|
||||
payload_json TEXT NOT NULL,
|
||||
status TEXT NOT NULL CHECK (
|
||||
status IN ('pending', 'accepted', 'declined', 'revoked')
|
||||
),
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
responded_at INTEGER
|
||||
)
|
||||
''');
|
||||
await customStatement('''
|
||||
INSERT INTO share_inbox_items_v23 (
|
||||
share_id,
|
||||
sender_user_id,
|
||||
resource_type,
|
||||
payload_json,
|
||||
status,
|
||||
created_at,
|
||||
updated_at,
|
||||
responded_at
|
||||
)
|
||||
SELECT
|
||||
share_id,
|
||||
sender_user_id,
|
||||
resource_type,
|
||||
payload_json,
|
||||
status,
|
||||
created_at,
|
||||
updated_at,
|
||||
responded_at
|
||||
FROM share_inbox_items
|
||||
''');
|
||||
await customStatement('DROP TABLE share_inbox_items');
|
||||
await customStatement(
|
||||
'ALTER TABLE share_inbox_items_v23 RENAME TO share_inbox_items',
|
||||
);
|
||||
|
||||
await customStatement('''
|
||||
CREATE TABLE IF NOT EXISTS pending_share_actions_v23 (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
action_type TEXT NOT NULL CHECK (
|
||||
action_type IN ('send', 'accept', 'decline', 'revoke')
|
||||
),
|
||||
share_id TEXT,
|
||||
resource_type TEXT CHECK (
|
||||
resource_type IS NULL OR
|
||||
resource_type IN ('program', 'workoutTemplate', 'pack')
|
||||
),
|
||||
payload_json TEXT,
|
||||
recipient_emails_json TEXT,
|
||||
created_at INTEGER NOT NULL,
|
||||
last_attempt_at INTEGER,
|
||||
attempt_count INTEGER NOT NULL CHECK (attempt_count >= 0),
|
||||
status TEXT NOT NULL CHECK (status IN ('pending', 'succeeded', 'failed'))
|
||||
)
|
||||
''');
|
||||
await customStatement('''
|
||||
INSERT INTO pending_share_actions_v23 (
|
||||
id,
|
||||
action_type,
|
||||
share_id,
|
||||
resource_type,
|
||||
payload_json,
|
||||
recipient_emails_json,
|
||||
created_at,
|
||||
last_attempt_at,
|
||||
attempt_count,
|
||||
status
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
action_type,
|
||||
share_id,
|
||||
resource_type,
|
||||
payload_json,
|
||||
recipient_emails_json,
|
||||
created_at,
|
||||
last_attempt_at,
|
||||
attempt_count,
|
||||
status
|
||||
FROM pending_share_actions
|
||||
''');
|
||||
await customStatement('DROP TABLE pending_share_actions');
|
||||
await customStatement(
|
||||
'ALTER TABLE pending_share_actions_v23 RENAME TO pending_share_actions',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _migrateToSchema24(Migrator migrator) async {
|
||||
await _addColumnIfMissing(
|
||||
tableName: 'workout_history',
|
||||
columnName: 'min_heart_rate_bpm',
|
||||
definition:
|
||||
'min_heart_rate_bpm INTEGER CHECK '
|
||||
'(min_heart_rate_bpm IS NULL OR min_heart_rate_bpm > 0)',
|
||||
);
|
||||
await _addColumnIfMissing(
|
||||
tableName: 'workout_history',
|
||||
columnName: 'total_distance_meters',
|
||||
definition:
|
||||
'total_distance_meters REAL CHECK '
|
||||
'(total_distance_meters IS NULL OR total_distance_meters >= 0)',
|
||||
);
|
||||
await _addColumnIfMissing(
|
||||
tableName: 'workout_history',
|
||||
columnName: 'total_calories_kcal',
|
||||
definition:
|
||||
'total_calories_kcal REAL CHECK '
|
||||
'(total_calories_kcal IS NULL OR total_calories_kcal >= 0)',
|
||||
);
|
||||
await migrator.createTable(workoutTelemetrySamples);
|
||||
await migrator.createTable(workoutTelemetryAggregates);
|
||||
}
|
||||
|
||||
Future<void> _backfillWorkoutHistorySetSourceExerciseIds() async {
|
||||
await customStatement(r'''
|
||||
UPDATE workout_history_set_results AS result
|
||||
@ -890,4 +1030,12 @@ WHERE result.source_exercise_id_snapshot IS NULL
|
||||
final rows = await customSelect('PRAGMA table_info($tableName)').get();
|
||||
return rows.any((row) => row.data['name'] == columnName);
|
||||
}
|
||||
|
||||
Future<bool> _hasTable(String tableName) async {
|
||||
final rows = await customSelect(
|
||||
"SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?",
|
||||
variables: [Variable<String>(tableName)],
|
||||
).get();
|
||||
return rows.isNotEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user