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:
@ -29,7 +29,7 @@ enum ActiveSetTimerStatus { running, paused, stopped, skipped }
|
||||
|
||||
enum ActiveScoreStopwatchStatus { running, paused, stopped }
|
||||
|
||||
enum ShareResourceType { program, workoutTemplate }
|
||||
enum ShareResourceType { program, workoutTemplate, pack }
|
||||
|
||||
enum ShareInboxStatus { pending, accepted, declined, revoked }
|
||||
|
||||
@ -1476,16 +1476,25 @@ final class WorkoutHistory {
|
||||
required this.totalActiveMs,
|
||||
required this.completed,
|
||||
required this.historySnapshotJson,
|
||||
this.minHeartRateBpm,
|
||||
this.averageHeartRateBpm,
|
||||
this.maxHeartRateBpm,
|
||||
this.totalDistanceMeters,
|
||||
this.totalCaloriesKcal,
|
||||
this.results = const [],
|
||||
this.stepResults = const [],
|
||||
}) {
|
||||
_requireNullablePositive(minHeartRateBpm, 'Min heart rate bpm');
|
||||
_requireNullablePositiveDouble(
|
||||
averageHeartRateBpm,
|
||||
'Average heart rate bpm',
|
||||
);
|
||||
_requireNullablePositive(maxHeartRateBpm, 'Max heart rate bpm');
|
||||
_requireNullableNonNegativeDouble(
|
||||
totalDistanceMeters,
|
||||
'Total distance meters',
|
||||
);
|
||||
_requireNullableNonNegativeDouble(totalCaloriesKcal, 'Total calories kcal');
|
||||
}
|
||||
|
||||
final EntityMetadata metadata;
|
||||
@ -1497,15 +1506,21 @@ final class WorkoutHistory {
|
||||
final int totalActiveMs;
|
||||
final bool completed;
|
||||
final String historySnapshotJson;
|
||||
final int? minHeartRateBpm;
|
||||
final double? averageHeartRateBpm;
|
||||
final int? maxHeartRateBpm;
|
||||
final double? totalDistanceMeters;
|
||||
final double? totalCaloriesKcal;
|
||||
final List<WorkoutHistorySetResult> results;
|
||||
final List<WorkoutHistoryStepResult> stepResults;
|
||||
|
||||
WorkoutHistory copyWith({
|
||||
EntityMetadata? metadata,
|
||||
Object? minHeartRateBpm = _unchanged,
|
||||
Object? averageHeartRateBpm = _unchanged,
|
||||
Object? maxHeartRateBpm = _unchanged,
|
||||
Object? totalDistanceMeters = _unchanged,
|
||||
Object? totalCaloriesKcal = _unchanged,
|
||||
}) {
|
||||
return WorkoutHistory(
|
||||
metadata: metadata ?? this.metadata,
|
||||
@ -1517,18 +1532,149 @@ final class WorkoutHistory {
|
||||
totalActiveMs: totalActiveMs,
|
||||
completed: completed,
|
||||
historySnapshotJson: historySnapshotJson,
|
||||
minHeartRateBpm: minHeartRateBpm == _unchanged
|
||||
? this.minHeartRateBpm
|
||||
: minHeartRateBpm as int?,
|
||||
averageHeartRateBpm: averageHeartRateBpm == _unchanged
|
||||
? this.averageHeartRateBpm
|
||||
: averageHeartRateBpm as double?,
|
||||
maxHeartRateBpm: maxHeartRateBpm == _unchanged
|
||||
? this.maxHeartRateBpm
|
||||
: maxHeartRateBpm as int?,
|
||||
totalDistanceMeters: totalDistanceMeters == _unchanged
|
||||
? this.totalDistanceMeters
|
||||
: totalDistanceMeters as double?,
|
||||
totalCaloriesKcal: totalCaloriesKcal == _unchanged
|
||||
? this.totalCaloriesKcal
|
||||
: totalCaloriesKcal as double?,
|
||||
results: results,
|
||||
stepResults: stepResults,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum WorkoutTelemetryAggregateScope {
|
||||
session('session'),
|
||||
exercise('exercise'),
|
||||
set('set'),
|
||||
step('step');
|
||||
|
||||
const WorkoutTelemetryAggregateScope(this.wireName);
|
||||
|
||||
final String wireName;
|
||||
|
||||
static WorkoutTelemetryAggregateScope parse(String value) {
|
||||
for (final scope in values) {
|
||||
if (scope.wireName == value) {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
throw DomainException('Unsupported telemetry scope: $value.');
|
||||
}
|
||||
}
|
||||
|
||||
final class WorkoutTelemetrySample {
|
||||
WorkoutTelemetrySample({
|
||||
required String id,
|
||||
required String sessionId,
|
||||
required DateTime capturedAt,
|
||||
this.programIndex,
|
||||
this.exerciseIndex,
|
||||
this.setIndex,
|
||||
this.passageIndex,
|
||||
this.stepIndex,
|
||||
this.programSnapshotId,
|
||||
this.exerciseSnapshotId,
|
||||
this.stepSnapshotId,
|
||||
this.heartRateBpm,
|
||||
this.distanceMeters,
|
||||
this.caloriesKcal,
|
||||
}) : id = _nonBlank(id, 'Telemetry sample id'),
|
||||
sessionId = _nonBlank(sessionId, 'Telemetry session id'),
|
||||
capturedAt = capturedAt.toUtc() {
|
||||
_requireNullableNonNegative(programIndex, 'Program index');
|
||||
_requireNullableNonNegative(exerciseIndex, 'Exercise index');
|
||||
_requireNullableNonNegative(setIndex, 'Set index');
|
||||
_requireNullableNonNegative(passageIndex, 'Passage index');
|
||||
_requireNullableNonNegative(stepIndex, 'Step index');
|
||||
_requireNullablePositive(heartRateBpm, 'Heart rate bpm');
|
||||
_requireNullableNonNegativeDouble(distanceMeters, 'Distance meters');
|
||||
_requireNullableNonNegativeDouble(caloriesKcal, 'Calories kcal');
|
||||
if (heartRateBpm == null &&
|
||||
distanceMeters == null &&
|
||||
caloriesKcal == null) {
|
||||
throw const DomainException(
|
||||
'Telemetry sample must contain at least one metric.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final String id;
|
||||
final String sessionId;
|
||||
final DateTime capturedAt;
|
||||
final int? programIndex;
|
||||
final int? exerciseIndex;
|
||||
final int? setIndex;
|
||||
final int? passageIndex;
|
||||
final int? stepIndex;
|
||||
final String? programSnapshotId;
|
||||
final String? exerciseSnapshotId;
|
||||
final String? stepSnapshotId;
|
||||
final int? heartRateBpm;
|
||||
final double? distanceMeters;
|
||||
final double? caloriesKcal;
|
||||
}
|
||||
|
||||
final class WorkoutTelemetryAggregate {
|
||||
WorkoutTelemetryAggregate({
|
||||
required String sessionId,
|
||||
required this.scope,
|
||||
this.programIndex,
|
||||
this.exerciseIndex,
|
||||
this.setIndex,
|
||||
this.passageIndex,
|
||||
this.stepIndex,
|
||||
required this.sampleCount,
|
||||
this.minHeartRateBpm,
|
||||
this.averageHeartRateBpm,
|
||||
this.maxHeartRateBpm,
|
||||
this.totalDistanceMeters,
|
||||
this.totalCaloriesKcal,
|
||||
}) : sessionId = _nonBlank(sessionId, 'Telemetry aggregate session id') {
|
||||
_requireNullableNonNegative(programIndex, 'Program index');
|
||||
_requireNullableNonNegative(exerciseIndex, 'Exercise index');
|
||||
_requireNullableNonNegative(setIndex, 'Set index');
|
||||
_requireNullableNonNegative(passageIndex, 'Passage index');
|
||||
_requireNullableNonNegative(stepIndex, 'Step index');
|
||||
_requireNonNegative(sampleCount, 'Telemetry sample count');
|
||||
_requireNullablePositive(minHeartRateBpm, 'Min heart rate bpm');
|
||||
_requireNullablePositiveDouble(
|
||||
averageHeartRateBpm,
|
||||
'Average heart rate bpm',
|
||||
);
|
||||
_requireNullablePositive(maxHeartRateBpm, 'Max heart rate bpm');
|
||||
_requireNullableNonNegativeDouble(
|
||||
totalDistanceMeters,
|
||||
'Total distance meters',
|
||||
);
|
||||
_requireNullableNonNegativeDouble(totalCaloriesKcal, 'Total calories kcal');
|
||||
}
|
||||
|
||||
final String sessionId;
|
||||
final WorkoutTelemetryAggregateScope scope;
|
||||
final int? programIndex;
|
||||
final int? exerciseIndex;
|
||||
final int? setIndex;
|
||||
final int? passageIndex;
|
||||
final int? stepIndex;
|
||||
final int sampleCount;
|
||||
final int? minHeartRateBpm;
|
||||
final double? averageHeartRateBpm;
|
||||
final int? maxHeartRateBpm;
|
||||
final double? totalDistanceMeters;
|
||||
final double? totalCaloriesKcal;
|
||||
}
|
||||
|
||||
final class WorkoutHistorySetResult {
|
||||
WorkoutHistorySetResult({
|
||||
required this.metadata,
|
||||
|
||||
Reference in New Issue
Block a user