Files
Client/lib/view/pages/marianum_dates/marianum_dates_view.dart
T
2026-05-08 20:12:40 +02:00

125 lines
4.8 KiB
Dart

import 'package:flutter/material.dart';
import '../../../extensions/date_time.dart';
import '../../../state/app/infrastructure/loadable_state/loadable_state.dart';
import '../../../state/app/infrastructure/loadable_state/view/loadable_state_consumer.dart';
import '../../../state/app/infrastructure/utility_widgets/bloc_module.dart';
import '../../../state/app/modules/marianum_dates/bloc/marianum_dates_bloc.dart';
import '../../../state/app/modules/marianum_dates/bloc/marianum_dates_event.dart';
import '../../../state/app/modules/marianum_dates/bloc/marianum_dates_state.dart';
import '../../../widget/placeholder_view.dart';
import 'search_marianum_dates.dart';
import 'widgets/event_list_tile.dart';
import 'widgets/month_section_header.dart';
class MarianumDatesView extends StatelessWidget {
const MarianumDatesView({super.key});
/// Groups events by `yyyy-MM` (chronological). Uses the event's start date.
static List<_MonthGroup> _groupByMonth(List<MarianumDate> events) {
final byMonth = <String, List<MarianumDate>>{};
for (final e in events) {
final key =
'${e.start.year.toString().padLeft(4, '0')}-${e.start.month.toString().padLeft(2, '0')}';
byMonth.putIfAbsent(key, () => []).add(e);
}
final keys = byMonth.keys.toList()..sort();
return keys.map((key) {
final first = byMonth[key]!.first.start;
final label = first.formatMonthYear().toUpperCase();
return _MonthGroup(key: key, label: label, events: byMonth[key]!);
}).toList();
}
@override
Widget build(BuildContext context) =>
BlocModule<MarianumDatesBloc, LoadableState<MarianumDatesState>>(
create: (context) => MarianumDatesBloc(),
autoRebuild: true,
child: (context, bloc, state) => Scaffold(
appBar: AppBar(
title: const Text('Marianum Termine'),
actions: [
PopupMenuButton<bool>(
initialValue: bloc.showPastEvents(),
icon: const Icon(Icons.history),
itemBuilder: (context) => [true, false]
.map(
(e) => PopupMenuItem<bool>(
value: e,
enabled: e != bloc.showPastEvents(),
child: Row(
children: [
Icon(
e
? Icons.history_outlined
: Icons.history_toggle_off_outlined,
color: Theme.of(context).colorScheme.onSurface,
),
const SizedBox(width: 15),
Text(
e ? 'Alle anzeigen' : 'Nur zukünftige anzeigen',
),
],
),
),
)
.toList(),
onSelected: (e) => bloc.add(SetPastEventsVisible(e)),
),
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
final events = bloc.getEvents() ?? const <MarianumDate>[];
showSearch(
context: context,
delegate: SearchMarianumDates(events),
);
},
),
],
),
body: LoadableStateConsumer<MarianumDatesBloc, MarianumDatesState>(
child: (state, loading) {
final events = bloc.getEvents() ?? const <MarianumDate>[];
final groups = _groupByMonth(events);
if (groups.isEmpty) {
return const PlaceholderView(
icon: Icons.event_busy_outlined,
text: 'Keine Termine',
);
}
return CustomScrollView(
slivers: [
for (final group in groups)
SliverMainAxisGroup(
slivers: [
SliverPersistentHeader(
pinned: true,
delegate: MonthHeaderDelegate(label: group.label),
),
SliverList.builder(
itemCount: group.events.length,
itemBuilder: (_, i) =>
MarianumDateRow(event: group.events[i]),
),
],
),
const SliverToBoxAdapter(child: SizedBox(height: 24)),
],
);
},
),
),
);
}
class _MonthGroup {
final String key;
final String label;
final List<MarianumDate> events;
_MonthGroup({required this.key, required this.label, required this.events});
}