implemented custom subject colors for the timetable and unified the color palette system

This commit is contained in:
2026-07-12 21:30:08 +02:00
parent a7111844b1
commit fe2b3c43b2
23 changed files with 916 additions and 89 deletions
@@ -10,13 +10,20 @@ class AppointmentDetailsDispatcher {
static void show(
BuildContext context,
TimetableState? state,
Appointment appointment,
) {
Appointment appointment, {
bool canEditSubjectColor = false,
}) {
final id = appointment.id;
if (id is! ArbitraryAppointment) return;
id.when(
lesson: (entry) => LessonSheet.show(context, state, appointment, entry),
lesson: (entry) => LessonSheet.show(
context,
state,
appointment,
entry,
canEditSubjectColor: canEditSubjectColor,
),
custom: (event) => CustomEventSheet.show(context, event),
);
}
@@ -1,18 +1,22 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rrule/rrule.dart';
import '../../../../api/mhsl/custom_timetable_event/custom_timetable_event.dart';
import '../../../../extensions/date_time.dart';
import '../../../../state/app/modules/timetable/bloc/timetable_bloc.dart';
import '../../../../widget/centered_leading.dart';
import '../../../../widget/debug/debug_tile.dart';
import '../../../../widget/details_bottom_sheet.dart';
import '../custom_events/custom_event_edit_dialog.dart';
import '../data/rrule_with_exceptions.dart';
import '../subject_colors/subject_color_picker.dart';
import 'delete_custom_event.dart';
class CustomEventSheet {
static void show(BuildContext context, CustomTimetableEvent event) {
final timeRange = event.startDate.timeRangeTo(event.endDate);
final bloc = context.read<TimetableBloc>();
showDetailsBottomSheet(
context,
@@ -23,6 +27,12 @@ class CustomEventSheet {
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(timeRange),
trailing: TimetableColorHeaderButton(
initialColorName: event.color,
pickerTitle: 'Farbe wählen',
onSelected: (option) =>
bloc.updateCustomEvent(event.id, _withColor(event, option.name)),
),
),
children: (sheetCtx) => [
Padding(
@@ -104,4 +114,18 @@ class CustomEventSheet {
],
);
}
static CustomTimetableEvent _withColor(CustomTimetableEvent e, String color) =>
CustomTimetableEvent(
id: e.id,
title: e.title,
description: e.description,
startDate: e.startDate,
endDate: e.endDate,
color: color,
rrule: e.rrule,
skipHolidays: e.skipHolidays,
createdAt: e.createdAt,
updatedAt: DateTime.now(),
);
}
@@ -1,31 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
import '../../../../api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
import '../../../../extensions/date_time.dart';
import '../../../../extensions/text.dart';
import '../../../../routing/app_routes.dart';
import '../../../../state/app/modules/timetable/bloc/timetable_bloc.dart';
import '../../../../state/app/modules/timetable/bloc/timetable_state.dart';
import '../../../../widget/debug/debug_tile.dart';
import '../../../../widget/details_bottom_sheet.dart';
import '../data/lesson_type_label.dart';
import '../subject_colors/subject_color_picker.dart';
class LessonSheet {
static void show(
BuildContext context,
TimetableState? state,
Appointment appointment,
McTimetableEntry lesson,
) {
McTimetableEntry lesson, {
bool canEditSubjectColor = false,
}) {
if (state == null) return;
// In Fremdansichten würde der Farb-Button die eigene, globale Fach-Farbe
// ändern, ohne dass sich der fremde Plan aktualisiert — daher nur im
// eigenen Plan anbieten. Die Einfärbung (eigene Farben) bleibt.
final bloc = context.read<TimetableBloc>();
final subjectShort = lesson.subjects.firstOrNull;
final headerLong = subjectShort == null
final subjectEntry = subjectShort == null
? null
: state.subjects?.result
.where((s) => s.shortName == subjectShort)
.firstOrNull
?.longName;
.firstOrNull;
final headerLong = subjectEntry?.longName;
// Bei Stunden ohne Fach (Pausenaufsicht etc.) den Lesson-Type-Titel
// einsetzen — sonst stünde im Header nur ein generisches "?".
final headerTitle = subjectShort != null
@@ -50,6 +58,18 @@ class LessonSheet {
headerLongName.isNotEmpty ? '$timeRange\n$headerLongName' : timeRange,
),
isThreeLine: headerLongName.isNotEmpty,
trailing: canEditSubjectColor && subjectShort != null
? TimetableColorHeaderButton(
initialColorName: subjectEntry?.color,
pickerTitle: (headerLong != null && headerLong.isNotEmpty)
? 'Farbe: $headerLong'
: 'Farbe: $subjectShort',
pickerSubtitle: 'Nur reguläre Stunden werden eingefärbt',
onSelected: (option) =>
bloc.setSubjectColor(subjectShort, option.name),
onReset: () => bloc.clearSubjectColor(subjectShort),
)
: null,
),
children: (_) => <Widget>[
ListTile(