From f4c07287bc387d4d8d141a9b9f52434ff12236d5 Mon Sep 17 00:00:00 2001 From: qzl Date: Fri, 20 Mar 2026 15:39:44 +0800 Subject: [PATCH] feat: add app lifecycle refresh coordinator --- .../core/cache/cache_refresh_coordinator.dart | 29 +++++++++++++++++++ apps/lib/main.dart | 24 +++++++++++++++ .../cache/cache_refresh_coordinator_test.dart | 27 +++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 apps/lib/core/cache/cache_refresh_coordinator.dart create mode 100644 apps/test/core/cache/cache_refresh_coordinator_test.dart diff --git a/apps/lib/core/cache/cache_refresh_coordinator.dart b/apps/lib/core/cache/cache_refresh_coordinator.dart new file mode 100644 index 0000000..97c781d --- /dev/null +++ b/apps/lib/core/cache/cache_refresh_coordinator.dart @@ -0,0 +1,29 @@ +import 'package:flutter/widgets.dart'; + +class CacheRefreshCoordinator with WidgetsBindingObserver { + final Duration minInterval; + final void Function() onRefresh; + final DateTime Function() now; + + DateTime? _lastRefreshedAt; + + CacheRefreshCoordinator({ + required this.minInterval, + required this.onRefresh, + DateTime Function()? now, + }) : now = now ?? DateTime.now; + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state != AppLifecycleState.resumed) { + return; + } + final current = now(); + final last = _lastRefreshedAt; + if (last != null && current.difference(last) < minInterval) { + return; + } + _lastRefreshedAt = current; + onRefresh(); + } +} diff --git a/apps/lib/main.dart b/apps/lib/main.dart index 2636733..f165ece 100644 --- a/apps/lib/main.dart +++ b/apps/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'core/constants/app_constants.dart'; +import 'core/cache/cache_refresh_coordinator.dart'; import 'core/di/injection.dart'; import 'core/notifications/local_notification_service.dart'; import 'core/notifications/reminder_notification_callbacks.dart'; @@ -14,9 +15,13 @@ import 'features/auth/presentation/bloc/auth_bloc.dart'; import 'features/auth/presentation/bloc/auth_event.dart'; import 'features/auth/presentation/bloc/auth_state.dart'; import 'features/calendar/data/services/calendar_service.dart'; +import 'features/calendar/data/services/calendar_repository.dart'; import 'features/calendar/reminders/reminder_action_executor.dart'; import 'features/calendar/reminders/ui/reminder_foreground_presenter.dart'; +import 'features/calendar/ui/calendar_state_manager.dart'; import 'features/chat/presentation/bloc/chat_bloc.dart'; +import 'features/settings/data/services/settings_user_cache.dart'; +import 'features/todo/data/todo_repository.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -44,6 +49,25 @@ void main() async { final authBloc = sl(); authBloc.add(AuthStarted()); + final cacheRefreshCoordinator = CacheRefreshCoordinator( + minInterval: const Duration(minutes: 5), + onRefresh: () { + final selected = sl().selectedDate; + unawaited( + sl().getDayEvents(selected, forceRefresh: true), + ); + unawaited( + sl().getMonthEvents( + DateTime(selected.year, selected.month, 1), + forceRefresh: true, + ), + ); + unawaited(sl().getPendingTodos(forceRefresh: true)); + unawaited(sl().getProfile(forceRefresh: true)); + }, + ); + WidgetsBinding.instance.addObserver(cacheRefreshCoordinator); + runApp( LinksyApp( authBloc: authBloc, diff --git a/apps/test/core/cache/cache_refresh_coordinator_test.dart b/apps/test/core/cache/cache_refresh_coordinator_test.dart new file mode 100644 index 0000000..fcd361c --- /dev/null +++ b/apps/test/core/cache/cache_refresh_coordinator_test.dart @@ -0,0 +1,27 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:social_app/core/cache/cache_refresh_coordinator.dart'; + +void main() { + test('resume should trigger refresh only when min interval elapsed', () { + var calls = 0; + var now = DateTime(2026, 3, 20, 10, 0); + + final coordinator = CacheRefreshCoordinator( + minInterval: const Duration(minutes: 5), + onRefresh: () => calls += 1, + now: () => now, + ); + + coordinator.didChangeAppLifecycleState(AppLifecycleState.resumed); + expect(calls, 1); + + now = DateTime(2026, 3, 20, 10, 3); + coordinator.didChangeAppLifecycleState(AppLifecycleState.resumed); + expect(calls, 1); + + now = DateTime(2026, 3, 20, 10, 6); + coordinator.didChangeAppLifecycleState(AppLifecycleState.resumed); + expect(calls, 2); + }); +}