migrated timetable integration from WebUntis to the MarianumConnect API, implementing a Dio-based client with bearer token authentication, background session validation, and auto-refresh logic.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import '../../../../api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
|
||||
|
||||
/// Combines back-to-back lessons with identical subject/room/teacher/status
|
||||
/// into a single visual block. Shared by the calendar tile builder and the
|
||||
/// home-widget data mapper so both surfaces show the same merged spans.
|
||||
///
|
||||
/// Built as a new list rather than mutating inputs — earlier in-place merges
|
||||
/// extended merged blocks further on every rebuild when the same lesson
|
||||
/// objects were observed again.
|
||||
class LessonMerger {
|
||||
const LessonMerger._();
|
||||
|
||||
static const Duration defaultMaxGap = Duration(minutes: 5);
|
||||
|
||||
static List<McTimetableEntry> merge(
|
||||
List<McTimetableEntry> input, {
|
||||
Duration maxGap = defaultMaxGap,
|
||||
}) {
|
||||
if (input.isEmpty) return const [];
|
||||
|
||||
final sorted = [...input]
|
||||
..sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
|
||||
|
||||
final merged = <McTimetableEntry>[];
|
||||
for (final current in sorted) {
|
||||
if (merged.isNotEmpty && _canMerge(merged.last, current, maxGap)) {
|
||||
final prev = merged.removeLast();
|
||||
merged.add(_extendedEnd(prev, current.endTime));
|
||||
} else {
|
||||
merged.add(current);
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
static bool _canMerge(
|
||||
McTimetableEntry a,
|
||||
McTimetableEntry b,
|
||||
Duration maxGap,
|
||||
) {
|
||||
if (a.subjects.firstOrNull != b.subjects.firstOrNull) return false;
|
||||
if (a.rooms.firstOrNull != b.rooms.firstOrNull) return false;
|
||||
if (a.teachers.firstOrNull?.shortName !=
|
||||
b.teachers.firstOrNull?.shortName) {
|
||||
return false;
|
||||
}
|
||||
if (a.status != b.status) return false;
|
||||
// Lower bound on the gap — without it, two identical-metadata lessons that
|
||||
// overlap in time would silently collapse into one.
|
||||
final gap = b.startDateTime.difference(a.endDateTime);
|
||||
return !gap.isNegative && gap <= maxGap;
|
||||
}
|
||||
|
||||
static McTimetableEntry _extendedEnd(
|
||||
McTimetableEntry source,
|
||||
DateTime newEndTime,
|
||||
) => McTimetableEntry(
|
||||
id: source.id,
|
||||
date: source.date,
|
||||
startTime: source.startTime,
|
||||
endTime: newEndTime,
|
||||
subjects: source.subjects,
|
||||
teachers: source.teachers,
|
||||
rooms: source.rooms,
|
||||
classNames: source.classNames,
|
||||
lessonType: source.lessonType,
|
||||
status: source.status,
|
||||
substitutionText: source.substitutionText,
|
||||
lessonText: source.lessonText,
|
||||
infoText: source.infoText,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user