simplified code across the app and consolidated duplicated helpers
This commit is contained in:
@@ -10,8 +10,10 @@ import '../../../state/app/modules/foreign_timetable/bloc/foreign_timetable_bloc
|
||||
import '../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
||||
import '../../../state/app/modules/timetable/bloc/timetable_bloc.dart';
|
||||
import '../../../state/app/modules/timetable/bloc/timetable_state.dart';
|
||||
import '../../../state/app/modules/timetable/bloc/timetable_week_loader.dart';
|
||||
import '../../../utils/haptics.dart';
|
||||
import '../../../widget/demo_restricted.dart';
|
||||
import '../foreign_timetable/element_type_icon.dart';
|
||||
import 'custom_events/custom_event_edit_dialog.dart';
|
||||
import 'details/appointment_details_dispatcher.dart';
|
||||
import 'widgets/timetable_calendar_view.dart';
|
||||
@@ -33,14 +35,8 @@ class _TimetableState extends State<Timetable> {
|
||||
/// user's own. Cleared (back to own plan) via the viewing banner.
|
||||
TimetableElementRef? _selected;
|
||||
|
||||
DateTime _initialDisplayDate() => DateTime.now().addDays(2);
|
||||
|
||||
void _jumpToToday() {
|
||||
_calendarKey.currentState?.jumpToToday();
|
||||
}
|
||||
|
||||
bool _isOnInitialWeek(TimetableState state) =>
|
||||
state.startDate == _mondayOf(_initialDisplayDate());
|
||||
static bool _isOnInitialWeek(TimetableState? state) =>
|
||||
state != null && state.startDate == timetableAnchorDate().mondayOfWeek;
|
||||
|
||||
Future<void> _openPicker() async {
|
||||
if (guardDemoAction(context)) return;
|
||||
@@ -57,20 +53,7 @@ class _TimetableState extends State<Timetable> {
|
||||
setState(() => _selected = null);
|
||||
}
|
||||
|
||||
void _onAction(_CalendarAction action) {
|
||||
switch (action) {
|
||||
case _CalendarAction.addEvent:
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => const CustomEventEditDialog(),
|
||||
barrierDismissible: false,
|
||||
);
|
||||
case _CalendarAction.viewEvents:
|
||||
AppRoutes.openCustomEvents(context);
|
||||
}
|
||||
}
|
||||
|
||||
void _onCreateEventAt(DateTime start, DateTime end) {
|
||||
void _showEventEditor({DateTime? start, DateTime? end}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
@@ -79,6 +62,15 @@ class _TimetableState extends State<Timetable> {
|
||||
);
|
||||
}
|
||||
|
||||
void _onAction(_CalendarAction action) {
|
||||
switch (action) {
|
||||
case _CalendarAction.addEvent:
|
||||
_showEventEditor();
|
||||
case _CalendarAction.viewEvents:
|
||||
AppRoutes.openCustomEvents(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selected = _selected;
|
||||
@@ -87,11 +79,8 @@ class _TimetableState extends State<Timetable> {
|
||||
// (or back to the own plan) tears it down and builds a fresh one.
|
||||
return BlocProvider<ForeignTimetableBloc>(
|
||||
key: ValueKey('${selected.type.name}-${selected.id}'),
|
||||
create: (_) => ForeignTimetableBloc(
|
||||
type: selected.type,
|
||||
elementId: selected.id,
|
||||
title: selected.label,
|
||||
),
|
||||
create: (_) =>
|
||||
ForeignTimetableBloc(type: selected.type, elementId: selected.id),
|
||||
// Builder gives us a context *below* the provider so the foreign bloc is
|
||||
// resolvable inside _buildForeignPlan.
|
||||
child: Builder(
|
||||
@@ -100,55 +89,68 @@ class _TimetableState extends State<Timetable> {
|
||||
);
|
||||
}
|
||||
|
||||
AppBar _appBar({
|
||||
required bool atToday,
|
||||
required bool canViewForeign,
|
||||
bool withEventMenu = false,
|
||||
}) => AppBar(
|
||||
// Der Kalender scrollt nicht (nur ziehen/reloaden), aber seine internen
|
||||
// Scrollables feuern ScrollNotifications, die sonst den Material-3
|
||||
// "scrolled under"-Farbwechsel der AppBar dauerhaft auslösen.
|
||||
notificationPredicate: (_) => false,
|
||||
title: const Text('Stunden & Vertretungsplan'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.home_outlined),
|
||||
tooltip: 'Zur aktuellen Woche',
|
||||
onPressed: atToday
|
||||
? null
|
||||
: () => _calendarKey.currentState?.jumpToToday(),
|
||||
),
|
||||
if (withEventMenu)
|
||||
PopupMenuButton<_CalendarAction>(
|
||||
tooltip: 'Kalendereinträge',
|
||||
icon: const Icon(Icons.edit_calendar_outlined),
|
||||
onSelected: _onAction,
|
||||
itemBuilder: (_) => const [
|
||||
PopupMenuItem(
|
||||
value: _CalendarAction.addEvent,
|
||||
child: ListTile(
|
||||
title: Text('Kalendereintrag hinzufügen'),
|
||||
leading: Icon(Icons.add),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _CalendarAction.viewEvents,
|
||||
child: ListTile(
|
||||
title: Text('Kalendereinträge anzeigen'),
|
||||
leading: Icon(Icons.perm_contact_calendar_outlined),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (canViewForeign)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.person_search),
|
||||
tooltip: 'Anderen Stundenplan öffnen',
|
||||
onPressed: _openPicker,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildOwnPlan(BuildContext context) {
|
||||
final bloc = context.read<TimetableBloc>();
|
||||
final loadableState = context.watch<TimetableBloc>().state;
|
||||
final innerState = loadableState.data;
|
||||
final atToday = innerState != null && _isOnInitialWeek(innerState);
|
||||
final atToday = context.select(
|
||||
(TimetableBloc b) => _isOnInitialWeek(b.state.data),
|
||||
);
|
||||
final capabilities = context.watch<CapabilitiesCubit>();
|
||||
final canViewForeign = capabilities.canViewForeignTimetables;
|
||||
final teacherPlan = capabilities.isTeacher;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
// Der Kalender scrollt nicht (nur ziehen/reloaden), aber seine internen
|
||||
// Scrollables feuern ScrollNotifications, die sonst den Material-3
|
||||
// "scrolled under"-Farbwechsel der AppBar dauerhaft auslösen.
|
||||
notificationPredicate: (_) => false,
|
||||
title: const Text('Stunden & Vertretungsplan'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.home_outlined),
|
||||
tooltip: 'Zur aktuellen Woche',
|
||||
onPressed: atToday ? null : _jumpToToday,
|
||||
),
|
||||
PopupMenuButton<_CalendarAction>(
|
||||
tooltip: 'Kalendereinträge',
|
||||
icon: const Icon(Icons.edit_calendar_outlined),
|
||||
onSelected: _onAction,
|
||||
itemBuilder: (_) => const [
|
||||
PopupMenuItem(
|
||||
value: _CalendarAction.addEvent,
|
||||
child: ListTile(
|
||||
title: Text('Kalendereintrag hinzufügen'),
|
||||
leading: Icon(Icons.add),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _CalendarAction.viewEvents,
|
||||
child: ListTile(
|
||||
title: Text('Kalendereinträge anzeigen'),
|
||||
leading: Icon(Icons.perm_contact_calendar_outlined),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (canViewForeign)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.person_search),
|
||||
tooltip: 'Anderen Stundenplan öffnen',
|
||||
onPressed: _openPicker,
|
||||
),
|
||||
],
|
||||
appBar: _appBar(
|
||||
atToday: atToday,
|
||||
canViewForeign: canViewForeign,
|
||||
withEventMenu: true,
|
||||
),
|
||||
body: LoadableStateConsumer<TimetableBloc, TimetableState>(
|
||||
// Without this predicate the consumer treats the freshly-initialised
|
||||
@@ -170,7 +172,8 @@ class _TimetableState extends State<Timetable> {
|
||||
teacherPlan: teacherPlan,
|
||||
onOpenElement: canViewForeign ? _openElement : null,
|
||||
),
|
||||
onCreateEvent: _onCreateEventAt,
|
||||
onCreateEvent: (start, end) =>
|
||||
_showEventEditor(start: start, end: end),
|
||||
customEvents: state.customEvents?.events ?? const [],
|
||||
showClassInsteadOfTeacher: teacherPlan,
|
||||
),
|
||||
@@ -180,44 +183,21 @@ class _TimetableState extends State<Timetable> {
|
||||
|
||||
Widget _buildForeignPlan(BuildContext context, TimetableElementRef selected) {
|
||||
final bloc = context.read<ForeignTimetableBloc>();
|
||||
final loadableState = context.watch<ForeignTimetableBloc>().state;
|
||||
final innerState = loadableState.data;
|
||||
final atToday = innerState != null && _isOnInitialWeek(innerState);
|
||||
final canViewForeign = context
|
||||
.watch<CapabilitiesCubit>()
|
||||
.canViewForeignTimetables;
|
||||
final atToday = context.select(
|
||||
(ForeignTimetableBloc b) => _isOnInitialWeek(b.state.data),
|
||||
);
|
||||
final canViewForeign = context.select(
|
||||
(CapabilitiesCubit c) => c.canViewForeignTimetables,
|
||||
);
|
||||
final teacherPlan = selected.type == TimetableElementType.teacher;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
// Siehe _buildOwnPlan: den scroll-under-Farbwechsel unterdrücken, weil
|
||||
// der Kalender nicht scrollt, aber ScrollNotifications feuert.
|
||||
notificationPredicate: (_) => false,
|
||||
title: const Text('Stunden & Vertretungsplan'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.home_outlined),
|
||||
tooltip: 'Zur aktuellen Woche',
|
||||
onPressed: atToday ? null : _jumpToToday,
|
||||
),
|
||||
if (canViewForeign)
|
||||
IconButton(
|
||||
icon: const Icon(Icons.person_search),
|
||||
tooltip: 'Anderen Stundenplan öffnen',
|
||||
onPressed: _openPicker,
|
||||
),
|
||||
],
|
||||
),
|
||||
appBar: _appBar(atToday: atToday, canViewForeign: canViewForeign),
|
||||
body: Column(
|
||||
children: [
|
||||
_ViewingBanner(element: selected, onClose: _backToOwnPlan),
|
||||
Expanded(
|
||||
child: LoadableStateConsumer<ForeignTimetableBloc, TimetableState>(
|
||||
// Foreign plans never carry custom events, so unlike the own-plan
|
||||
// view we must not require `customEvents` here.
|
||||
isReady: (state) =>
|
||||
state.rooms != null &&
|
||||
state.subjects != null &&
|
||||
state.schoolHolidays != null,
|
||||
isReady: (state) => state.hasSchoolReferenceData,
|
||||
child: (state, _) => TimetableCalendarView(
|
||||
key: _calendarKey,
|
||||
state: state,
|
||||
@@ -229,7 +209,6 @@ class _TimetableState extends State<Timetable> {
|
||||
teacherPlan: teacherPlan,
|
||||
onOpenElement: canViewForeign ? _openElement : null,
|
||||
),
|
||||
customEvents: const [],
|
||||
showClassInsteadOfTeacher: teacherPlan,
|
||||
),
|
||||
),
|
||||
@@ -238,11 +217,6 @@ class _TimetableState extends State<Timetable> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static DateTime _mondayOf(DateTime d) {
|
||||
final monday = d.subtractDays(d.weekday - 1);
|
||||
return DateTime(monday.year, monday.month, monday.day);
|
||||
}
|
||||
}
|
||||
|
||||
/// Slim banner shown at the top of the timetable while a foreign element's plan
|
||||
@@ -297,7 +271,7 @@ class _ViewingBanner extends StatelessWidget {
|
||||
padding: const EdgeInsets.fromLTRB(16, 2, 6, 2),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(_iconFor(element.type), size: 16, color: onColor),
|
||||
Icon(element.type.icon, size: 16, color: onColor),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Text(
|
||||
@@ -324,17 +298,4 @@ class _ViewingBanner extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static IconData _iconFor(TimetableElementType type) {
|
||||
switch (type) {
|
||||
case TimetableElementType.student:
|
||||
return Icons.person_outline;
|
||||
case TimetableElementType.teacher:
|
||||
return Icons.school_outlined;
|
||||
case TimetableElementType.room:
|
||||
return Icons.meeting_room_outlined;
|
||||
case TimetableElementType.schoolClass:
|
||||
return Icons.groups_outlined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user