Files
social-app/apps/lib/app/services/app_prewarm_orchestrator.dart
T

156 lines
4.5 KiB
Dart

import 'dart:async';
import 'package:flutter/foundation.dart';
import '../../features/calendar/data/repositories/calendar_repository.dart';
import '../../features/messages/data/repositories/inbox_repository.dart';
import '../../core/chat/chat_history_repository.dart';
enum AppPrewarmStatus { idle, running, completed, timedOut, failed }
class AppPrewarmOrchestrator extends ChangeNotifier {
AppPrewarmOrchestrator({
required CalendarRepository calendarRepository,
required InboxRepository inboxRepository,
required ChatHistoryRepository chatHistoryRepository,
this.bootBudget = const Duration(milliseconds: 1200),
Future<void> Function()? prewarmChatHistory,
Future<void> Function()? prewarmCalendarToday,
Future<void> Function()? prewarmCalendarReminderWindow,
Future<void> Function()? prewarmUnreadInbox,
}) : _calendarRepository = calendarRepository,
_inboxRepository = inboxRepository,
_chatHistoryRepository = chatHistoryRepository,
_prewarmChatHistory = prewarmChatHistory,
_prewarmCalendarToday = prewarmCalendarToday,
_prewarmCalendarReminderWindow = prewarmCalendarReminderWindow,
_prewarmUnreadInbox = prewarmUnreadInbox;
final CalendarRepository _calendarRepository;
final InboxRepository _inboxRepository;
final ChatHistoryRepository _chatHistoryRepository;
final Duration bootBudget;
final Future<void> Function()? _prewarmChatHistory;
final Future<void> Function()? _prewarmCalendarToday;
final Future<void> Function()? _prewarmCalendarReminderWindow;
final Future<void> Function()? _prewarmUnreadInbox;
AppPrewarmStatus _status = AppPrewarmStatus.idle;
AppPrewarmStatus get status => _status;
String? _userId;
Future<void>? _running;
int _runToken = 0;
bool get isBootBlocking => _status == AppPrewarmStatus.running;
Future<void> ensureStartedFor(String userId) {
if (_userId == userId &&
(_status == AppPrewarmStatus.completed ||
_status == AppPrewarmStatus.timedOut)) {
return Future<void>.value();
}
if (_userId == userId && _running != null) {
return _running!;
}
_userId = userId;
final runToken = ++_runToken;
_status = AppPrewarmStatus.running;
notifyListeners();
final tasks = Future.wait<void>([
_runPrewarmChatHistory(),
_runPrewarmCalendarToday(),
_runPrewarmCalendarReminderWindow(),
_runPrewarmUnreadInbox(),
]);
final running = _runWithBudget(tasks, userId: userId, runToken: runToken);
_running = running;
return running.whenComplete(() {
if (identical(_running, running)) {
_running = null;
}
});
}
Future<void> _runPrewarmChatHistory() {
final override = _prewarmChatHistory;
if (override != null) {
return override();
}
return _chatHistoryRepository.loadHistory();
}
Future<void> _runPrewarmCalendarToday() {
final override = _prewarmCalendarToday;
if (override != null) {
return override();
}
return _calendarRepository.getDayEvents(DateTime.now());
}
Future<void> _runPrewarmUnreadInbox() {
final override = _prewarmUnreadInbox;
if (override != null) {
return override();
}
return _inboxRepository.getMessages(isRead: false);
}
Future<void> _runPrewarmCalendarReminderWindow() {
final override = _prewarmCalendarReminderWindow;
if (override != null) {
return override();
}
final now = DateTime.now();
final start = DateTime(
now.year,
now.month,
now.day,
).subtract(const Duration(days: 1));
final end = start.add(const Duration(days: 90));
return _calendarRepository.listByRange(startAt: start, endAt: end);
}
Future<void> _runWithBudget(
Future<void> tasks, {
required String userId,
required int runToken,
}) async {
bool isLatestRun() => _runToken == runToken && _userId == userId;
try {
await tasks.timeout(bootBudget);
if (!isLatestRun()) {
return;
}
_status = AppPrewarmStatus.completed;
notifyListeners();
} on TimeoutException {
if (!isLatestRun()) {
return;
}
_status = AppPrewarmStatus.timedOut;
notifyListeners();
} catch (_) {
if (!isLatestRun()) {
return;
}
_status = AppPrewarmStatus.failed;
notifyListeners();
}
}
void reset() {
_userId = null;
_running = null;
_runToken += 1;
if (_status != AppPrewarmStatus.idle) {
_status = AppPrewarmStatus.idle;
notifyListeners();
}
}
}