85 lines
2.4 KiB
Text
85 lines
2.4 KiB
Text
import 'dart:convert';
|
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
class RemoteStateStore {
|
|
static const _storage = FlutterSecureStorage(
|
|
aOptions: AndroidOptions(
|
|
encryptedSharedPreferences: true,
|
|
resetOnError: true,
|
|
),
|
|
);
|
|
|
|
static const _kWsSessionId = 'ws_session_id';
|
|
static const _kProcessedRing = 'ws_processed_events_ring';
|
|
static const _kLastSyncIso = 'remote_last_sync_iso';
|
|
|
|
Future<String> getOrCreateSessionId() async {
|
|
var id = await _storage.read(key: _kWsSessionId);
|
|
if (id == null || id.isEmpty) {
|
|
id = const Uuid().v4();
|
|
await _storage.write(key: _kWsSessionId, value: id);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
Future<void> clearSessionId() => _storage.delete(key: _kWsSessionId);
|
|
|
|
Future<String?> getLastSyncIso() => _storage.read(key: _kLastSyncIso);
|
|
Future<void> setLastSyncIso(String iso) => _storage.write(key: _kLastSyncIso, value: iso);
|
|
|
|
Future<List<String>> loadProcessedRing({int maxSize = 2000}) async {
|
|
final raw = await _storage.read(key: _kProcessedRing);
|
|
if (raw == null || raw.isEmpty) return <String>[];
|
|
try {
|
|
final v = jsonDecode(raw);
|
|
if (v is List) {
|
|
final list = v.whereType<String>().toList();
|
|
return list.length > maxSize ? list.sublist(list.length - maxSize) : list;
|
|
}
|
|
} catch (_) {}
|
|
return <String>[];
|
|
}
|
|
|
|
Future<void> saveProcessedRing(List<String> ring, {int maxSize = 2000}) async {
|
|
final trimmed = ring.length > maxSize ? ring.sublist(ring.length - maxSize) : ring;
|
|
await _storage.write(key: _kProcessedRing, value: jsonEncode(trimmed));
|
|
}
|
|
}
|
|
|
|
class EventRingDeduper {
|
|
EventRingDeduper(this.store, {this.maxSize = 2000});
|
|
|
|
final RemoteStateStore store;
|
|
final int maxSize;
|
|
|
|
final List<String> _ring = [];
|
|
final Set<String> _set = {};
|
|
|
|
Future<void> init() async {
|
|
final saved = await store.loadProcessedRing(maxSize: maxSize);
|
|
_ring
|
|
..clear()
|
|
..addAll(saved);
|
|
_set
|
|
..clear()
|
|
..addAll(saved);
|
|
}
|
|
|
|
bool has(String? id) => id != null && id.isNotEmpty && _set.contains(id);
|
|
|
|
Future<void> mark(String? id) async {
|
|
if (id == null || id.isEmpty) return;
|
|
if (_set.contains(id)) return;
|
|
|
|
_ring.add(id);
|
|
_set.add(id);
|
|
|
|
while (_ring.length > maxSize) {
|
|
final old = _ring.removeAt(0);
|
|
_set.remove(old);
|
|
}
|
|
|
|
await store.saveProcessedRing(_ring, maxSize: maxSize);
|
|
}
|
|
}
|