This commit is contained in:
@ -63,6 +63,10 @@ enum WatchManualScoreScope { series, step }
|
||||
|
||||
enum WatchStepType { time, reps }
|
||||
|
||||
enum WatchAlertKind { countdownTick, countdownFinished }
|
||||
|
||||
enum WatchAlertPattern { countdownTick, timerFinished }
|
||||
|
||||
final class WatchCommandEnvelope {
|
||||
const WatchCommandEnvelope({
|
||||
this.schemaVersion = watchBridgeSchemaVersion,
|
||||
@ -134,6 +138,133 @@ final class WatchCommandEnvelope {
|
||||
}
|
||||
}
|
||||
|
||||
final class WatchAlertEnvelope {
|
||||
const WatchAlertEnvelope({
|
||||
this.schemaVersion = watchBridgeSchemaVersion,
|
||||
required this.alertId,
|
||||
required this.sessionId,
|
||||
required this.revision,
|
||||
required this.kind,
|
||||
required this.timerKind,
|
||||
this.programIndex,
|
||||
this.exerciseIndex,
|
||||
this.setIndex,
|
||||
this.passageIndex,
|
||||
this.stepIndex,
|
||||
required this.scheduledForEpochMs,
|
||||
required this.expiresAtEpochMs,
|
||||
required this.pattern,
|
||||
});
|
||||
|
||||
factory WatchAlertEnvelope.fromJson(Map<String, Object?> json) {
|
||||
return WatchAlertEnvelope(
|
||||
schemaVersion: _intFromJson(
|
||||
json['schemaVersion'],
|
||||
watchBridgeSchemaVersion,
|
||||
),
|
||||
alertId: _stringFromJson(json['alertId']),
|
||||
sessionId: _stringFromJson(json['sessionId']),
|
||||
revision: _intFromJson(json['revision'], 0),
|
||||
kind: _enumFromJson(
|
||||
json['kind'],
|
||||
WatchAlertKind.values,
|
||||
WatchAlertKind.countdownTick,
|
||||
),
|
||||
timerKind: _enumFromJson(
|
||||
json['timerKind'],
|
||||
WatchTimerKind.values,
|
||||
WatchTimerKind.step,
|
||||
),
|
||||
programIndex: _nullableIntFromJson(json['programIndex']),
|
||||
exerciseIndex: _nullableIntFromJson(json['exerciseIndex']),
|
||||
setIndex: _nullableIntFromJson(json['setIndex']),
|
||||
passageIndex: _nullableIntFromJson(json['passageIndex']),
|
||||
stepIndex: _nullableIntFromJson(json['stepIndex']),
|
||||
scheduledForEpochMs: _intFromJson(json['scheduledForEpochMs'], 0),
|
||||
expiresAtEpochMs: _intFromJson(json['expiresAtEpochMs'], 0),
|
||||
pattern: _enumFromJson(
|
||||
json['pattern'],
|
||||
WatchAlertPattern.values,
|
||||
WatchAlertPattern.countdownTick,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final int schemaVersion;
|
||||
final String alertId;
|
||||
final String sessionId;
|
||||
final int revision;
|
||||
final WatchAlertKind kind;
|
||||
final WatchTimerKind timerKind;
|
||||
final int? programIndex;
|
||||
final int? exerciseIndex;
|
||||
final int? setIndex;
|
||||
final int? passageIndex;
|
||||
final int? stepIndex;
|
||||
final int scheduledForEpochMs;
|
||||
final int expiresAtEpochMs;
|
||||
final WatchAlertPattern pattern;
|
||||
|
||||
Map<String, Object?> toJson() {
|
||||
return {
|
||||
'schemaVersion': schemaVersion,
|
||||
'alertId': alertId,
|
||||
'sessionId': sessionId,
|
||||
'revision': revision,
|
||||
'kind': kind.name,
|
||||
'timerKind': timerKind.name,
|
||||
'programIndex': programIndex,
|
||||
'exerciseIndex': exerciseIndex,
|
||||
'setIndex': setIndex,
|
||||
'passageIndex': passageIndex,
|
||||
'stepIndex': stepIndex,
|
||||
'scheduledForEpochMs': scheduledForEpochMs,
|
||||
'expiresAtEpochMs': expiresAtEpochMs,
|
||||
'pattern': pattern.name,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
other is WatchAlertEnvelope &&
|
||||
schemaVersion == other.schemaVersion &&
|
||||
alertId == other.alertId &&
|
||||
sessionId == other.sessionId &&
|
||||
revision == other.revision &&
|
||||
kind == other.kind &&
|
||||
timerKind == other.timerKind &&
|
||||
programIndex == other.programIndex &&
|
||||
exerciseIndex == other.exerciseIndex &&
|
||||
setIndex == other.setIndex &&
|
||||
passageIndex == other.passageIndex &&
|
||||
stepIndex == other.stepIndex &&
|
||||
scheduledForEpochMs == other.scheduledForEpochMs &&
|
||||
expiresAtEpochMs == other.expiresAtEpochMs &&
|
||||
pattern == other.pattern;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
schemaVersion,
|
||||
alertId,
|
||||
sessionId,
|
||||
revision,
|
||||
kind,
|
||||
timerKind,
|
||||
programIndex,
|
||||
exerciseIndex,
|
||||
setIndex,
|
||||
passageIndex,
|
||||
stepIndex,
|
||||
scheduledForEpochMs,
|
||||
expiresAtEpochMs,
|
||||
pattern,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class WatchSessionProjection {
|
||||
const WatchSessionProjection({
|
||||
this.schemaVersion = watchBridgeSchemaVersion,
|
||||
|
||||
@ -52,6 +52,55 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('WatchAlertEnvelope', () {
|
||||
test('round-trips every alert kind through JSON', () {
|
||||
for (final kind in WatchAlertKind.values) {
|
||||
final alert = WatchAlertEnvelope(
|
||||
alertId: 'alert-${kind.name}',
|
||||
sessionId: 'session-1',
|
||||
revision: 7,
|
||||
kind: kind,
|
||||
timerKind: WatchTimerKind.step,
|
||||
programIndex: 0,
|
||||
exerciseIndex: 1,
|
||||
setIndex: 2,
|
||||
passageIndex: 3,
|
||||
stepIndex: 4,
|
||||
scheduledForEpochMs: 1710000000700,
|
||||
expiresAtEpochMs: 1710000002700,
|
||||
pattern: kind == WatchAlertKind.countdownTick
|
||||
? WatchAlertPattern.countdownTick
|
||||
: WatchAlertPattern.timerFinished,
|
||||
);
|
||||
|
||||
final decoded = WatchAlertEnvelope.fromJson(
|
||||
jsonDecode(jsonEncode(alert.toJson())) as Map<String, Object?>,
|
||||
);
|
||||
|
||||
expect(decoded, alert);
|
||||
}
|
||||
});
|
||||
|
||||
test('falls back safely for absent fields', () {
|
||||
final alert = WatchAlertEnvelope.fromJson({});
|
||||
|
||||
expect(alert.schemaVersion, watchBridgeSchemaVersion);
|
||||
expect(alert.alertId, '');
|
||||
expect(alert.sessionId, '');
|
||||
expect(alert.revision, 0);
|
||||
expect(alert.kind, WatchAlertKind.countdownTick);
|
||||
expect(alert.timerKind, WatchTimerKind.step);
|
||||
expect(alert.programIndex, isNull);
|
||||
expect(alert.exerciseIndex, isNull);
|
||||
expect(alert.setIndex, isNull);
|
||||
expect(alert.passageIndex, isNull);
|
||||
expect(alert.stepIndex, isNull);
|
||||
expect(alert.scheduledForEpochMs, 0);
|
||||
expect(alert.expiresAtEpochMs, 0);
|
||||
expect(alert.pattern, WatchAlertPattern.countdownTick);
|
||||
});
|
||||
});
|
||||
|
||||
group('WatchSensorSummary', () {
|
||||
test('round-trips heart rate summary through JSON', () {
|
||||
const summary = WatchSensorSummary(
|
||||
|
||||
Reference in New Issue
Block a user