feat(watch): clôture lot #91 - fréquence cardiaque live, notifications de séance et finitions montre

Consolide le lot applicatif watch companion validé :
- télémétrie fréquence cardiaque live remontée montre -> téléphone
  (collecteur watch, adapter Wear Data Layer, persistance Drift,
  propagation aux écrans historique/programme/profil/exécution)
- notifications de séance en arrière-plan côté téléphone (service
  foreground de statut + passerelle applicative)
- finitions montre : chrono d'étape, score d'étape, retrait du bouton
  "lancer une séance", thème, icônes et polices watch_app

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 05:56:12 +02:00
parent c65a5a76a9
commit 65d43b9768
80 changed files with 12292 additions and 892 deletions

View File

@ -137,7 +137,7 @@ class PendingShareActions extends Table {
@override
List<String> get customConstraints => [
"CHECK (action_type IN ('send', 'accept', 'decline', 'revoke'))",
"CHECK (resource_type IS NULL OR resource_type IN "
'CHECK (resource_type IS NULL OR resource_type IN '
"('program', 'workoutTemplate'))",
"CHECK (status IN ('pending', 'succeeded', 'failed'))",
];
@ -250,6 +250,8 @@ class ExerciseSteps extends SyncableTable {
TextColumn get scoreUnit => text().nullable()();
RealColumn get defaultTargetScore => real().nullable()();
IntColumn get defaultTargetScoreTimeMs => integer().nullable()();
BoolColumn get linkedToSeriesScore =>
boolean().withDefault(const Constant(false))();
@override
List<String> get customConstraints => [
@ -276,6 +278,8 @@ class ExerciseSteps extends SyncableTable {
'AND default_target_score IS NULL))',
'CHECK (default_target_score IS NULL OR '
'default_target_score_time_ms IS NULL)',
'CHECK (NOT linked_to_series_score OR '
"(has_score AND score_input_mode = 'manual'))",
];
}
@ -548,6 +552,29 @@ class ActiveScoreStopwatchStates extends SyncableTable {
];
}
class ActiveManualScoreStates extends SyncableTable {
@override
String get tableName => 'active_manual_score_states';
TextColumn get activeWorkoutSessionId =>
text().references(ActiveWorkoutSessions, #id)();
IntColumn get programIndex => integer()();
IntColumn get exerciseIndex => integer()();
IntColumn get setIndex => integer()();
RealColumn get value => real()();
DateTimeColumn get scoreUpdatedAt => dateTime()();
@override
List<String> get customConstraints => [
'UNIQUE (active_workout_session_id, program_index, exercise_index, '
'set_index)',
'CHECK (program_index >= 0)',
'CHECK (exercise_index >= 0)',
'CHECK (set_index >= 0)',
'CHECK (value >= 0)',
];
}
class ActiveSetTimerStates extends SyncableTable {
@override
String get tableName => 'active_set_timer_states';
@ -723,9 +750,15 @@ class WorkoutHistories extends SyncableTable {
IntColumn get totalActiveMs => integer()();
BoolColumn get completed => boolean()();
TextColumn get historySnapshotJson => text().withLength(min: 1)();
RealColumn get averageHeartRateBpm => real().nullable()();
IntColumn get maxHeartRateBpm => integer().nullable()();
@override
List<String> get customConstraints => ['CHECK (total_active_ms >= 0)'];
List<String> get customConstraints => [
'CHECK (total_active_ms >= 0)',
'CHECK (average_heart_rate_bpm IS NULL OR average_heart_rate_bpm > 0)',
'CHECK (max_heart_rate_bpm IS NULL OR max_heart_rate_bpm > 0)',
];
}
class WorkoutHistorySetResults extends SyncableTable {