Add sync queue orchestration and local mutation envelopes
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:relationship_saver/integrations/backend/models/backend_models.dart';
|
||||
|
||||
/// Persisted state for sync queue and cursor progression.
|
||||
@immutable
|
||||
class SyncState {
|
||||
const SyncState({
|
||||
this.cursor,
|
||||
required this.pendingChanges,
|
||||
this.lastSyncAt,
|
||||
this.lastAttemptAt,
|
||||
this.lastError,
|
||||
this.lastRejected = const <MutationRejection>[],
|
||||
});
|
||||
|
||||
factory SyncState.fromJson(Map<String, dynamic> json) {
|
||||
return SyncState(
|
||||
cursor: json['cursor'] as String?,
|
||||
pendingChanges: (json['pendingChanges'] as List<dynamic>? ?? <dynamic>[])
|
||||
.map(
|
||||
(dynamic item) =>
|
||||
ChangeEnvelope.fromJson(item as Map<String, dynamic>),
|
||||
)
|
||||
.toList(growable: false),
|
||||
lastSyncAt: _tryParseDate(json['lastSyncAt'] as String?),
|
||||
lastAttemptAt: _tryParseDate(json['lastAttemptAt'] as String?),
|
||||
lastError: json['lastError'] as String?,
|
||||
lastRejected: (json['lastRejected'] as List<dynamic>? ?? <dynamic>[])
|
||||
.map(
|
||||
(dynamic item) =>
|
||||
MutationRejection.fromJson(item as Map<String, dynamic>),
|
||||
)
|
||||
.toList(growable: false),
|
||||
);
|
||||
}
|
||||
|
||||
final String? cursor;
|
||||
final List<ChangeEnvelope> pendingChanges;
|
||||
final DateTime? lastSyncAt;
|
||||
final DateTime? lastAttemptAt;
|
||||
final String? lastError;
|
||||
final List<MutationRejection> lastRejected;
|
||||
|
||||
static const Object _unset = Object();
|
||||
|
||||
static const SyncState empty = SyncState(
|
||||
pendingChanges: <ChangeEnvelope>[],
|
||||
lastRejected: <MutationRejection>[],
|
||||
);
|
||||
|
||||
SyncState copyWith({
|
||||
Object? cursor = _unset,
|
||||
List<ChangeEnvelope>? pendingChanges,
|
||||
Object? lastSyncAt = _unset,
|
||||
Object? lastAttemptAt = _unset,
|
||||
Object? lastError = _unset,
|
||||
List<MutationRejection>? lastRejected,
|
||||
}) {
|
||||
return SyncState(
|
||||
cursor: identical(cursor, _unset) ? this.cursor : cursor as String?,
|
||||
pendingChanges: pendingChanges ?? this.pendingChanges,
|
||||
lastSyncAt: identical(lastSyncAt, _unset)
|
||||
? this.lastSyncAt
|
||||
: lastSyncAt as DateTime?,
|
||||
lastAttemptAt: identical(lastAttemptAt, _unset)
|
||||
? this.lastAttemptAt
|
||||
: lastAttemptAt as DateTime?,
|
||||
lastError: identical(lastError, _unset)
|
||||
? this.lastError
|
||||
: lastError as String?,
|
||||
lastRejected: lastRejected ?? this.lastRejected,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{
|
||||
'cursor': cursor,
|
||||
'pendingChanges': pendingChanges
|
||||
.map((ChangeEnvelope change) => change.toJson())
|
||||
.toList(growable: false),
|
||||
'lastSyncAt': lastSyncAt?.toUtc().toIso8601String(),
|
||||
'lastAttemptAt': lastAttemptAt?.toUtc().toIso8601String(),
|
||||
'lastError': lastError,
|
||||
'lastRejected': lastRejected
|
||||
.map((MutationRejection rejection) => rejection.toJson())
|
||||
.toList(growable: false),
|
||||
};
|
||||
}
|
||||
|
||||
static DateTime? _tryParseDate(String? raw) {
|
||||
if (raw == null || raw.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final DateTime? parsed = DateTime.tryParse(raw);
|
||||
return parsed?.toLocal();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user