145 lines
4.5 KiB
Dart
145 lines
4.5 KiB
Dart
import 'dart:async';
|
|
|
|
import '../../../../core/cache/cache_entry.dart';
|
|
import '../../../../core/cache/cache_policy.dart';
|
|
import '../../../../core/cache/hybrid_cache_store.dart';
|
|
import '../models/schedule_item_model.dart';
|
|
|
|
class CalendarRepository {
|
|
final HybridCacheStore store;
|
|
final CachePolicy policy;
|
|
final DateTime Function() now;
|
|
final Future<List<ScheduleItemModel>> Function(DateTime date)
|
|
loadDayFromRemote;
|
|
final Future<List<ScheduleItemModel>> Function(DateTime start, DateTime end)
|
|
loadMonthFromRemote;
|
|
|
|
final Map<String, Future<void>> _refreshInFlight = <String, Future<void>>{};
|
|
|
|
CalendarRepository({
|
|
required this.store,
|
|
required this.loadDayFromRemote,
|
|
required this.loadMonthFromRemote,
|
|
CachePolicy? policy,
|
|
DateTime Function()? now,
|
|
}) : policy =
|
|
policy ??
|
|
const CachePolicy(
|
|
softTtl: Duration(minutes: 2),
|
|
hardTtl: Duration(minutes: 30),
|
|
minRefreshInterval: Duration(minutes: 1),
|
|
),
|
|
now = now ?? DateTime.now;
|
|
|
|
static String dayKey(DateTime date) {
|
|
final day =
|
|
'${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
|
return 'calendar:day:$day';
|
|
}
|
|
|
|
static String monthKey(DateTime date) {
|
|
return 'calendar:month:${date.year}-${date.month.toString().padLeft(2, '0')}';
|
|
}
|
|
|
|
Future<List<ScheduleItemModel>> getDayEvents(
|
|
DateTime date, {
|
|
bool forceRefresh = false,
|
|
}) async {
|
|
final key = dayKey(date);
|
|
if (forceRefresh) {
|
|
return _refreshDayAndRead(date, key);
|
|
}
|
|
|
|
final cached = await store.read<CacheEntry<List<ScheduleItemModel>>>(key);
|
|
if (cached == null) {
|
|
return _refreshDayAndRead(date, key);
|
|
}
|
|
|
|
final decision = policy.evaluate(now: now(), fetchedAt: cached.fetchedAt);
|
|
if (decision.shouldRefreshInBackground) {
|
|
_refreshDayInBackground(date, key);
|
|
}
|
|
if (decision.mustBlockForNetwork || !decision.canUseCached) {
|
|
return _refreshDayAndRead(date, key);
|
|
}
|
|
return cached.value;
|
|
}
|
|
|
|
Future<List<ScheduleItemModel>> getMonthEvents(
|
|
DateTime monthStart, {
|
|
bool forceRefresh = false,
|
|
}) async {
|
|
final key = monthKey(monthStart);
|
|
if (forceRefresh) {
|
|
return _refreshMonthAndRead(monthStart, key);
|
|
}
|
|
final cached = await store.read<CacheEntry<List<ScheduleItemModel>>>(key);
|
|
if (cached == null) {
|
|
return _refreshMonthAndRead(monthStart, key);
|
|
}
|
|
final decision = policy.evaluate(now: now(), fetchedAt: cached.fetchedAt);
|
|
if (decision.shouldRefreshInBackground) {
|
|
_refreshMonthInBackground(monthStart, key);
|
|
}
|
|
if (decision.mustBlockForNetwork || !decision.canUseCached) {
|
|
return _refreshMonthAndRead(monthStart, key);
|
|
}
|
|
return cached.value;
|
|
}
|
|
|
|
Future<List<ScheduleItemModel>> _refreshDayAndRead(
|
|
DateTime date,
|
|
String key,
|
|
) async {
|
|
await _refreshDay(date, key);
|
|
final cached = await store.read<CacheEntry<List<ScheduleItemModel>>>(key);
|
|
return cached?.value ?? const <ScheduleItemModel>[];
|
|
}
|
|
|
|
Future<List<ScheduleItemModel>> _refreshMonthAndRead(
|
|
DateTime monthStart,
|
|
String key,
|
|
) async {
|
|
await _refreshMonth(monthStart, key);
|
|
final cached = await store.read<CacheEntry<List<ScheduleItemModel>>>(key);
|
|
return cached?.value ?? const <ScheduleItemModel>[];
|
|
}
|
|
|
|
Future<void> _refreshDay(DateTime date, String key) async {
|
|
final remote = await loadDayFromRemote(date);
|
|
await store.write<CacheEntry<List<ScheduleItemModel>>>(
|
|
key,
|
|
CacheEntry<List<ScheduleItemModel>>(value: remote, fetchedAt: now()),
|
|
);
|
|
}
|
|
|
|
Future<void> _refreshMonth(DateTime monthStart, String key) async {
|
|
final start = DateTime(monthStart.year, monthStart.month, 1);
|
|
final end = DateTime(monthStart.year, monthStart.month + 1, 0, 23, 59, 59);
|
|
final remote = await loadMonthFromRemote(start, end);
|
|
await store.write<CacheEntry<List<ScheduleItemModel>>>(
|
|
key,
|
|
CacheEntry<List<ScheduleItemModel>>(value: remote, fetchedAt: now()),
|
|
);
|
|
}
|
|
|
|
void _refreshDayInBackground(DateTime date, String key) {
|
|
_refreshInBackground(key, () => _refreshDay(date, key));
|
|
}
|
|
|
|
void _refreshMonthInBackground(DateTime monthStart, String key) {
|
|
_refreshInBackground(key, () => _refreshMonth(monthStart, key));
|
|
}
|
|
|
|
void _refreshInBackground(String key, Future<void> Function() taskFactory) {
|
|
if (_refreshInFlight.containsKey(key)) {
|
|
return;
|
|
}
|
|
final task = taskFactory().whenComplete(() {
|
|
_refreshInFlight.remove(key);
|
|
});
|
|
_refreshInFlight[key] = task;
|
|
unawaited(task);
|
|
}
|
|
}
|