didvan-app/lib/services/story_read_cache.dart

58 lines
1.6 KiB
Dart

import 'dart:convert';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
/// Local persistence of story content-uuids the user has already viewed.
/// Backend's `isRead` may lag or reset on next fetch; this cache guarantees
/// the ring turns grey right after viewing and stays grey across refetches.
class StoryReadCache {
StoryReadCache._();
static final StoryReadCache instance = StoryReadCache._();
static const String _key = 'story_read_uuids';
static const int _maxEntries = 500;
static const FlutterSecureStorage _storage = FlutterSecureStorage();
final Set<String> _memory = <String>{};
bool _hydrated = false;
Future<void> hydrate() async {
if (_hydrated) return;
try {
final raw = await _storage.read(key: _key);
if (raw != null && raw.isNotEmpty) {
final decoded = jsonDecode(raw);
if (decoded is List) {
_memory.addAll(decoded.whereType<String>());
}
}
} catch (_) {}
_hydrated = true;
}
bool contains(String? uuid) {
if (uuid == null || uuid.isEmpty) return false;
return _memory.contains(uuid);
}
Future<void> add(String? uuid) async {
if (uuid == null || uuid.isEmpty) return;
if (!_memory.add(uuid)) return;
if (_memory.length > _maxEntries) {
final excess = _memory.length - _maxEntries;
final toRemove = _memory.take(excess).toList();
_memory.removeAll(toRemove);
}
try {
await _storage.write(key: _key, value: jsonEncode(_memory.toList()));
} catch (_) {}
}
Future<void> clear() async {
_memory.clear();
try {
await _storage.delete(key: _key);
} catch (_) {}
}
}