99 lines
3.1 KiB
Dart
99 lines
3.1 KiB
Dart
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();
|
|
}
|
|
}
|