feat(online): partage client - use cases, inbox cache et import local (ticket #66)

Ajoute l'adapter API distant de partage (infrastructure/remote/share_api.dart)
et les use cases associés (application/use_cases.dart), avec cache
d'inbox et import local des ressources partagées acceptées. Étend le
modèle Drift (migration schemaVersion 11→12) et les entités du domaine
en conséquence. flutter pub get OK, build_runner OK, dart format
appliqué, analyze propre (mêmes infos préexistantes), 124/124 tests
verts, build APK debug validé.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 21:54:45 +02:00
parent 715fdd4335
commit 010e055404
12 changed files with 3445 additions and 1 deletions

View File

@ -16,6 +16,14 @@ enum SetResultStatus { completed, skipped }
enum ActiveScoreStopwatchStatus { running, stopped }
enum ShareResourceType { program, workoutTemplate }
enum ShareInboxStatus { pending, accepted, declined, revoked }
enum PendingShareActionType { send, accept, decline, revoke }
enum PendingShareActionStatus { pending, succeeded, failed }
enum ActiveExerciseStepProgressStatus {
notStarted,
waitingManual,
@ -154,6 +162,115 @@ final class UserAccountSession {
}
}
final class ShareInboxItem {
ShareInboxItem({
required String shareId,
required String senderUserId,
required this.resourceType,
required String payloadJson,
required this.status,
required this.createdAt,
this.respondedAt,
}) : shareId = _nonBlank(shareId, 'Share id'),
senderUserId = _nonBlank(senderUserId, 'Sender user id'),
payloadJson = _nonBlank(payloadJson, 'Share payload JSON');
final String shareId;
final String senderUserId;
final ShareResourceType resourceType;
final String payloadJson;
final ShareInboxStatus status;
final DateTime createdAt;
final DateTime? respondedAt;
ShareInboxItem copyWith({
String? senderUserId,
ShareResourceType? resourceType,
String? payloadJson,
ShareInboxStatus? status,
DateTime? createdAt,
Object? respondedAt = _unchanged,
}) {
return ShareInboxItem(
shareId: shareId,
senderUserId: senderUserId ?? this.senderUserId,
resourceType: resourceType ?? this.resourceType,
payloadJson: payloadJson ?? this.payloadJson,
status: status ?? this.status,
createdAt: createdAt ?? this.createdAt,
respondedAt: respondedAt == _unchanged
? this.respondedAt
: respondedAt as DateTime?,
);
}
}
final class PendingShareAction {
PendingShareAction({
required String id,
required this.actionType,
this.shareId,
this.resourceType,
this.payloadJson,
this.recipientEmailsJson,
required this.createdAt,
this.lastAttemptAt,
this.attemptCount = 0,
this.status = PendingShareActionStatus.pending,
}) : id = _nonBlank(id, 'Pending share action id') {
_requireNonNegative(attemptCount, 'Share action attempt count');
if (shareId != null) {
_nonBlank(shareId, 'Share id');
}
if (payloadJson != null) {
_nonBlank(payloadJson, 'Share payload JSON');
}
if (recipientEmailsJson != null) {
_nonBlank(recipientEmailsJson, 'Recipient emails JSON');
}
}
final String id;
final PendingShareActionType actionType;
final String? shareId;
final ShareResourceType? resourceType;
final String? payloadJson;
final String? recipientEmailsJson;
final DateTime createdAt;
final DateTime? lastAttemptAt;
final int attemptCount;
final PendingShareActionStatus status;
PendingShareAction copyWith({
Object? shareId = _unchanged,
ShareResourceType? resourceType,
Object? payloadJson = _unchanged,
Object? recipientEmailsJson = _unchanged,
Object? lastAttemptAt = _unchanged,
int? attemptCount,
PendingShareActionStatus? status,
}) {
return PendingShareAction(
id: id,
actionType: actionType,
shareId: shareId == _unchanged ? this.shareId : shareId as String?,
resourceType: resourceType ?? this.resourceType,
payloadJson: payloadJson == _unchanged
? this.payloadJson
: payloadJson as String?,
recipientEmailsJson: recipientEmailsJson == _unchanged
? this.recipientEmailsJson
: recipientEmailsJson as String?,
createdAt: createdAt,
lastAttemptAt: lastAttemptAt == _unchanged
? this.lastAttemptAt
: lastAttemptAt as DateTime?,
attemptCount: attemptCount ?? this.attemptCount,
status: status ?? this.status,
);
}
}
final class MediaAsset {
const MediaAsset({
required this.metadata,