migrated custom timetable events to Marianum-Connect and refactored push notification handling
This commit is contained in:
@@ -63,6 +63,10 @@ class TalkSection extends StatelessWidget {
|
||||
Haptics.selection();
|
||||
final enabled = e ?? false;
|
||||
settings.val(write: true).notificationSettings.enabled = enabled;
|
||||
// Turning off does NOT unregister: the device stays subscribed so
|
||||
// silent sync pushes keep arriving; the message handler and iOS
|
||||
// NSE suppress only the visible notification (via the mirrored
|
||||
// flag). Enabling (re-)registers and ensures the OS permission.
|
||||
if (enabled) {
|
||||
final messenger = ScaffoldMessenger.of(context);
|
||||
unawaited(() async {
|
||||
@@ -82,8 +86,6 @@ class TalkSection extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}());
|
||||
} else {
|
||||
unawaited(PushRegistration().unregister());
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@@ -57,6 +57,10 @@ class _CustomEventEditDialogState extends State<CustomEventEditDialog> {
|
||||
text: widget.existingEvent?.description ?? widget.initialDescription,
|
||||
);
|
||||
late String _rrule = widget.existingEvent?.rrule ?? '';
|
||||
// Für neue Termine standardmäßig an, bestehende behalten ihren Wert. Migrierte
|
||||
// mhsl-Termine tragen ausgeschaltet (Modell-Default), da sie nie ein Serien-
|
||||
// Ferien-Flag hatten.
|
||||
late bool _skipHolidays = widget.existingEvent?.skipHolidays ?? true;
|
||||
late CustomTimetableColors _color = CustomTimetableColors.values.firstWhere(
|
||||
(e) => e.name == widget.existingEvent?.color,
|
||||
orElse: () => TimetableColors.defaultColor,
|
||||
@@ -150,6 +154,7 @@ class _CustomEventEditDialogState extends State<CustomEventEditDialog> {
|
||||
endDate: endDate,
|
||||
color: _color.name,
|
||||
rrule: _rrule,
|
||||
skipHolidays: _rrule.trim().isEmpty ? false : _skipHolidays,
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
);
|
||||
@@ -297,6 +302,18 @@ class _CustomEventEditDialogState extends State<CustomEventEditDialog> {
|
||||
},
|
||||
),
|
||||
),
|
||||
if (_rrule.trim().isNotEmpty) ...[
|
||||
const Divider(),
|
||||
SwitchListTile(
|
||||
secondary: const Icon(Icons.beach_access_outlined),
|
||||
title: const Text('In den Ferien ausblenden'),
|
||||
subtitle: const Text(
|
||||
'An Ferientagen wird der Termin nicht angezeigt',
|
||||
),
|
||||
value: _skipHolidays,
|
||||
onChanged: (v) => setState(() => _skipHolidays = v),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:syncfusion_flutter_calendar/calendar.dart';
|
||||
|
||||
import '../../../../api/marianumconnect/models/mc_holiday.dart';
|
||||
import '../../../../api/marianumconnect/queries/timetable_get_subjects/timetable_get_subjects_response.dart';
|
||||
import '../../../../api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
|
||||
import '../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart';
|
||||
@@ -16,6 +17,7 @@ class TimetableAppointmentFactory {
|
||||
final List<McTimetableEntry> lessons;
|
||||
final List<CustomTimetableEvent> customEvents;
|
||||
final List<McSubject> subjects;
|
||||
final List<McHoliday> holidays;
|
||||
final TimetableSettings settings;
|
||||
final DateTime now;
|
||||
|
||||
@@ -25,6 +27,7 @@ class TimetableAppointmentFactory {
|
||||
required this.subjects,
|
||||
required this.settings,
|
||||
required this.now,
|
||||
this.holidays = const [],
|
||||
});
|
||||
|
||||
List<Appointment> build() {
|
||||
@@ -98,6 +101,14 @@ class TimetableAppointmentFactory {
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
// Ferien ausblenden ist nur für Serien sinnvoll; Syncfusion matcht
|
||||
// Ausnahmen über recurrenceExceptionDates. Überzählige Ferientage, an denen
|
||||
// gar keine Occurrence liegt, ignoriert es folgenlos — daher genügt es, für
|
||||
// jeden Ferientag ein Ausnahmedatum zur Terminzeit einzustreuen, ohne die
|
||||
// RRULE selbst expandieren zu müssen.
|
||||
if (event.skipHolidays && parsed.rule.isNotEmpty) {
|
||||
exceptionDates.addAll(_holidayExceptionDates(event));
|
||||
}
|
||||
return Appointment(
|
||||
id: CustomAppointment(event),
|
||||
startTime: event.startDate,
|
||||
@@ -128,6 +139,36 @@ class TimetableAppointmentFactory {
|
||||
);
|
||||
}
|
||||
|
||||
List<DateTime> _holidayExceptionDates(CustomTimetableEvent event) {
|
||||
final result = <DateTime>[];
|
||||
for (final holiday in holidays) {
|
||||
var day = DateTime(
|
||||
holiday.startDate.year,
|
||||
holiday.startDate.month,
|
||||
holiday.startDate.day,
|
||||
);
|
||||
final last = DateTime(
|
||||
holiday.endDate.year,
|
||||
holiday.endDate.month,
|
||||
holiday.endDate.day,
|
||||
);
|
||||
while (!day.isAfter(last)) {
|
||||
result.add(
|
||||
DateTime(
|
||||
day.year,
|
||||
day.month,
|
||||
day.day,
|
||||
event.startDate.hour,
|
||||
event.startDate.minute,
|
||||
event.startDate.second,
|
||||
),
|
||||
);
|
||||
day = DateTime(day.year, day.month, day.day + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// All-day convention: a `CustomTimetableEvent` is treated as all-day when
|
||||
/// its `startDate` and `endDate` both land on midnight of the same day.
|
||||
/// Keeps the backend schema unchanged — the editor stores all-day events as
|
||||
|
||||
@@ -79,6 +79,7 @@ class TimetableCalendarViewState extends State<TimetableCalendarView> {
|
||||
lessons: state.getAllKnownLessons().toList(),
|
||||
customEvents: widget.customEvents,
|
||||
subjects: state.subjects?.result ?? const [],
|
||||
holidays: state.schoolHolidays?.result ?? const [],
|
||||
settings: timetableSettings,
|
||||
now: DateTime.now(),
|
||||
).build();
|
||||
|
||||
Reference in New Issue
Block a user