feat: add todo cache repository and precise invalidation

This commit is contained in:
qzl
2026-03-20 15:37:59 +08:00
parent 8883248968
commit e64b9c670c
6 changed files with 155 additions and 10 deletions
@@ -17,6 +17,10 @@ class TodoApi {
return data.map((json) => TodoResponse.fromJson(json)).toList();
}
Future<List<TodoResponse>> getPendingTodos() {
return getTodos(status: 'pending');
}
Future<TodoResponse> getTodo(String id) async {
final response = await _client.get('$_prefix/$id');
return TodoResponse.fromJson(response.data);
@@ -0,0 +1,79 @@
import 'dart:async';
import '../../../core/cache/cache_entry.dart';
import '../../../core/cache/cache_invalidator.dart';
import '../../../core/cache/hybrid_cache_store.dart';
import 'todo_api.dart';
class TodoRepository {
static const String pendingListKey = 'todo:list:pending';
final TodoApi api;
final HybridCacheStore store;
final CacheInvalidator invalidator;
final DateTime Function() now;
TodoRepository({
required this.api,
required this.store,
required this.invalidator,
DateTime Function()? now,
}) : now = now ?? DateTime.now;
Future<List<TodoResponse>> getPendingTodos({
bool forceRefresh = false,
}) async {
if (!forceRefresh) {
final cached = await store.read<CacheEntry<List<TodoResponse>>>(
pendingListKey,
);
if (cached != null) {
return cached.value;
}
}
final remote = await api.getPendingTodos();
await store.write<CacheEntry<List<TodoResponse>>>(
pendingListKey,
CacheEntry(value: remote, fetchedAt: now()),
);
return remote;
}
Future<void> completeTodo(String id) async {
final cached = await store.read<CacheEntry<List<TodoResponse>>>(
pendingListKey,
);
if (cached != null) {
final next = cached.value
.map(
(todo) => todo.id == id
? todo.copyWith(status: 'completed', completedAt: now())
: todo,
)
.toList(growable: false);
await store.write<CacheEntry<List<TodoResponse>>>(
pendingListKey,
CacheEntry(value: next, fetchedAt: now()),
);
}
invalidator.invalidate(pendingListKey);
try {
await api.completeTodo(id);
} catch (error) {
if (cached != null) {
await store.write<CacheEntry<List<TodoResponse>>>(
pendingListKey,
cached,
);
}
rethrow;
}
}
Future<void> invalidatePending() {
invalidator.invalidate(pendingListKey);
return Future<void>.value();
}
}