feat(watch): route watch commands to existing session use cases (#91-C)
This commit is contained in:
686
test/application/watch_companion_command_handler_test.dart
Normal file
686
test/application/watch_companion_command_handler_test.dart
Normal file
@ -0,0 +1,686 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:gametime/application/application.dart';
|
||||
import 'package:gametime/domain/domain.dart';
|
||||
import 'package:watch_bridge_contract/watch_bridge_contract.dart';
|
||||
|
||||
void main() {
|
||||
test('routes startCurrentExercise to active execution timers', () async {
|
||||
final env = _env(
|
||||
session: _session(timeEnabled: true),
|
||||
projection: _projection(
|
||||
primaryAction: WatchPrimaryAction.startCurrentExercise,
|
||||
),
|
||||
);
|
||||
|
||||
final ack = await env.dispatch(WatchCommandType.startCurrentExercise);
|
||||
|
||||
expect(ack, WatchCommandAck.accepted);
|
||||
expect(env.repository.setTimerStates, hasLength(1));
|
||||
expect(env.projections.emitCount, 1);
|
||||
});
|
||||
|
||||
test('routes pauseSession and resumeSession to session use cases', () async {
|
||||
final pauseEnv = _env(
|
||||
session: _session(timeEnabled: true),
|
||||
projection: _projection(
|
||||
phase: WatchSessionPhase.running,
|
||||
primaryAction: WatchPrimaryAction.pauseSession,
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
await pauseEnv.dispatch(WatchCommandType.pauseSession),
|
||||
WatchCommandAck.accepted,
|
||||
);
|
||||
expect(pauseEnv.repository.session?.status, ActiveWorkoutStatus.paused);
|
||||
|
||||
final resumeEnv = _env(
|
||||
session: _session(status: ActiveWorkoutStatus.paused, pausedAt: _now),
|
||||
projection: _projection(
|
||||
phase: WatchSessionPhase.paused,
|
||||
primaryAction: WatchPrimaryAction.resumeSession,
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
await resumeEnv.dispatch(WatchCommandType.resumeSession),
|
||||
WatchCommandAck.accepted,
|
||||
);
|
||||
expect(resumeEnv.repository.session?.status, ActiveWorkoutStatus.running);
|
||||
});
|
||||
|
||||
test('routes startPreparedTimedStep to step timer start', () async {
|
||||
final session = _session(
|
||||
steps: [
|
||||
_step(),
|
||||
_step(id: 'step-2', position: 1),
|
||||
],
|
||||
);
|
||||
final env = _env(
|
||||
session: session,
|
||||
projection: _projection(
|
||||
phase: WatchSessionPhase.nextTimerReady,
|
||||
primaryAction: WatchPrimaryAction.startPreparedTimedStep,
|
||||
),
|
||||
);
|
||||
env.repository.stepProgressStates['step-state'] = _stepState(
|
||||
sessionId: session.metadata.id,
|
||||
stepId: 'step-2',
|
||||
stepIndex: 1,
|
||||
);
|
||||
|
||||
final ack = await env.dispatch(WatchCommandType.startPreparedTimedStep);
|
||||
|
||||
expect(ack, WatchCommandAck.accepted);
|
||||
expect(
|
||||
env.repository.stepProgressStates['step-state']?.status,
|
||||
ActiveExerciseStepProgressStatus.runningTimer,
|
||||
);
|
||||
});
|
||||
|
||||
test('routes skipCurrentStep to step use case', () async {
|
||||
final session = _session(steps: [_step()]);
|
||||
final env = _env(
|
||||
session: session,
|
||||
projection: _projection(
|
||||
secondaryActions: [WatchSecondaryAction.skipCurrentStep],
|
||||
),
|
||||
);
|
||||
env.repository.stepProgressStates['step-state'] = _stepState(
|
||||
sessionId: session.metadata.id,
|
||||
);
|
||||
|
||||
final ack = await env.dispatch(WatchCommandType.skipCurrentStep);
|
||||
|
||||
expect(ack, WatchCommandAck.accepted);
|
||||
expect(env.repository.stepResults, hasLength(1));
|
||||
});
|
||||
|
||||
test('routes skipCurrentPassage to step use case', () async {
|
||||
final session = _session(
|
||||
targetReps: 2,
|
||||
steps: [
|
||||
_step(),
|
||||
_step(id: 'step-2', position: 1),
|
||||
],
|
||||
);
|
||||
final env = _env(
|
||||
session: session,
|
||||
projection: _projection(
|
||||
secondaryActions: [WatchSecondaryAction.skipCurrentPassage],
|
||||
),
|
||||
);
|
||||
env.repository.stepProgressStates['step-state'] = _stepState(
|
||||
sessionId: session.metadata.id,
|
||||
);
|
||||
|
||||
final ack = await env.dispatch(WatchCommandType.skipCurrentPassage);
|
||||
|
||||
expect(ack, WatchCommandAck.accepted);
|
||||
expect(env.repository.stepResults, hasLength(2));
|
||||
});
|
||||
|
||||
test(
|
||||
'routes finishCurrentSet and advances to next set without rest',
|
||||
() async {
|
||||
final session = _session(timeEnabled: true, setsCount: 2);
|
||||
final env = _env(
|
||||
session: session,
|
||||
projection: _projection(
|
||||
secondaryActions: [WatchSecondaryAction.finishCurrentSet],
|
||||
),
|
||||
);
|
||||
env.repository.setTimerStates['set'] = _setTimer(
|
||||
sessionId: session.metadata.id,
|
||||
);
|
||||
|
||||
final ack = await env.dispatch(WatchCommandType.finishCurrentSet);
|
||||
|
||||
expect(ack, WatchCommandAck.accepted);
|
||||
expect(env.repository.results, hasLength(1));
|
||||
expect(env.repository.session?.currentSetIndex, 1);
|
||||
},
|
||||
);
|
||||
|
||||
test('routes finishCurrentSet and starts rest before next set', () async {
|
||||
final session = _session(timeEnabled: true, setsCount: 2, restSeconds: 60);
|
||||
final env = _env(
|
||||
session: session,
|
||||
projection: _projection(
|
||||
secondaryActions: [WatchSecondaryAction.finishCurrentSet],
|
||||
),
|
||||
);
|
||||
env.repository.setTimerStates['set'] = _setTimer(
|
||||
sessionId: session.metadata.id,
|
||||
);
|
||||
|
||||
final ack = await env.dispatch(WatchCommandType.finishCurrentSet);
|
||||
|
||||
expect(ack, WatchCommandAck.accepted);
|
||||
expect(env.repository.restStates.values.single.plannedRestSeconds, 60);
|
||||
expect(env.repository.session?.currentSetIndex, 0);
|
||||
});
|
||||
|
||||
test('routes skipCurrentSet and advances once on retry duplicate', () async {
|
||||
final session = _session(timeEnabled: true, setsCount: 3);
|
||||
final env = _env(
|
||||
session: session,
|
||||
projection: _projection(
|
||||
secondaryActions: [WatchSecondaryAction.skipCurrentSet],
|
||||
),
|
||||
);
|
||||
env.repository.setTimerStates['set'] = _setTimer(
|
||||
sessionId: session.metadata.id,
|
||||
);
|
||||
final command = _command(WatchCommandType.skipCurrentSet);
|
||||
|
||||
final firstAck = await env.handler.dispatch(command);
|
||||
final retryAck = await env.handler.dispatch(command);
|
||||
|
||||
expect(firstAck, WatchCommandAck.accepted);
|
||||
expect(retryAck, WatchCommandAck.acceptedNoOp);
|
||||
expect(env.repository.results, hasLength(1));
|
||||
expect(env.repository.session?.currentSetIndex, 1);
|
||||
});
|
||||
|
||||
test('routes skipCurrentRest to rest skip and next position', () async {
|
||||
final session = _session(setsCount: 2);
|
||||
final env = _env(
|
||||
session: session,
|
||||
projection: _projection(
|
||||
phase: WatchSessionPhase.restRunning,
|
||||
primaryAction: WatchPrimaryAction.pauseSession,
|
||||
secondaryActions: [WatchSecondaryAction.skipCurrentRest],
|
||||
),
|
||||
);
|
||||
env.repository.restStates['rest'] = ActiveRestState(
|
||||
metadata: _metadata('rest'),
|
||||
activeWorkoutSessionId: session.metadata.id,
|
||||
afterProgramIndex: 0,
|
||||
afterExerciseIndex: 0,
|
||||
afterSetIndex: 0,
|
||||
plannedRestSeconds: 60,
|
||||
adjustedRestSeconds: 60,
|
||||
startedAt: _now,
|
||||
);
|
||||
|
||||
final ack = await env.dispatch(WatchCommandType.skipCurrentRest);
|
||||
|
||||
expect(ack, WatchCommandAck.accepted);
|
||||
expect(env.repository.restStates['rest']?.skippedAt, isNotNull);
|
||||
expect(env.repository.session?.currentSetIndex, 1);
|
||||
});
|
||||
|
||||
test(
|
||||
'rejects stale revision, non applicable, missing and mismatch',
|
||||
() async {
|
||||
final stale = _env(
|
||||
session: _session(),
|
||||
projection: _projection(revision: 2),
|
||||
);
|
||||
expect(
|
||||
await stale.dispatch(WatchCommandType.startCurrentExercise),
|
||||
WatchCommandAck.rejectedStaleRevision,
|
||||
);
|
||||
|
||||
final nonApplicable = _env(
|
||||
session: _session(),
|
||||
projection: _projection(),
|
||||
);
|
||||
expect(
|
||||
await nonApplicable.dispatch(WatchCommandType.pauseSession),
|
||||
WatchCommandAck.rejectedNotApplicable,
|
||||
);
|
||||
|
||||
final missing = _env(
|
||||
projection: _projection(
|
||||
phase: WatchSessionPhase.noActiveSession,
|
||||
deviceSessionId: '',
|
||||
primaryAction: WatchPrimaryAction.none,
|
||||
),
|
||||
);
|
||||
expect(
|
||||
await missing.dispatch(WatchCommandType.startCurrentExercise),
|
||||
WatchCommandAck.rejectedNoActiveSession,
|
||||
);
|
||||
|
||||
final mismatch = _env(
|
||||
session: _session(),
|
||||
projection: _projection(deviceSessionId: 'other-session'),
|
||||
);
|
||||
expect(
|
||||
await mismatch.dispatch(WatchCommandType.startCurrentExercise),
|
||||
WatchCommandAck.rejectedSessionMismatch,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
final _now = DateTime.utc(2026, 7, 25, 12);
|
||||
|
||||
_Harness _env({
|
||||
ActiveWorkoutSession? session,
|
||||
required WatchSessionProjection projection,
|
||||
}) {
|
||||
final repository = _FakeActiveSessionRepository()..session = session;
|
||||
final clock = _FakeClock(_now);
|
||||
final ids = _FakeIds();
|
||||
final activeUseCases = ActiveWorkoutSessionUseCases(
|
||||
sessionRepository: repository,
|
||||
templateRepository: _FakeWorkoutTemplateRepository(),
|
||||
clock: clock,
|
||||
ids: ids,
|
||||
originDeviceId: 'device-1',
|
||||
);
|
||||
final stepUseCases = ActiveExerciseStepUseCases(
|
||||
sessionRepository: repository,
|
||||
clock: clock,
|
||||
ids: ids,
|
||||
originDeviceId: 'device-1',
|
||||
);
|
||||
final projections = _FakeProjectionSource(projection);
|
||||
return _Harness(
|
||||
repository: repository,
|
||||
projections: projections,
|
||||
handler: WatchCompanionCommandHandler(
|
||||
sessionRepository: repository,
|
||||
activeSessionUseCases: activeUseCases,
|
||||
stepUseCases: stepUseCases,
|
||||
projectionSource: projections,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final class _Harness {
|
||||
const _Harness({
|
||||
required this.repository,
|
||||
required this.projections,
|
||||
required this.handler,
|
||||
});
|
||||
|
||||
final _FakeActiveSessionRepository repository;
|
||||
final _FakeProjectionSource projections;
|
||||
final WatchCompanionCommandHandler handler;
|
||||
|
||||
Future<WatchCommandAck> dispatch(WatchCommandType type) {
|
||||
return handler.dispatch(_command(type));
|
||||
}
|
||||
}
|
||||
|
||||
WatchCommandEnvelope _command(
|
||||
WatchCommandType type, {
|
||||
String commandId = 'command-1',
|
||||
}) {
|
||||
return WatchCommandEnvelope(
|
||||
commandId: commandId,
|
||||
type: type,
|
||||
sessionId: 'session-1',
|
||||
expectedRevision: 1,
|
||||
sentAtEpochMs: _now.millisecondsSinceEpoch,
|
||||
);
|
||||
}
|
||||
|
||||
WatchSessionProjection _projection({
|
||||
WatchSessionPhase phase = WatchSessionPhase.ready,
|
||||
String deviceSessionId = 'session-1',
|
||||
int revision = 1,
|
||||
WatchPrimaryAction primaryAction = WatchPrimaryAction.startCurrentExercise,
|
||||
List<WatchSecondaryAction> secondaryActions = const [
|
||||
WatchSecondaryAction.finishCurrentSet,
|
||||
WatchSecondaryAction.skipCurrentSet,
|
||||
],
|
||||
}) {
|
||||
return WatchSessionProjection(
|
||||
deviceSessionId: deviceSessionId,
|
||||
revision: revision,
|
||||
projectedAtEpochMs: _now.millisecondsSinceEpoch,
|
||||
phase: phase,
|
||||
phoneReachable: true,
|
||||
seriesIndex: 1,
|
||||
seriesTotal: 2,
|
||||
exerciseName: 'Squat',
|
||||
primaryAction: primaryAction,
|
||||
secondaryActions: secondaryActions,
|
||||
);
|
||||
}
|
||||
|
||||
ActiveWorkoutSession _session({
|
||||
ActiveWorkoutStatus status = ActiveWorkoutStatus.running,
|
||||
DateTime? pausedAt,
|
||||
int currentSetIndex = 0,
|
||||
int setsCount = 2,
|
||||
bool timeEnabled = false,
|
||||
int? targetReps = 10,
|
||||
int restSeconds = 0,
|
||||
List<ExerciseStep> steps = const [],
|
||||
}) {
|
||||
final exerciseSnapshot = {
|
||||
'id': 'exercise-snapshot-1',
|
||||
'exerciseNameSnapshot': 'Squat',
|
||||
'setsCount': setsCount,
|
||||
'timeEnabled': timeEnabled,
|
||||
'repsEnabled': true,
|
||||
'scoreEnabled': false,
|
||||
'targetReps': targetReps,
|
||||
'scoreInputModeSnapshot': ScoreInputMode.manual.name,
|
||||
'restSecondsOverride': restSeconds,
|
||||
'exerciseStepsSnapshot': steps
|
||||
.map((step) => step.toSnapshotJson())
|
||||
.toList(),
|
||||
'autoStartNextTimedStepSnapshot': false,
|
||||
};
|
||||
return ActiveWorkoutSession(
|
||||
metadata: _metadata('session-1'),
|
||||
status: status,
|
||||
startedAt: _now,
|
||||
pausedAt: pausedAt,
|
||||
lastPersistedAt: _now,
|
||||
elapsedActiveMs: 0,
|
||||
currentProgramIndex: 0,
|
||||
currentExerciseIndex: 0,
|
||||
currentSetIndex: currentSetIndex,
|
||||
resolvedTemplateSnapshotJson: jsonEncode({
|
||||
'programs': [
|
||||
{
|
||||
'id': 'program-snapshot-1',
|
||||
'programNameSnapshot': 'Programme',
|
||||
'programSnapshotJson': jsonEncode({
|
||||
'exercises': [exerciseSnapshot],
|
||||
}),
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
ExerciseStep _step({String id = 'step-1', int position = 0}) {
|
||||
return ExerciseStep(
|
||||
id: id,
|
||||
position: position,
|
||||
name: 'Step ${position + 1}',
|
||||
type: ExerciseStepType.time,
|
||||
defaultTargetValue: 1,
|
||||
);
|
||||
}
|
||||
|
||||
ActiveExerciseStepProgressState _stepState({
|
||||
required String sessionId,
|
||||
String stepId = 'step-1',
|
||||
int stepIndex = 0,
|
||||
}) {
|
||||
return ActiveExerciseStepProgressState(
|
||||
metadata: _metadata('step-state'),
|
||||
activeWorkoutSessionId: sessionId,
|
||||
programIndex: 0,
|
||||
exerciseIndex: 0,
|
||||
setIndex: 0,
|
||||
currentPassageIndex: 0,
|
||||
currentStepIndex: stepIndex,
|
||||
currentStepSnapshotId: stepId,
|
||||
status: ActiveExerciseStepProgressStatus.stoppedTimer,
|
||||
accumulatedMs: 0,
|
||||
lastTransitionAt: _now,
|
||||
);
|
||||
}
|
||||
|
||||
ActiveSetTimerState _setTimer({required String sessionId}) {
|
||||
return ActiveSetTimerState(
|
||||
metadata: _metadata('set'),
|
||||
activeWorkoutSessionId: sessionId,
|
||||
programIndex: 0,
|
||||
exerciseIndex: 0,
|
||||
setIndex: 0,
|
||||
status: ActiveSetTimerStatus.running,
|
||||
startedAt: _now,
|
||||
accumulatedMs: 0,
|
||||
);
|
||||
}
|
||||
|
||||
EntityMetadata _metadata(String id) {
|
||||
return EntityMetadata(
|
||||
id: id,
|
||||
createdAt: _now,
|
||||
updatedAt: _now,
|
||||
originDeviceId: 'device-1',
|
||||
);
|
||||
}
|
||||
|
||||
final class _FakeProjectionSource implements WatchProjectionSource {
|
||||
_FakeProjectionSource(this.projection);
|
||||
|
||||
WatchSessionProjection projection;
|
||||
var emitCount = 0;
|
||||
|
||||
@override
|
||||
Stream<WatchSessionProjection> get projections => const Stream.empty();
|
||||
|
||||
@override
|
||||
Future<WatchSessionProjection> currentProjection() async => projection;
|
||||
|
||||
@override
|
||||
Future<WatchSessionProjection> emitCurrentProjection() async {
|
||||
emitCount += 1;
|
||||
projection = WatchSessionProjection(
|
||||
deviceSessionId: projection.deviceSessionId,
|
||||
revision: projection.revision + 1,
|
||||
projectedAtEpochMs: projection.projectedAtEpochMs,
|
||||
phase: projection.phase,
|
||||
phoneReachable: projection.phoneReachable,
|
||||
seriesIndex: projection.seriesIndex,
|
||||
seriesTotal: projection.seriesTotal,
|
||||
exerciseName: projection.exerciseName,
|
||||
primaryAction: projection.primaryAction,
|
||||
secondaryActions: projection.secondaryActions,
|
||||
);
|
||||
return projection;
|
||||
}
|
||||
}
|
||||
|
||||
final class _FakeClock implements Clock {
|
||||
const _FakeClock(this.value);
|
||||
|
||||
final DateTime value;
|
||||
|
||||
@override
|
||||
DateTime now() => value;
|
||||
}
|
||||
|
||||
final class _FakeIds implements IdGenerator {
|
||||
var next = 0;
|
||||
|
||||
@override
|
||||
String newId() {
|
||||
next += 1;
|
||||
return 'id-$next';
|
||||
}
|
||||
}
|
||||
|
||||
final class _FakeWorkoutTemplateRepository
|
||||
implements WorkoutTemplateRepository {
|
||||
@override
|
||||
Future<WorkoutTemplate?> findById(String id) async => null;
|
||||
|
||||
@override
|
||||
Future<List<WorkoutTemplate>> listActive() async => const [];
|
||||
|
||||
@override
|
||||
Future<void> replaceComposition(
|
||||
WorkoutTemplate template,
|
||||
DateTime deletedAt,
|
||||
) async {}
|
||||
|
||||
@override
|
||||
Future<void> save(WorkoutTemplate template) async {}
|
||||
|
||||
@override
|
||||
Future<void> saveOverride(WorkoutTemplateExerciseOverride override) async {}
|
||||
|
||||
@override
|
||||
Future<void> saveProgram(WorkoutTemplateProgram program) async {}
|
||||
}
|
||||
|
||||
final class _FakeActiveSessionRepository implements ActiveSessionRepository {
|
||||
ActiveWorkoutSession? session;
|
||||
final results = <ActiveSetResult>[];
|
||||
final restStates = <String, ActiveRestState>{};
|
||||
final setTimerStates = <String, ActiveSetTimerState>{};
|
||||
final scoreStopwatchStates = <String, ActiveScoreStopwatchState>{};
|
||||
final stepProgressStates = <String, ActiveExerciseStepProgressState>{};
|
||||
final stepResults = <ActiveExerciseStepResult>[];
|
||||
|
||||
@override
|
||||
Future<void> deleteScoreStopwatchState({
|
||||
required String sessionId,
|
||||
required int programIndex,
|
||||
required int exerciseIndex,
|
||||
required int setIndex,
|
||||
required DateTime deletedAt,
|
||||
}) async {
|
||||
scoreStopwatchStates.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ActiveWorkoutSession?> findById(String id) async {
|
||||
return session?.metadata.id == id ? session : null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ActiveWorkoutSession?> findOpen() async => session;
|
||||
|
||||
@override
|
||||
Future<ActiveExerciseStepProgressState?> findExerciseStepProgressState({
|
||||
required String sessionId,
|
||||
required int programIndex,
|
||||
required int exerciseIndex,
|
||||
required int setIndex,
|
||||
}) async {
|
||||
return stepProgressStates.values.where((state) {
|
||||
return state.activeWorkoutSessionId == sessionId &&
|
||||
state.programIndex == programIndex &&
|
||||
state.exerciseIndex == exerciseIndex &&
|
||||
state.setIndex == setIndex;
|
||||
}).firstOrNull;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ActiveRestState?> findRestStateById(String id) async {
|
||||
return restStates[id];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ActiveScoreStopwatchState?> findScoreStopwatchState({
|
||||
required String sessionId,
|
||||
required int programIndex,
|
||||
required int exerciseIndex,
|
||||
required int setIndex,
|
||||
}) async {
|
||||
return scoreStopwatchStates.values.where((state) {
|
||||
return state.activeWorkoutSessionId == sessionId &&
|
||||
state.programIndex == programIndex &&
|
||||
state.exerciseIndex == exerciseIndex &&
|
||||
state.setIndex == setIndex;
|
||||
}).firstOrNull;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ActiveSetTimerState?> findSetTimerState({
|
||||
required String sessionId,
|
||||
required int programIndex,
|
||||
required int exerciseIndex,
|
||||
required int setIndex,
|
||||
}) async {
|
||||
return setTimerStates.values.where((state) {
|
||||
return state.activeWorkoutSessionId == sessionId &&
|
||||
state.programIndex == programIndex &&
|
||||
state.exerciseIndex == exerciseIndex &&
|
||||
state.setIndex == setIndex;
|
||||
}).firstOrNull;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ActiveExerciseStepProgressState>> listExerciseStepProgressStates(
|
||||
String sessionId,
|
||||
) async {
|
||||
return stepProgressStates.values
|
||||
.where((state) => state.activeWorkoutSessionId == sessionId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ActiveExerciseStepResult>> listExerciseStepResults(
|
||||
String sessionId,
|
||||
) async {
|
||||
return stepResults
|
||||
.where((result) => result.activeWorkoutSessionId == sessionId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ActiveRestState>> listRestStates(String sessionId) async {
|
||||
return restStates.values
|
||||
.where((state) => state.activeWorkoutSessionId == sessionId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ActiveScoreStopwatchState>> listScoreStopwatchStates(
|
||||
String sessionId,
|
||||
) async {
|
||||
return scoreStopwatchStates.values
|
||||
.where((state) => state.activeWorkoutSessionId == sessionId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ActiveSetResult>> listSetResults(String sessionId) async {
|
||||
return results
|
||||
.where((result) => result.activeWorkoutSessionId == sessionId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ActiveSetTimerState>> listSetTimerStates(String sessionId) async {
|
||||
return setTimerStates.values
|
||||
.where((state) => state.activeWorkoutSessionId == sessionId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> save(ActiveWorkoutSession session) async {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveExerciseStepProgressState(
|
||||
ActiveExerciseStepProgressState state,
|
||||
) async {
|
||||
stepProgressStates[state.metadata.id] = state;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveExerciseStepResult(ActiveExerciseStepResult result) async {
|
||||
stepResults.add(result);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveRestState(ActiveRestState restState) async {
|
||||
restStates[restState.metadata.id] = restState;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveScoreStopwatchState(ActiveScoreStopwatchState state) async {
|
||||
scoreStopwatchStates[state.metadata.id] = state;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSetResult(ActiveSetResult result) async {
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveSetTimerState(ActiveSetTimerState state) async {
|
||||
setTimerStates[state.metadata.id] = state;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user