implemented dynamic module settings and configurable bottom bar, added all-day event support to timetable, and overhauled marianum dates UI with month grouping and search

This commit is contained in:
2026-05-06 22:37:41 +02:00
parent 86d12884fc
commit 95ef29fb09
19 changed files with 1114 additions and 253 deletions
@@ -68,27 +68,51 @@ class TimetableAppointmentFactory {
}
}
Appointment _customEventToAppointment(CustomTimetableEvent event) => Appointment(
id: CustomAppointment(event),
startTime: event.startDate,
endTime: event.endDate,
location: _collapseWhitespace(event.description),
subject: _collapseWhitespace(event.title) ?? event.title,
recurrenceRule: event.rrule,
color: TimetableColors.getColorFromString(event.color ?? TimetableColors.defaultColor.name),
startTimeZone: '',
endTimeZone: '',
);
Appointment _customEventToAppointment(CustomTimetableEvent event) {
final allDay = isCustomEventAllDay(event);
return Appointment(
id: CustomAppointment(event),
startTime: event.startDate,
endTime: allDay
? DateTime(event.startDate.year, event.startDate.month, event.startDate.day, 23, 59)
: event.endDate,
isAllDay: allDay,
location: _collapseWhitespace(event.description),
subject: _collapseWhitespace(event.title) ?? event.title,
recurrenceRule: event.rrule,
color: TimetableColors.getColorFromString(event.color ?? TimetableColors.defaultColor.name),
startTimeZone: '',
endTimeZone: '',
);
}
/// 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
/// `start == end == midnight(date)`.
static bool isCustomEventAllDay(CustomTimetableEvent event) {
final s = event.startDate;
final e = event.endDate;
return s.year == e.year &&
s.month == e.month &&
s.day == e.day &&
s.hour == 0 &&
s.minute == 0 &&
s.second == 0 &&
e.hour == 0 &&
e.minute == 0 &&
e.second == 0;
}
String _subjectName(GetTimetableResponseObject lesson) {
final subject = subjects.result.firstWhereOrNull((s) => s.id == lesson.su.firstOrNull?.id);
if (subject == null) return 'Unbekannt';
if (subject == null) return 'Event';
final name = switch (settings.timetableNameMode) {
TimetableNameMode.name => subject.name,
TimetableNameMode.longName => subject.longName,
TimetableNameMode.alternateName => subject.alternateName,
};
return _collapseWhitespace(name) ?? 'Unbekannt';
return _collapseWhitespace(name) ?? 'Event';
}
String _locationLabel(GetTimetableResponseObject lesson) {