refreshUser method

Future<void> refreshUser()

Refreshes login-level fields (avatar, name, email) via Portal login, and academic data (profile, registrations) via the student query service. The login call also establishes a fresh session for the subsequent SSO calls.

On missing or rejected credentials, destroys the session and returns a never-completing future (router guard handles redirect).

Implementation

Future<void> refreshUser() async {
  final user = await _database.select(_database.users).getSingleOrNull();
  if (user == null) {
    throw StateError('Cannot fetch user profile when not logged in');
  }

  // Re-login to refresh login-level fields and establish a fresh session.
  // This makes the subsequent withAuth call's inner SSO unlikely to need
  // re-authentication, since the session was just established.
  final UserDto userDto;
  try {
    userDto = await _reauthenticate();
  } on _AuthFailedException {
    return Completer<void>().future;
  }

  final (profile, records) = await withAuth(
    () async {
      final profileFuture = _studentQueryService.getStudentProfile();
      final recordsFuture = _studentQueryService.getRegistrationRecords();
      return (profileFuture, recordsFuture).wait;
    },
    sso: [.studentQueryService],
  );

  await _database.transaction(() async {
    await (_database.update(
      _database.users,
    )..where((t) => t.id.equals(user.id))).write(
      UsersCompanion(
        avatarFilename: Value(userDto.avatarFilename ?? ''),
        nameZh: Value(userDto.name ?? ''),
        email: Value(userDto.email ?? ''),
        nameEn: Value(profile.englishName),
        dateOfBirth: Value(profile.dateOfBirth),
        programZh: Value(profile.programZh),
        programEn: Value(profile.programEn),
        departmentZh: Value(profile.departmentZh),
        departmentEn: Value(profile.departmentEn),
        fetchedAt: Value(DateTime.now()),
      ),
    );

    for (final record in records) {
      if (record.semester.year == null || record.semester.term == null) {
        continue;
      }
      final semester = await _database.getOrCreateSemester(
        record.semester.year!,
        record.semester.term!,
      );
      await _database
          .into(_database.userSemesterSummaries)
          .insert(
            UserSemesterSummariesCompanion.insert(
              user: user.id,
              semester: semester.id,
              className: Value(record.className),
              enrollmentStatus: Value(record.enrollmentStatus),
              registered: Value(record.registered),
              graduated: Value(record.graduated),
            ),
            onConflict: DoUpdate(
              (old) => UserSemesterSummariesCompanion(
                className: Value(record.className),
                enrollmentStatus: Value(record.enrollmentStatus),
                registered: Value(record.registered),
                graduated: Value(record.graduated),
              ),
              target: [
                _database.userSemesterSummaries.user,
                _database.userSemesterSummaries.semester,
              ],
            ),
          );
    }
  });
}