improved performance and battery usage on older devices

This commit is contained in:
2026-09-25 23:58:08 +02:00
parent ed8e52f475
commit 56a497985b
70 changed files with 1631 additions and 621 deletions
@@ -133,11 +133,7 @@ class TimetableBloc
Future<void> _refreshSubjects() async {
final subjects = await repo.data.getSubjects(renew: true);
add(
DataGathered(
(s) => s.copyWith(subjects: subjects, dataVersion: s.dataVersion + 1),
),
);
add(DataGathered((s) => s.withReferenceData(subjects: subjects)));
}
Future<void> _loadCurrentWeek(
@@ -177,12 +173,11 @@ class TimetableBloc
add(
Emit(
(s) => s.copyWith(
(s) => s.withReferenceData(
rooms: rooms,
subjects: subjects,
schoolHolidays: schoolHolidays,
schoolyear: schoolyear,
dataVersion: s.dataVersion + 1,
),
),
);
@@ -192,11 +187,7 @@ class TimetableBloc
try {
final timegrid = await repo.data.getTimegrid(renew: renew);
add(
Emit(
(s) => s.copyWith(timegrid: timegrid, dataVersion: s.dataVersion + 1),
),
);
add(Emit((s) => s.withReferenceData(timegrid: timegrid)));
} catch (_) {
// Timegrid load failure falls back to a hardcoded schedule in the UI layer.
}
@@ -212,12 +203,7 @@ class TimetableBloc
renew: renew,
onError: onError,
);
add(
Emit(
(s) =>
s.copyWith(customEvents: events, dataVersion: s.dataVersion + 1),
),
);
add(Emit((s) => s.withReferenceData(customEvents: events)));
} catch (e) {
onError?.call(e);
}
@@ -225,11 +211,7 @@ class TimetableBloc
Future<void> _refreshCustomEvents() async {
final events = await repo.data.getCustomEvents(renew: true);
add(
DataGathered(
(s) => s.copyWith(customEvents: events, dataVersion: s.dataVersion + 1),
),
);
add(DataGathered((s) => s.withReferenceData(customEvents: events)));
}
void _prefetchAdjacentWeeks(DateTime start, DateTime end) {
@@ -7,6 +7,7 @@ import '../../../../../api/marianumconnect/queries/timetable_get_subjects/timeta
import '../../../../../api/marianumconnect/queries/timetable_get_timegrid/timetable_get_timegrid_response.dart';
import '../../../../../api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
import '../../../../../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart';
import '../../../../../utils/json_equality.dart';
import 'week_cache.dart';
part 'timetable_state.freezed.dart';
@@ -58,6 +59,45 @@ abstract class TimetableState with _$TimetableState {
return copyWith(weekCache: updated, dataVersion: dataVersion + 1);
}
/// Applies freshly loaded reference data. Values whose content equals the
/// current one keep the old instance, and when nothing changed at all this
/// returns `this` — every `dataVersion` bump and new identity makes the
/// calendar rebuild all appointments and break regions.
TimetableState withReferenceData({
TimetableGetRoomsResponse? rooms,
TimetableGetSubjectsResponse? subjects,
TimetableGetHolidaysResponse? schoolHolidays,
TimetableGetSchoolyearResponse? schoolyear,
TimetableGetTimegridResponse? timegrid,
GetCustomTimetableEventResponse? customEvents,
}) {
T? changed<T>(T? next, T? current) =>
next == null || sameJson(next, current) ? null : next;
final newRooms = changed(rooms, this.rooms);
final newSubjects = changed(subjects, this.subjects);
final newHolidays = changed(schoolHolidays, this.schoolHolidays);
final newSchoolyear = changed(schoolyear, this.schoolyear);
final newTimegrid = changed(timegrid, this.timegrid);
final newCustomEvents = changed(customEvents, this.customEvents);
if (newRooms == null &&
newSubjects == null &&
newHolidays == null &&
newSchoolyear == null &&
newTimegrid == null &&
newCustomEvents == null) {
return this;
}
return copyWith(
rooms: newRooms ?? this.rooms,
subjects: newSubjects ?? this.subjects,
schoolHolidays: newHolidays ?? this.schoolHolidays,
schoolyear: newSchoolyear ?? this.schoolyear,
timegrid: newTimegrid ?? this.timegrid,
customEvents: newCustomEvents ?? this.customEvents,
dataVersion: dataVersion + 1,
);
}
bool get hasReferenceData =>
rooms != null &&
subjects != null &&
@@ -1,13 +1,12 @@
import 'package:collection/collection.dart';
import '../../../../../api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
import '../../../../../extensions/date_time.dart';
import '../../../../../utils/json_equality.dart';
/// Weeks kept around the viewed week and around today's week. Everything
/// further away is dropped: every state emit re-serializes the whole cache for
/// HydratedBloc and the calendar rebuilds its appointments from all of it, so
/// an unbounded cache makes week swipes slower the longer the app is used.
const int kWeekCacheRadius = 4;
const int kWeekCacheRadius = 8;
/// Returns the cache with [week] stored under [weekStart], pruned to the
/// weeks near [viewedWeekStart] or [now]. Returns null when the stored week
@@ -21,10 +20,7 @@ Map<String, TimetableGetWeekResponse>? mergeWeekIntoCache(
}) {
final key = weekStart.weekKey();
final existing = cache[key];
if (existing != null &&
const DeepCollectionEquality().equals(existing.toJson(), week.toJson())) {
return null;
}
if (sameJson(existing, week)) return null;
final viewedMonday = viewedWeekStart.mondayOfWeek;
final todayMonday = now.mondayOfWeek;