Add rejected mutation requeue flow in sync UI

This commit is contained in:
Rijad Zuzo
2026-02-15 23:06:48 +01:00
parent 831c217e29
commit c4b86c18da
5 changed files with 205 additions and 7 deletions
+56 -2
View File
@@ -172,6 +172,11 @@ class _SyncViewState extends ConsumerState<SyncView> {
'Rejected Changes',
style: Theme.of(context).textTheme.titleMedium,
),
TextButton.icon(
onPressed: _busy ? null : _requeueRejected,
icon: const Icon(Icons.replay_rounded),
label: const Text('Requeue'),
),
TextButton.icon(
onPressed: _busy ? null : _dismissRejections,
icon: const Icon(Icons.done_all_rounded),
@@ -183,7 +188,13 @@ class _SyncViewState extends ConsumerState<SyncView> {
...state.lastRejected.map(
(MutationRejection rejection) => Padding(
padding: const EdgeInsets.only(bottom: 10),
child: _rejectionTile(rejection),
child: _rejectionTile(
rejection,
matched: _findRejectedChange(
state.lastRejectedChanges,
rejection.clientMutationId,
),
),
),
),
],
@@ -244,7 +255,10 @@ class _SyncViewState extends ConsumerState<SyncView> {
);
}
Widget _rejectionTile(MutationRejection rejection) {
Widget _rejectionTile(
MutationRejection rejection, {
required ChangeEnvelope? matched,
}) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
@@ -279,6 +293,13 @@ class _SyncViewState extends ConsumerState<SyncView> {
context,
).textTheme.bodySmall?.copyWith(color: AppTheme.textSecondary),
),
if (matched != null)
Text(
'${matched.entityType}:${matched.entityId}',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: AppTheme.textSecondary,
),
),
],
),
const SizedBox(height: 6),
@@ -291,6 +312,18 @@ class _SyncViewState extends ConsumerState<SyncView> {
);
}
ChangeEnvelope? _findRejectedChange(
List<ChangeEnvelope> changes,
String clientMutationId,
) {
for (final ChangeEnvelope change in changes) {
if (change.clientMutationId == clientMutationId) {
return change;
}
}
return null;
}
String _formatDateTime(DateTime? value) {
if (value == null) {
return 'never';
@@ -354,6 +387,27 @@ class _SyncViewState extends ConsumerState<SyncView> {
}
}
Future<void> _requeueRejected() async {
setState(() {
_busy = true;
});
try {
await ref
.read(syncQueueRepositoryProvider.notifier)
.requeueRejectedChanges();
setState(() {
_status = 'Requeued rejected changes. Sync again after local fixes.';
});
} finally {
if (mounted) {
setState(() {
_busy = false;
});
}
}
}
Future<void> _run(
Future<SyncRunResult> Function(SyncCoordinator coordinator) action,
) async {