fix(watch): finalise correctif sync workoutHistory/exercise et distance live montre (#157)

This commit is contained in:
2026-07-29 11:22:17 +02:00
parent 6f913e4e8d
commit 30c6259748
28 changed files with 1658 additions and 248 deletions

View File

@ -19,11 +19,11 @@ final class HttpRemoteShareApi implements RemoteShareApi {
final response = await client.postJson(
'/shares',
bearerToken: token,
body: {
'resourceType': _shareResourceTypeToWire(resourceType),
'payload': payload,
'recipientEmails': recipientEmails,
},
body: _shareRequestBody(
resourceType: resourceType,
payload: payload,
recipientEmails: recipientEmails,
),
expectedStatuses: const {201},
);
return RemoteShareSendResult(
@ -43,12 +43,22 @@ final class HttpRemoteShareApi implements RemoteShareApi {
}
@override
Future<RemoteSyncedItem> acceptShare(String shareId, String token) async {
Future<List<RemoteSyncedItem>> acceptShare(
String shareId,
String token,
) async {
final response = await client.postJson(
'/shares/$shareId/accept',
bearerToken: token,
);
return _syncedItemFromJson(_map(response['createdResource']));
final resources = _list(
response,
'createdResources',
).map((item) => _syncedItemFromJson(_map(item))).toList(growable: false);
if (resources.isNotEmpty) {
return resources;
}
return [_syncedItemFromJson(_map(response['createdResource']))];
}
@override
@ -62,20 +72,91 @@ final class HttpRemoteShareApi implements RemoteShareApi {
}
}
Map<String, Object?> _shareRequestBody({
required ShareResourceType resourceType,
required Map<String, Object?> payload,
required List<String> recipientEmails,
}) {
if (resourceType != ShareResourceType.pack) {
return {
'shareKind': 'single',
'resourceType': _shareResourceTypeToWire(resourceType),
'payload': payload,
'recipientEmails': recipientEmails,
};
}
final rawWorkouts = payload['workouts'];
if (rawWorkouts is! List) {
throw const RemoteAuthException(
RemoteAuthFailure.unknown,
'Pack payload must contain workouts.',
);
}
return {
'shareKind': 'pack',
'packName': _stringFromObject(payload['name'], 'Pack'),
'items': [
for (final rawWorkout in rawWorkouts)
{'resourceType': 'workoutTemplate', 'payload': _map(rawWorkout)},
],
'recipientEmails': recipientEmails,
};
}
ShareInboxItem _inboxItemFromJson(Map<String, Object?> json) {
final resourceType = _inboxResourceType(json);
return ShareInboxItem(
shareId: _requiredString(json, 'shareId'),
senderUserId: _requiredString(json, 'senderUserId'),
resourceType: _shareResourceTypeFromWire(
_requiredString(json, 'resourceType'),
),
payloadJson: _jsonObjectString(json['payload']),
resourceType: resourceType,
payloadJson: _payloadJsonString(json, resourceType),
status: _shareInboxStatusFromWire(_requiredString(json, 'status')),
createdAt: _requiredDateTime(json, 'createdAt'),
respondedAt: _optionalDateTime(json, 'respondedAt'),
);
}
ShareResourceType _inboxResourceType(Map<String, Object?> json) {
final shareKind = json['shareKind'];
if (shareKind == 'pack') {
return ShareResourceType.pack;
}
return _shareResourceTypeFromWire(_requiredString(json, 'resourceType'));
}
String _payloadJsonString(
Map<String, Object?> json,
ShareResourceType resourceType,
) {
final payload = _map(json['payload']);
if (resourceType != ShareResourceType.pack) {
return jsonEncode(payload);
}
if (payload['workouts'] is List) {
return jsonEncode({
'name': json['packName'] as String? ?? payload['name'] ?? 'Pack',
...payload,
});
}
final rawItems = payload['items'];
if (rawItems is! List) {
return jsonEncode({
'name': json['packName'] as String? ?? 'Pack',
'workouts': const <Object?>[],
});
}
return jsonEncode({
'name': json['packName'] as String? ?? 'Pack',
'workouts': [
for (final rawItem in rawItems)
if (rawItem is Map &&
Map<String, Object?>.from(rawItem)['resourceType'] ==
'workoutTemplate')
_map(Map<String, Object?>.from(rawItem)['payload']),
],
});
}
RemoteSyncedItem _syncedItemFromJson(Map<String, Object?> json) {
return RemoteSyncedItem(
resourceType: _syncResourceTypeFromWire(
@ -163,6 +244,10 @@ String _requiredString(Map<String, Object?> json, String key) {
);
}
String _stringFromObject(Object? value, String fallback) {
return value is String && value.trim().isNotEmpty ? value : fallback;
}
int _requiredInt(Map<String, Object?> json, String key) {
final value = json[key];
if (value is int) {
@ -184,7 +269,3 @@ DateTime? _optionalDateTime(Map<String, Object?> json, String key) {
? DateTime.parse(value).toUtc()
: null;
}
String _jsonObjectString(Object? value) {
return jsonEncode(_map(value));
}