watchSemesterRecords method

Stream<List<SemesterRecordData>> watchSemesterRecords()

Watches aggregated academic records grouped by semester.

Emits cached data immediately, then triggers a background network fetch if data is empty or stale. The stream re-emits automatically when the DB is updated.

Network errors during background refresh are absorbed — the stream continues showing stale (or empty) data rather than erroring.

Implementation

Stream<List<SemesterRecordData>> watchSemesterRecords() async* {
  const ttl = Duration(days: 3);

  // Watch academic summaries as the trigger signal. Score data changes
  // atomically in a transaction, so this catches all updates.
  await for (final _
      in _database.select(_database.userAcademicSummaries).watch()) {
    final user = await _database.select(_database.users).getSingleOrNull();
    if (user == null) {
      yield [];
      continue;
    }

    final records = await _buildSemesterRecordData(user.id);

    if (records.isEmpty) {
      try {
        await refreshSemesterRecords();
      } catch (_) {
        // Absorb: yield empty below so UI exits loading state
      }
    }

    yield records;

    // Re-read user to get the latest scoreDataFetchedAt
    final freshUser = await _database
        .select(_database.users)
        .getSingleOrNull();
    final age = switch (freshUser?.scoreDataFetchedAt) {
      final t? => DateTime.now().difference(t),
      null => ttl,
    };
    if (age >= ttl) {
      try {
        await refreshSemesterRecords();
      } catch (_) {
        // Absorb: stale data is shown via stream
      }
    }
  }
}