feat: add todo cache repository and precise invalidation
This commit is contained in:
+1
-8
@@ -1,19 +1,12 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'hybrid_cache_store.dart';
|
||||
|
||||
class CacheInvalidator {
|
||||
final HybridCacheStore? _store;
|
||||
final Set<String> _invalidated = <String>{};
|
||||
|
||||
CacheInvalidator({HybridCacheStore? store}) : _store = store;
|
||||
CacheInvalidator({HybridCacheStore? store});
|
||||
|
||||
void invalidate(String key) {
|
||||
_invalidated.add(key);
|
||||
final removeFuture = _store?.remove(key);
|
||||
if (removeFuture != null) {
|
||||
unawaited(removeFuture);
|
||||
}
|
||||
}
|
||||
|
||||
void invalidateCalendarDay(DateTime date) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import '../../features/settings/data/services/settings_user_cache.dart';
|
||||
import '../../features/settings/data/services/user_profile_cache_repository.dart';
|
||||
import '../../features/users/data/users_api.dart';
|
||||
import '../../features/todo/data/todo_api.dart';
|
||||
import '../../features/todo/data/todo_repository.dart';
|
||||
|
||||
final sl = GetIt.instance;
|
||||
|
||||
@@ -124,6 +125,13 @@ Future<void> configureDependencies() async {
|
||||
|
||||
final todoApi = TodoApi(apiClient);
|
||||
sl.registerSingleton<TodoApi>(todoApi);
|
||||
sl.registerSingleton<TodoRepository>(
|
||||
TodoRepository(
|
||||
api: todoApi,
|
||||
store: hybridCacheStore,
|
||||
invalidator: sl<CacheInvalidator>(),
|
||||
),
|
||||
);
|
||||
|
||||
final authRepository = AuthRepositoryImpl(
|
||||
api: authApi,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
import '../../../calendar/ui/calendar_state_manager.dart';
|
||||
import '../../../calendar/ui/widgets/bottom_dock.dart';
|
||||
import '../../data/todo_api.dart';
|
||||
import '../../data/todo_repository.dart';
|
||||
|
||||
class TodoQuadrantsScreen extends StatefulWidget {
|
||||
const TodoQuadrantsScreen({super.key});
|
||||
@@ -26,6 +27,7 @@ class TodoQuadrantsScreen extends StatefulWidget {
|
||||
|
||||
class _TodoQuadrantsScreenState extends State<TodoQuadrantsScreen> {
|
||||
final TodoApi _todoApi = sl<TodoApi>();
|
||||
final TodoRepository _todoRepository = sl<TodoRepository>();
|
||||
|
||||
List<TodoResponse> _todos = [];
|
||||
bool _isLoading = true;
|
||||
@@ -210,7 +212,9 @@ class _TodoQuadrantsScreenState extends State<TodoQuadrantsScreen> {
|
||||
});
|
||||
|
||||
try {
|
||||
final todos = await _todoApi.getTodos(status: 'pending');
|
||||
final todos = await _todoRepository.getPendingTodos(
|
||||
forceRefresh: !showPageLoader,
|
||||
);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
@@ -263,7 +267,7 @@ class _TodoQuadrantsScreenState extends State<TodoQuadrantsScreen> {
|
||||
|
||||
Future<void> _completeTodo(TodoResponse todo) async {
|
||||
try {
|
||||
await _todoApi.completeTodo(todo.id);
|
||||
await _todoRepository.completeTodo(todo.id);
|
||||
if (mounted) {
|
||||
Toast.show(context, '已完成', type: ToastType.success);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user