simplified code across the app and consolidated duplicated helpers
This commit is contained in:
@@ -53,6 +53,42 @@ class WidgetDataMapper {
|
||||
/// native renderers can roll the view forward without fresh data.
|
||||
static const int weekWindowDays = 14;
|
||||
|
||||
/// Day and week payload from the same inputs.
|
||||
static ({WidgetTimetableData day, WidgetTimetableData week}) buildSnapshot({
|
||||
required DateTime now,
|
||||
required Iterable<McTimetableEntry> lessons,
|
||||
required TimetableGetSubjectsResponse? subjects,
|
||||
required TimetableGetRoomsResponse? rooms,
|
||||
required TimetableGetHolidaysResponse? holidays,
|
||||
TimetableGetTimegridResponse? timegrid,
|
||||
GetCustomTimetableEventResponse? customEvents,
|
||||
bool connectDoubleLessons = true,
|
||||
bool showClassInsteadOfTeacher = false,
|
||||
}) => (
|
||||
day: buildDayData(
|
||||
now: now,
|
||||
lessons: lessons,
|
||||
subjects: subjects,
|
||||
rooms: rooms,
|
||||
holidays: holidays,
|
||||
timegrid: timegrid,
|
||||
customEvents: customEvents,
|
||||
connectDoubleLessons: connectDoubleLessons,
|
||||
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
|
||||
),
|
||||
week: buildWeekData(
|
||||
now: now,
|
||||
lessons: lessons,
|
||||
subjects: subjects,
|
||||
rooms: rooms,
|
||||
holidays: holidays,
|
||||
timegrid: timegrid,
|
||||
customEvents: customEvents,
|
||||
connectDoubleLessons: connectDoubleLessons,
|
||||
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
|
||||
),
|
||||
);
|
||||
|
||||
static WidgetTimetableData buildDayData({
|
||||
required DateTime now,
|
||||
required Iterable<McTimetableEntry> lessons,
|
||||
@@ -68,24 +104,23 @@ class WidgetDataMapper {
|
||||
final holiday = _findHoliday(anchor, holidays);
|
||||
final dayStart = anchor;
|
||||
final dayEnd = anchor.add(const Duration(days: 1));
|
||||
final dayLessons = lessons.where((l) => _onSameDay(l, anchor)).toList();
|
||||
final dayLessons = lessons.where((l) => l.date.isSameDay(anchor)).toList();
|
||||
final source = connectDoubleLessons
|
||||
? LessonMerger.merge(dayLessons)
|
||||
: dayLessons;
|
||||
final mapped = <WidgetLesson>[
|
||||
..._mapAll(
|
||||
return WidgetTimetableData(
|
||||
fetchedAt: now,
|
||||
anchorDate: anchor,
|
||||
lessons: _mapWindow(
|
||||
source,
|
||||
now,
|
||||
subjects,
|
||||
rooms,
|
||||
customEvents,
|
||||
dayStart,
|
||||
dayEnd,
|
||||
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
|
||||
),
|
||||
..._expandCustomEvents(customEvents, dayStart, dayEnd),
|
||||
]..sort((a, b) => a.start.compareTo(b.start));
|
||||
return WidgetTimetableData(
|
||||
fetchedAt: now,
|
||||
anchorDate: anchor,
|
||||
lessons: _resolveCollisions(mapped),
|
||||
periods: _resolvePeriods(timegrid),
|
||||
isHoliday: holiday != null,
|
||||
holidayName: holiday?.longName,
|
||||
@@ -118,16 +153,6 @@ class WidgetDataMapper {
|
||||
final source = connectDoubleLessons
|
||||
? _mergePerDay(weekLessons)
|
||||
: weekLessons;
|
||||
final mapped = <WidgetLesson>[
|
||||
..._mapAll(
|
||||
source,
|
||||
now,
|
||||
subjects,
|
||||
rooms,
|
||||
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
|
||||
),
|
||||
..._expandCustomEvents(customEvents, windowStart, endExclusive),
|
||||
]..sort((a, b) => a.start.compareTo(b.start));
|
||||
final days = [
|
||||
for (var i = 0; i < weekWindowDays; i++)
|
||||
_dayInfo(windowStart.addDays(i), holidays),
|
||||
@@ -141,7 +166,16 @@ class WidgetDataMapper {
|
||||
return WidgetTimetableData(
|
||||
fetchedAt: now,
|
||||
anchorDate: anchor,
|
||||
lessons: _resolveCollisions(mapped),
|
||||
lessons: _mapWindow(
|
||||
source,
|
||||
now,
|
||||
subjects,
|
||||
rooms,
|
||||
customEvents,
|
||||
windowStart,
|
||||
endExclusive,
|
||||
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
|
||||
),
|
||||
periods: _resolvePeriods(timegrid),
|
||||
isHoliday: anchorInfo.isHoliday,
|
||||
holidayName: anchorInfo.holidayName,
|
||||
@@ -300,28 +334,38 @@ class WidgetDataMapper {
|
||||
return [for (final group in byDay.values) ...LessonMerger.merge(group)];
|
||||
}
|
||||
|
||||
static Iterable<WidgetLesson> _mapAll(
|
||||
/// Lessons plus custom events in [start, endExclusive), sorted by start
|
||||
/// with overlaps resolved.
|
||||
static List<WidgetLesson> _mapWindow(
|
||||
Iterable<McTimetableEntry> source,
|
||||
DateTime now,
|
||||
TimetableGetSubjectsResponse? subjects,
|
||||
TimetableGetRoomsResponse? rooms, {
|
||||
TimetableGetRoomsResponse? rooms,
|
||||
GetCustomTimetableEventResponse? customEvents,
|
||||
DateTime start,
|
||||
DateTime endExclusive, {
|
||||
required bool showClassInsteadOfTeacher,
|
||||
}) => source.map(
|
||||
(l) => _mapLesson(
|
||||
l,
|
||||
now,
|
||||
subjects,
|
||||
rooms,
|
||||
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
|
||||
),
|
||||
);
|
||||
}) {
|
||||
final mapped = <WidgetLesson>[
|
||||
for (final lesson in source)
|
||||
_mapLesson(
|
||||
lesson,
|
||||
now,
|
||||
subjects,
|
||||
rooms,
|
||||
showClassInsteadOfTeacher: showClassInsteadOfTeacher,
|
||||
),
|
||||
..._expandCustomEvents(customEvents, start, endExclusive),
|
||||
]..sort((a, b) => a.start.compareTo(b.start));
|
||||
return _resolveCollisions(mapped);
|
||||
}
|
||||
|
||||
static WidgetLesson _mapLesson(
|
||||
McTimetableEntry lesson,
|
||||
DateTime now,
|
||||
TimetableGetSubjectsResponse? subjects,
|
||||
TimetableGetRoomsResponse? rooms, {
|
||||
bool showClassInsteadOfTeacher = false,
|
||||
required bool showClassInsteadOfTeacher,
|
||||
}) {
|
||||
final start = lesson.startDateTime;
|
||||
final end = lesson.endDateTime;
|
||||
@@ -395,43 +439,29 @@ class WidgetDataMapper {
|
||||
return '#${rgb.toRadixString(16).padLeft(6, '0').toUpperCase()}';
|
||||
}
|
||||
|
||||
static WidgetLessonStatus _mapStatus(LessonStatus status) {
|
||||
switch (status) {
|
||||
case LessonStatus.cancelled:
|
||||
return WidgetLessonStatus.cancelled;
|
||||
case LessonStatus.event:
|
||||
return WidgetLessonStatus.event;
|
||||
case LessonStatus.irregular:
|
||||
return WidgetLessonStatus.irregular;
|
||||
case LessonStatus.teacherChanged:
|
||||
return WidgetLessonStatus.teacherChanged;
|
||||
case LessonStatus.duty:
|
||||
return WidgetLessonStatus.duty;
|
||||
case LessonStatus.past:
|
||||
return WidgetLessonStatus.past;
|
||||
case LessonStatus.ongoing:
|
||||
return WidgetLessonStatus.ongoing;
|
||||
case LessonStatus.regular:
|
||||
return WidgetLessonStatus.regular;
|
||||
}
|
||||
}
|
||||
|
||||
static bool _onSameDay(McTimetableEntry lesson, DateTime day) {
|
||||
return lesson.date.year == day.year &&
|
||||
lesson.date.month == day.month &&
|
||||
lesson.date.day == day.day;
|
||||
}
|
||||
static WidgetLessonStatus _mapStatus(LessonStatus status) =>
|
||||
switch (status) {
|
||||
LessonStatus.cancelled => WidgetLessonStatus.cancelled,
|
||||
LessonStatus.event => WidgetLessonStatus.event,
|
||||
LessonStatus.irregular => WidgetLessonStatus.irregular,
|
||||
LessonStatus.teacherChanged => WidgetLessonStatus.teacherChanged,
|
||||
LessonStatus.duty => WidgetLessonStatus.duty,
|
||||
LessonStatus.past => WidgetLessonStatus.past,
|
||||
LessonStatus.ongoing => WidgetLessonStatus.ongoing,
|
||||
LessonStatus.regular => WidgetLessonStatus.regular,
|
||||
};
|
||||
|
||||
static McHoliday? _findHoliday(
|
||||
DateTime day,
|
||||
TimetableGetHolidaysResponse? holidays,
|
||||
) {
|
||||
if (holidays == null) return null;
|
||||
final asDay = DateTime(day.year, day.month, day.day);
|
||||
final asDay = day.dateOnly;
|
||||
for (final h in holidays.result) {
|
||||
final start = DateTime(h.startDate.year, h.startDate.month, h.startDate.day);
|
||||
final end = DateTime(h.endDate.year, h.endDate.month, h.endDate.day);
|
||||
if (!asDay.isBefore(start) && !asDay.isAfter(end)) return h;
|
||||
if (!asDay.isBefore(h.startDate.dateOnly) &&
|
||||
!asDay.isAfter(h.endDate.dateOnly)) {
|
||||
return h;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -80,22 +80,10 @@ class WidgetPublisher {
|
||||
]);
|
||||
_lastFlags = flags;
|
||||
}
|
||||
final lessons = state.getAllKnownLessons();
|
||||
final now = widgetNow();
|
||||
final dayData = WidgetDataMapper.buildDayData(
|
||||
final (day: dayData, week: weekData) = WidgetDataMapper.buildSnapshot(
|
||||
now: now,
|
||||
lessons: lessons,
|
||||
subjects: state.subjects,
|
||||
rooms: state.rooms,
|
||||
holidays: state.schoolHolidays,
|
||||
timegrid: state.timegrid,
|
||||
customEvents: state.customEvents,
|
||||
connectDoubleLessons: connectDouble,
|
||||
showClassInsteadOfTeacher: isTeacher,
|
||||
);
|
||||
final weekData = WidgetDataMapper.buildWeekData(
|
||||
now: now,
|
||||
lessons: lessons,
|
||||
lessons: state.getAllKnownLessons(),
|
||||
subjects: state.subjects,
|
||||
rooms: state.rooms,
|
||||
holidays: state.schoolHolidays,
|
||||
@@ -121,10 +109,7 @@ class WidgetPublisher {
|
||||
}
|
||||
_lastSignature = signature;
|
||||
_lastPublishedAt = now;
|
||||
await WidgetSync.writeDayData(dayData);
|
||||
await WidgetSync.writeWeekData(weekData);
|
||||
await WidgetSync.setLoggedIn(true);
|
||||
await WidgetSync.triggerUpdate();
|
||||
await WidgetSync.publishSnapshot(day: dayData, week: weekData);
|
||||
} on Object catch (e, s) {
|
||||
// Catch Object: non-Exception Errors (RangeError, StateError) from the
|
||||
// bloc layer must not escape into the stream listener.
|
||||
@@ -135,15 +120,5 @@ class WidgetPublisher {
|
||||
static Map<String, Object?> _withoutFetchedAt(Map<String, Object?> json) =>
|
||||
Map.of(json)..remove('fetchedAt');
|
||||
|
||||
static String _themeName(ThemeMode? mode) {
|
||||
switch (mode) {
|
||||
case ThemeMode.light:
|
||||
return 'light';
|
||||
case ThemeMode.dark:
|
||||
return 'dark';
|
||||
case ThemeMode.system:
|
||||
case null:
|
||||
return 'system';
|
||||
}
|
||||
}
|
||||
static String _themeName(ThemeMode? mode) => (mode ?? ThemeMode.system).name;
|
||||
}
|
||||
|
||||
@@ -53,13 +53,21 @@ class WidgetSync {
|
||||
static Future<void> writeWeekData(WidgetTimetableData data) =>
|
||||
_writeData(weekDataKey, data);
|
||||
|
||||
/// Writes a complete snapshot and marks the widget signed in, then asks the
|
||||
/// native side to re-render.
|
||||
static Future<void> publishSnapshot({
|
||||
required WidgetTimetableData day,
|
||||
required WidgetTimetableData week,
|
||||
}) async {
|
||||
await writeDayData(day);
|
||||
await writeWeekData(week);
|
||||
await setLoggedIn(true);
|
||||
await triggerUpdate();
|
||||
}
|
||||
|
||||
static Future<void> _writeData(String key, WidgetTimetableData data) async {
|
||||
await ensureInitialized();
|
||||
await HomeWidget.saveWidgetData<String>(key, jsonEncode(data.toJson()));
|
||||
await HomeWidget.saveWidgetData<String>(
|
||||
fetchedAtKey,
|
||||
data.fetchedAt.toIso8601String(),
|
||||
);
|
||||
await _setString(key, jsonEncode(data.toJson()));
|
||||
await _setString(fetchedAtKey, data.fetchedAt.toIso8601String());
|
||||
}
|
||||
|
||||
static Future<void> setLoggedIn(bool loggedIn) =>
|
||||
@@ -88,27 +96,30 @@ class WidgetSync {
|
||||
return await HomeWidget.getWidgetData<bool>(key) ?? defaultValue;
|
||||
}
|
||||
|
||||
static Future<void> setThemeMode(String mode) async {
|
||||
await ensureInitialized();
|
||||
await HomeWidget.saveWidgetData<String>(themeModeKey, mode);
|
||||
}
|
||||
static Future<void> setThemeMode(String mode) =>
|
||||
_setString(themeModeKey, mode);
|
||||
|
||||
static Future<void> setMarianumConnectBaseUrl(String url) async {
|
||||
await ensureInitialized();
|
||||
await HomeWidget.saveWidgetData<String>(marianumConnectBaseUrlKey, url);
|
||||
}
|
||||
static Future<void> setMarianumConnectBaseUrl(String url) =>
|
||||
_setString(marianumConnectBaseUrlKey, url);
|
||||
|
||||
static Future<String?> getMarianumConnectBaseUrl() async {
|
||||
await ensureInitialized();
|
||||
return HomeWidget.getWidgetData<String>(marianumConnectBaseUrlKey);
|
||||
}
|
||||
static Future<String?> getMarianumConnectBaseUrl() =>
|
||||
_getString(marianumConnectBaseUrlKey);
|
||||
|
||||
static Future<DateTime?> getFetchedAt() async {
|
||||
await ensureInitialized();
|
||||
final raw = await HomeWidget.getWidgetData<String>(fetchedAtKey);
|
||||
final raw = await _getString(fetchedAtKey);
|
||||
return raw == null ? null : DateTime.tryParse(raw);
|
||||
}
|
||||
|
||||
static Future<void> _setString(String key, String? value) async {
|
||||
await ensureInitialized();
|
||||
await HomeWidget.saveWidgetData<String>(key, value);
|
||||
}
|
||||
|
||||
static Future<String?> _getString(String key) async {
|
||||
await ensureInitialized();
|
||||
return HomeWidget.getWidgetData<String>(key);
|
||||
}
|
||||
|
||||
static Future<void> clear() async {
|
||||
WidgetPublisher.resetDedupe();
|
||||
await ensureInitialized();
|
||||
|
||||
Reference in New Issue
Block a user