refactored and condensed technical documentation and comments across the codebase to improve readability

This commit is contained in:
2026-07-06 23:17:06 +02:00
parent c48f5ef215
commit a19b67eb84
27 changed files with 99 additions and 193 deletions
@@ -196,12 +196,9 @@ class ChatBloc
void _startLongPoll(String token) {
if (!_appResumed) return;
// A load chain may finish AFTER the user already switched chats — e.g. a
// notification tap resumes the app (lifecycle refresh starts loading the
// still-open chat A) and then navigates to chat B. Without this guard the
// stale chain's completion would hijack the long-poll back to A and its
// responses would merge A's messages into B's state (chat B showing chat
// A's content) while B never receives live updates.
// A load chain may finish after the user switched chats (A→B); without this
// guard the stale chain hijacks the long-poll back to A and merges A's
// messages into B's state while B never receives live updates.
if ((innerState?.currentToken ?? '') != token) return;
if (_pollingToken == token) return;
_stopLongPoll();
@@ -44,11 +44,9 @@ class FilesBloc
await _query(path, renew: true);
}
/// LoadableState.reFetch (used by the pull-to-refresh indicator and the
/// error-screen retry button) routes through here. The inherited retry()
/// goes via gatherData() which respects the cache TTL — for an explicit
/// user-initiated reload we must bypass it, otherwise the root listing
/// silently returns its day-old cached payload without hitting the server.
/// Pull-to-refresh and error-screen retry route through here. Unlike the
/// inherited retry() (via gatherData(), which respects the cache TTL), this
/// bypasses the TTL — otherwise the root listing returns its day-old cache.
@override
void retry() {
unawaited(refresh());
@@ -6,16 +6,13 @@ import '../../../../../api/marianumcloud/webdav/webdav_api.dart';
import '../../../../../api/request_cache.dart';
class FilesDataProvider {
/// Lists files at [path]. Cached payload is delivered via [onCacheData] as
/// soon as it is read from disk, so callers can render stale data while the
/// network call is still pending. The Future itself resolves once both the
/// cache lookup and the network attempt have settled, throwing if no payload
/// could be obtained at all.
/// Lists files at [path]. Cached payload is delivered via [onCacheData] the
/// moment it is read from disk so callers can render stale data while the
/// network call is pending; the Future resolves once both have settled,
/// throwing if no payload could be obtained at all.
///
/// Pass [renew] for explicit user-triggered reloads (pull-to-refresh, after
/// a rename / delete / move / upload). It bypasses the per-path TTL in
/// [ListFilesCache] so the root listing — which is otherwise cached for a
/// full day — still refetches when the user actively asks for it.
/// Pass [renew] for explicit user-triggered reloads to bypass the per-path
/// TTL in [ListFilesCache] (the root listing is otherwise cached for a day).
Future<ListFilesResponse> listFiles(
String path, {
void Function(ListFilesResponse)? onCacheData,
@@ -12,14 +12,11 @@ import '../../timetable/bloc/timetable_event.dart';
import '../../timetable/bloc/timetable_state.dart';
import '../repository/foreign_timetable_repository.dart';
/// Drives a foreign element's timetable. Mirrors the week-loading and
/// week-navigation logic of `TimetableBloc` but (a) loads weeks from the
/// element endpoint, (b) carries no custom events, and (c) does not persist —
/// it is created per opened page and recreated for every selected element.
///
/// It reuses [TimetableState] verbatim so the existing render pipeline works
/// unchanged; `customEvents` simply stays null (the foreign view uses an
/// `isReady` predicate that ignores it).
/// Drives a foreign element's timetable. Mirrors `TimetableBloc`'s week-loading
/// and navigation but loads weeks from the element endpoint, carries no custom
/// events, and does not persist (page-scoped, recreated per element). Reuses
/// [TimetableState] verbatim so the render pipeline is unchanged; `customEvents`
/// stays null (the foreign view's `isReady` predicate ignores it).
class ForeignTimetableBloc
extends
LoadableHydratedBloc<
@@ -56,10 +53,8 @@ class ForeignTimetableBloc
);
}
// Persistence is disabled: this bloc is page-scoped and element-specific, so
// there is nothing worth restoring across launches. Returning null from
// toJson means HydratedBloc never writes anything; fromJson ignores any
// legacy payload and starts fresh.
// Persistence disabled: page-scoped and element-specific, nothing worth
// restoring. toJson returns null so nothing is written; fromJson starts fresh.
@override
Map<String, dynamic>? toJson(LoadableState<TimetableState> state) => null;
@@ -10,9 +10,9 @@ import '../../timetable/data_provider/timetable_data_provider.dart';
/// Data access for a foreign element's timetable. The week comes from the
/// element-specific endpoint; all reference data (rooms/subjects/holidays/
/// school year/timegrid) is school-wide and identical to the user's own plan,
/// so it is delegated to the existing [TimetableDataProvider] (which already
/// caches it). Custom events are intentionally absent — they are user-private.
/// school year/timegrid) is school-wide, so it delegates to the existing
/// [TimetableDataProvider] (which caches it). Custom events are intentionally
/// absent — they are user-private.
class ForeignTimetableDataProvider {
final TimetableDataProvider _base;
@@ -17,9 +17,8 @@ class SettingsCubit extends HydratedCubit<Settings> {
Settings val({bool write = false}) {
if (write) {
// Defer the emit until the synchronous mutation on the returned object
// has finished. Without this scheduleMicrotask the cubit emits a copy
// captured *before* the assignment runs, so listeners (and HydratedBloc
// persistence) see the old value on the first emit.
// has finished — without this microtask the cubit emits a copy captured
// *before* the assignment, so listeners see the old value.
if (!_emitScheduled) {
_emitScheduled = true;
scheduleMicrotask(() {
@@ -27,10 +27,9 @@ abstract class TimetableState with _$TimetableState {
required DateTime startDate,
required DateTime endDate,
@Default(0) int dataVersion,
// Boundaries learned from past server denials of inaccessible weeks.
// Inclusive: weeks whose start is on/before `accessibleEndDate` and
// whose end is on/after `accessibleStartDate` are within the user's
// permitted range. Null = no upper / lower bound discovered yet.
// Boundaries learned from past server denials. A week is permitted when its
// start is on/before `accessibleEndDate` and its end on/after
// `accessibleStartDate`. Null = that bound not discovered yet.
DateTime? accessibleStartDate,
DateTime? accessibleEndDate,
}) = _TimetableState;
@@ -23,10 +23,9 @@ import '../../../../../api/mhsl/custom_timetable_event/update/update_custom_time
import '../../../../../api/request_cache.dart';
import '../../../../../model/account_data.dart';
/// Pulls the timetable from the Marianum-Connect mobile API. Each MC endpoint
/// is its own HTTP call; this provider just exposes the lazy futures so the
/// bloc can chain them without seeing the dio layer. Custom events still come
/// from the MHSL backend and are unchanged.
/// Pulls the timetable from the Marianum-Connect mobile API. Each endpoint is
/// its own HTTP call; this provider exposes the lazy futures so the bloc can
/// chain them without seeing the dio layer. Custom events still come from MHSL.
class TimetableDataProvider {
Future<TimetableGetWeekResponse> getWeek(
DateTime startDate,