custom login implementation, period-based timetable layout with overlap handling, enhanced error dialogs, and unified bottom sheets

This commit is contained in:
2026-05-06 20:42:09 +02:00
parent 50d2941e52
commit 86d12884fc
32 changed files with 1038 additions and 377 deletions
@@ -23,29 +23,24 @@ class WebuntisLessonSheet {
final headerTitle = _firstNonEmpty([headerSubject.alternateName, headerSubject.name, headerSubject.longName, '?']);
final headerLongName = headerSubject.longName.isNotEmpty && headerSubject.longName != headerTitle ? headerSubject.longName : '';
final timeRange =
'${Jiffy.parseFromDateTime(appointment.startTime).format(pattern: 'HH:mm')} - '
'${Jiffy.parseFromDateTime(appointment.endTime).format(pattern: 'HH:mm')}';
showAppointmentBottomSheet(
context,
header: (_) => Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${_codePrefix(lesson.code)}$headerTitle',
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 25),
overflow: TextOverflow.ellipsis,
),
if (headerLongName.isNotEmpty) Text(headerLongName),
Text(
'${Jiffy.parseFromDateTime(appointment.startTime).format(pattern: 'HH:mm')} - '
'${Jiffy.parseFromDateTime(appointment.endTime).format(pattern: 'HH:mm')}',
style: const TextStyle(fontSize: 15),
),
],
header: ListTile(
leading: Icon(_iconForCode(lesson.code), size: 32),
title: Text(
'${_codePrefix(lesson.code)}$headerTitle',
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(headerLongName.isNotEmpty
? '$timeRange\n$headerLongName'
: timeRange),
isThreeLine: headerLongName.isNotEmpty,
),
body: (_) => SliverChildListDelegate(<Widget>[
const Divider(),
children: (_) => <Widget>[
ListTile(
leading: const Icon(Icons.notifications_active),
title: Text('Status: ${_statusLabel(lesson.code)}'),
@@ -82,10 +77,21 @@ class WebuntisLessonSheet {
),
..._optionalTextTiles(lesson),
DebugTile(context).jsonData(lesson.toJson()),
]),
],
);
}
static IconData _iconForCode(String? code) {
switch (code) {
case 'cancelled':
return Icons.event_busy_outlined;
case 'irregular':
return Icons.swap_horiz;
default:
return Icons.school_outlined;
}
}
static Widget _roomTile(BuildContext context, TimetableState state, GetTimetableResponseObject lesson) {
final trailing = IconButton(
icon: const Icon(Icons.house_outlined),
@@ -193,7 +199,7 @@ class WebuntisLessonSheet {
static Widget? _textTile(IconData icon, String label, String? value) {
final text = (value ?? '').trim();
if (text.isEmpty) return null;
if (text.isEmpty || text == '-') return null;
return ListTile(
leading: Icon(icon),
title: Text(label),