show classname instead of teacher name in teacher timetable view

This commit is contained in:
2026-08-09 12:19:57 +02:00
parent 39c16bd4ea
commit 889d8f67c5
21 changed files with 292 additions and 81 deletions
+3
View File
@@ -28,6 +28,9 @@ abstract class WidgetLesson with _$WidgetLesson {
required String subjectShort,
String? subjectLong,
String? room,
/// On teacher accounts this carries the class label ("7a") instead of the
/// teacher short name — see `WidgetDataMapper` `showClassInsteadOfTeacher`;
/// [originalTeacher] is null in that case.
String? teacher,
String? originalTeacher,
required WidgetLessonStatus status,
+44 -6
View File
@@ -10,6 +10,7 @@ import '../api/marianumconnect/queries/timetable_get_week/timetable_get_week_res
import '../api/mhsl/custom_timetable_event/custom_timetable_event.dart';
import '../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart';
import '../extensions/date_time.dart';
import '../view/pages/timetable/data/lesson_labels.dart';
import '../view/pages/timetable/data/lesson_merger.dart';
import '../view/pages/timetable/data/lesson_period_schedule.dart';
import '../view/pages/timetable/data/lesson_status.dart';
@@ -58,6 +59,7 @@ class WidgetDataMapper {
TimetableGetTimegridResponse? timegrid,
GetCustomTimetableEventResponse? customEvents,
bool connectDoubleLessons = true,
bool showClassInsteadOfTeacher = false,
}) {
final anchor = resolveDayAnchor(now);
final holiday = _findHoliday(anchor, holidays);
@@ -68,7 +70,13 @@ class WidgetDataMapper {
? LessonMerger.merge(dayLessons)
: dayLessons;
final mapped = <WidgetLesson>[
...source.map((l) => _mapLesson(l, now, subjects, rooms)),
..._mapAll(
source,
now,
subjects,
rooms,
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
),
..._expandCustomEvents(customEvents, dayStart, dayEnd),
]..sort((a, b) => a.start.compareTo(b.start));
return WidgetTimetableData(
@@ -90,6 +98,7 @@ class WidgetDataMapper {
TimetableGetTimegridResponse? timegrid,
GetCustomTimetableEventResponse? customEvents,
bool connectDoubleLessons = true,
bool showClassInsteadOfTeacher = false,
}) {
final anchor = resolveWeekAnchor(now);
// The window is anchored at the *current* calendar week, not the
@@ -107,7 +116,13 @@ class WidgetDataMapper {
? _mergePerDay(weekLessons)
: weekLessons;
final mapped = <WidgetLesson>[
...source.map((l) => _mapLesson(l, now, subjects, rooms)),
..._mapAll(
source,
now,
subjects,
rooms,
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
),
..._expandCustomEvents(customEvents, windowStart, endExclusive),
]..sort((a, b) => a.start.compareTo(b.start));
final days = [
@@ -282,12 +297,29 @@ class WidgetDataMapper {
return [for (final group in byDay.values) ...LessonMerger.merge(group)];
}
static Iterable<WidgetLesson> _mapAll(
Iterable<McTimetableEntry> source,
DateTime now,
TimetableGetSubjectsResponse? subjects,
TimetableGetRoomsResponse? rooms, {
required bool showClassInsteadOfTeacher,
}) => source.map(
(l) => _mapLesson(
l,
now,
subjects,
rooms,
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
),
);
static WidgetLesson _mapLesson(
McTimetableEntry lesson,
DateTime now,
TimetableGetSubjectsResponse? subjects,
TimetableGetRoomsResponse? rooms,
) {
TimetableGetRoomsResponse? rooms, {
bool showClassInsteadOfTeacher = false,
}) {
final start = lesson.startDateTime;
final end = lesson.endDateTime;
final status = _mapStatus(
@@ -314,8 +346,14 @@ class WidgetDataMapper {
roomName;
}
final teacher = lesson.teachers.firstOrNull;
final teacherName = teacher?.shortName;
final originalTeacher = teacher?.originalShortName;
// Lehrerpläne: Klasse in den Teacher-Slot mappen, damit die nativen
// Renderer unverändert bleiben. Klassenlose Einträge (Aufsichten) behalten
// den Lehrer als Fallback.
final classLabel = showClassInsteadOfTeacher ? lesson.classLabel : null;
final teacherName = classLabel ?? teacher?.shortName;
final originalTeacher = classLabel != null
? null
: teacher?.originalShortName;
return WidgetLesson(
start: start,
end: end,
+9 -3
View File
@@ -22,14 +22,18 @@ class WidgetPublisher {
static Future<void> publishFromBlocState(
TimetableState state, {
Settings? settings,
bool isTeacher = false,
}) async {
try {
final connectDouble =
settings?.timetableSettings.connectDoubleLessons ?? true;
// Mirror into widget storage so the background isolate sees the same
// value the user just toggled.
await WidgetSync.setConnectDoubleLessons(connectDouble);
await WidgetSync.setThemeMode(_themeName(settings?.appTheme));
// values the user just toggled — concurrently, they are independent.
await Future.wait([
WidgetSync.setConnectDoubleLessons(connectDouble),
WidgetSync.setThemeMode(_themeName(settings?.appTheme)),
WidgetSync.setIsTeacher(isTeacher),
]);
final lessons = state.getAllKnownLessons();
final now = widgetNow();
final dayData = WidgetDataMapper.buildDayData(
@@ -41,6 +45,7 @@ class WidgetPublisher {
timegrid: state.timegrid,
customEvents: state.customEvents,
connectDoubleLessons: connectDouble,
showClassInsteadOfTeacher: isTeacher,
);
final weekData = WidgetDataMapper.buildWeekData(
now: now,
@@ -51,6 +56,7 @@ class WidgetPublisher {
timegrid: state.timegrid,
customEvents: state.customEvents,
connectDoubleLessons: connectDouble,
showClassInsteadOfTeacher: isTeacher,
);
await WidgetSync.writeDayData(dayData);
await WidgetSync.writeWeekData(weekData);
+22 -14
View File
@@ -31,6 +31,9 @@ class WidgetSync {
static const String connectDoubleLessonsKey =
'widget_setting_connect_double_lessons_v1';
static const String themeModeKey = 'widget_setting_theme_mode_v1';
// Mirrored from CapabilitiesCubit so the background isolate can render
// teacher plans (class instead of teacher name) without bloc storage.
static const String isTeacherKey = 'widget_setting_is_teacher_v1';
// Mirrored so the background isolate hits the same Marianum-Connect base
// URL the in-app settings cubit currently has selected.
static const String marianumConnectBaseUrlKey = 'widget_setting_mc_base_url_v1';
@@ -58,25 +61,30 @@ class WidgetSync {
);
}
static Future<void> setLoggedIn(bool loggedIn) async {
await ensureInitialized();
await HomeWidget.saveWidgetData<bool>(loggedInKey, loggedIn);
}
static Future<void> setLoggedIn(bool loggedIn) =>
_setBool(loggedInKey, loggedIn);
static Future<void> setConnectDoubleLessons(bool value) async {
await ensureInitialized();
await HomeWidget.saveWidgetData<bool>(connectDoubleLessonsKey, value);
}
static Future<void> setConnectDoubleLessons(bool value) =>
_setBool(connectDoubleLessonsKey, value);
/// Default `true` matches `default_settings.dart` — fresh install behaves
/// like the in-app calendar.
static Future<bool> getConnectDoubleLessons() async {
static Future<bool> getConnectDoubleLessons() =>
_getBool(connectDoubleLessonsKey, defaultValue: true);
static Future<void> setIsTeacher(bool value) => _setBool(isTeacherKey, value);
static Future<bool> getIsTeacher() =>
_getBool(isTeacherKey, defaultValue: false);
static Future<void> _setBool(String key, bool value) async {
await ensureInitialized();
final value = await HomeWidget.getWidgetData<bool>(
connectDoubleLessonsKey,
defaultValue: true,
);
return value ?? true;
await HomeWidget.saveWidgetData<bool>(key, value);
}
static Future<bool> _getBool(String key, {required bool defaultValue}) async {
await ensureInitialized();
return await HomeWidget.getWidgetData<bool>(key) ?? defaultValue;
}
static Future<void> setThemeMode(String mode) async {