153 lines
4.9 KiB
Dart
153 lines
4.9 KiB
Dart
import '../../../../data/network/i_api_client.dart';
|
|
import '../../../../data/cache/cache_store.dart';
|
|
import '../../../../core/notification/models/reminder_alarm.dart';
|
|
import '../../../../core/notification/services/reminder_reconcile_service.dart';
|
|
import '../models/schedule_item_model.dart';
|
|
|
|
class CalendarService {
|
|
static const _prefix = '/api/v1/schedule-items';
|
|
|
|
final IApiClient _apiClient;
|
|
final CacheInvalidator _invalidator;
|
|
final ReminderReconcileService? _reminderReconcileService;
|
|
|
|
CalendarService({
|
|
required IApiClient apiClient,
|
|
required CacheInvalidator invalidator,
|
|
ReminderReconcileService? reminderReconcileService,
|
|
}) : _apiClient = apiClient,
|
|
_invalidator = invalidator,
|
|
_reminderReconcileService = reminderReconcileService;
|
|
|
|
Future<List<ScheduleItemModel>> getEventsForDay(DateTime date) async {
|
|
final start = DateTime(date.year, date.month, date.day);
|
|
final end = DateTime(date.year, date.month, date.day, 23, 59, 59);
|
|
return getEventsForRange(start, end);
|
|
}
|
|
|
|
Future<List<ScheduleItemModel>> getEventsForRange(
|
|
DateTime start,
|
|
DateTime end,
|
|
) async {
|
|
final startParam = Uri.encodeQueryComponent(
|
|
start.toUtc().toIso8601String(),
|
|
);
|
|
final endParam = Uri.encodeQueryComponent(end.toUtc().toIso8601String());
|
|
final response = await _apiClient.get<List<dynamic>>(
|
|
'$_prefix?start_at=$startParam&end_at=$endParam',
|
|
);
|
|
final data = response.data;
|
|
if (data == null) {
|
|
throw StateError('Invalid getEventsForRange response: empty payload');
|
|
}
|
|
final events = data
|
|
.map((item) => item as Map<String, dynamic>)
|
|
.map(ScheduleItemModel.fromJson)
|
|
.toList(growable: false);
|
|
await _reminderReconcileService?.reconcileEvents(
|
|
events.map(_toReminderSnapshot).toList(growable: false),
|
|
);
|
|
return events;
|
|
}
|
|
|
|
Future<ScheduleItemModel> getEventById(
|
|
String id, {
|
|
bool reconcileReminder = true,
|
|
}) async {
|
|
final response = await _apiClient.get<Map<String, dynamic>>('$_prefix/$id');
|
|
final data = response.data;
|
|
if (data == null) {
|
|
throw StateError('Invalid getEventById response: empty payload');
|
|
}
|
|
final event = ScheduleItemModel.fromJson(data);
|
|
if (reconcileReminder) {
|
|
await _reminderReconcileService?.reconcileEvent(
|
|
_toReminderSnapshot(event),
|
|
);
|
|
}
|
|
return event;
|
|
}
|
|
|
|
Future<ScheduleItemModel> addEvent(ScheduleItemModel event) async {
|
|
final response = await _apiClient.post<Map<String, dynamic>>(
|
|
_prefix,
|
|
data: event.toCreateJson(),
|
|
);
|
|
final data = response.data;
|
|
if (data == null) {
|
|
throw StateError('Invalid addEvent response: empty payload');
|
|
}
|
|
final created = ScheduleItemModel.fromJson(data);
|
|
_invalidateEventCache(created);
|
|
await _reminderReconcileService?.reconcileEvent(
|
|
_toReminderSnapshot(created),
|
|
);
|
|
return created;
|
|
}
|
|
|
|
Future<ScheduleItemModel> updateEvent(ScheduleItemModel event) async {
|
|
final response = await _apiClient.patch<Map<String, dynamic>>(
|
|
'$_prefix/${event.id}',
|
|
data: event.toUpdateJson(),
|
|
);
|
|
final data = response.data;
|
|
if (data == null) {
|
|
throw StateError('Invalid updateEvent response: empty payload');
|
|
}
|
|
final updated = ScheduleItemModel.fromJson(data);
|
|
_invalidateEventCache(updated);
|
|
await _reminderReconcileService?.reconcileEvent(
|
|
_toReminderSnapshot(updated),
|
|
);
|
|
return updated;
|
|
}
|
|
|
|
Future<ScheduleItemModel> archiveEvent(String id) async {
|
|
final event = await getEventById(id);
|
|
final updatedEvent = await updateEvent(
|
|
event.copyWith(status: ScheduleStatus.archived),
|
|
);
|
|
_invalidateEventCache(updatedEvent);
|
|
await _reminderReconcileService?.archiveAndCancel(id);
|
|
return updatedEvent;
|
|
}
|
|
|
|
Future<void> deleteEvent(String id) async {
|
|
final event = await getEventById(id);
|
|
_invalidateEventCache(event);
|
|
await _apiClient.delete<void>('$_prefix/$id');
|
|
await _reminderReconcileService?.archiveAndCancel(id);
|
|
}
|
|
|
|
void _invalidateEventCache(ScheduleItemModel event) {
|
|
var current = DateTime(
|
|
event.startAt.year,
|
|
event.startAt.month,
|
|
event.startAt.day,
|
|
);
|
|
final end = DateTime(
|
|
event.endAt?.year ?? event.startAt.year,
|
|
event.endAt?.month ?? event.startAt.month,
|
|
event.endAt?.day ?? event.startAt.day,
|
|
);
|
|
while (!current.isAfter(end)) {
|
|
_invalidator.invalidateCalendarDay(current);
|
|
current = current.add(const Duration(days: 1));
|
|
}
|
|
}
|
|
|
|
ReminderEventSnapshot _toReminderSnapshot(ScheduleItemModel event) {
|
|
return ReminderEventSnapshot(
|
|
eventId: event.id,
|
|
title: event.title,
|
|
startAt: event.startAt,
|
|
endAt: event.endAt,
|
|
timezone: event.timezone,
|
|
reminderMinutes: event.metadata?.reminderMinutes,
|
|
location: event.metadata?.location,
|
|
notes: event.metadata?.notes,
|
|
isArchived: event.status == ScheduleStatus.archived,
|
|
);
|
|
}
|
|
}
|