Add rejected mutation requeue flow in sync UI
This commit is contained in:
@@ -68,22 +68,39 @@ class SyncQueueRepository extends AsyncNotifier<SyncState> {
|
||||
Future<void> applyPushResult(SyncPushResult result, {DateTime? at}) async {
|
||||
final SyncState current = await _currentState();
|
||||
final DateTime now = at ?? DateTime.now();
|
||||
final Set<String> completedMutationIds = <String>{
|
||||
...result.accepted.map((MutationAck ack) => ack.clientMutationId),
|
||||
final Set<String> rejectedMutationIds = <String>{
|
||||
...result.rejected.map(
|
||||
(MutationRejection rejection) => rejection.clientMutationId,
|
||||
),
|
||||
};
|
||||
final Set<String> completedMutationIds = <String>{
|
||||
...result.accepted.map((MutationAck ack) => ack.clientMutationId),
|
||||
...rejectedMutationIds,
|
||||
};
|
||||
final List<ChangeEnvelope> rejectedChanges = current.pendingChanges
|
||||
.where(
|
||||
(ChangeEnvelope change) =>
|
||||
rejectedMutationIds.contains(change.clientMutationId),
|
||||
)
|
||||
.toList(growable: false);
|
||||
final List<ChangeEnvelope> pending = current.pendingChanges
|
||||
.where(
|
||||
(ChangeEnvelope change) =>
|
||||
!completedMutationIds.contains(change.clientMutationId),
|
||||
)
|
||||
.toList(growable: false);
|
||||
final List<ChangeEnvelope> dedupedRejectedChanges = rejectedChanges
|
||||
.where(
|
||||
(ChangeEnvelope change) => !pending.any(
|
||||
(ChangeEnvelope pendingChange) =>
|
||||
pendingChange.clientMutationId == change.clientMutationId,
|
||||
),
|
||||
)
|
||||
.toList(growable: false);
|
||||
|
||||
final String? rejectionMessage = result.rejected.isEmpty
|
||||
? null
|
||||
: 'Push rejected ${result.rejected.length} change(s).';
|
||||
: 'Push rejected ${result.rejected.length} change(s). Fix locally and retry.';
|
||||
|
||||
await _setState(
|
||||
current.copyWith(
|
||||
@@ -93,6 +110,29 @@ class SyncQueueRepository extends AsyncNotifier<SyncState> {
|
||||
lastSyncAt: now,
|
||||
lastError: rejectionMessage,
|
||||
lastRejected: result.rejected,
|
||||
lastRejectedChanges: dedupedRejectedChanges,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Requeues rejected changes to pending sync list for manual retry.
|
||||
Future<void> requeueRejectedChanges() async {
|
||||
final SyncState current = await _currentState();
|
||||
if (current.lastRejectedChanges.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<ChangeEnvelope> pending = <ChangeEnvelope>[
|
||||
...current.lastRejectedChanges,
|
||||
...current.pendingChanges,
|
||||
];
|
||||
final List<ChangeEnvelope> dedupedPending = _dedupeByMutationId(pending);
|
||||
await _setState(
|
||||
current.copyWith(
|
||||
pendingChanges: dedupedPending,
|
||||
lastRejected: const <MutationRejection>[],
|
||||
lastRejectedChanges: const <ChangeEnvelope>[],
|
||||
lastError: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -107,6 +147,7 @@ class SyncQueueRepository extends AsyncNotifier<SyncState> {
|
||||
lastSyncAt: now,
|
||||
lastError: null,
|
||||
lastRejected: const <MutationRejection>[],
|
||||
lastRejectedChanges: const <ChangeEnvelope>[],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -123,6 +164,7 @@ class SyncQueueRepository extends AsyncNotifier<SyncState> {
|
||||
current.copyWith(
|
||||
pendingChanges: const <ChangeEnvelope>[],
|
||||
lastRejected: const <MutationRejection>[],
|
||||
lastRejectedChanges: const <ChangeEnvelope>[],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -138,11 +180,23 @@ class SyncQueueRepository extends AsyncNotifier<SyncState> {
|
||||
await _setState(
|
||||
current.copyWith(
|
||||
lastRejected: const <MutationRejection>[],
|
||||
lastRejectedChanges: const <ChangeEnvelope>[],
|
||||
lastError: lastError,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<ChangeEnvelope> _dedupeByMutationId(List<ChangeEnvelope> changes) {
|
||||
final Set<String> seen = <String>{};
|
||||
final List<ChangeEnvelope> deduped = <ChangeEnvelope>[];
|
||||
for (final ChangeEnvelope change in changes) {
|
||||
if (seen.add(change.clientMutationId)) {
|
||||
deduped.add(change);
|
||||
}
|
||||
}
|
||||
return deduped;
|
||||
}
|
||||
|
||||
Future<SyncState> _currentState() async {
|
||||
final SyncState? value = state.asData?.value;
|
||||
if (value != null) {
|
||||
|
||||
Reference in New Issue
Block a user