login method
Authenticates with NTUT Portal and saves the user profile.
Throws LoginException if login is rejected (wrong credentials, account locked, password expired, etc.). Throws DioException on network failure. On success, credentials are stored securely for auto-login.
Implementation
Future<User> login(String username, String password) async {
final isDemo = isDemoCredentials(username, password);
final UserDto userDto;
if (isDemo) {
// Demo mode: skip real portal, use hardcoded data
userDto = (
name: '王大同',
avatarFilename: null,
email: 't$demoUsername@ntut.edu.tw',
passwordExpiresInDays: null,
);
} else {
userDto = await _portalService.login(username, password);
}
// Save credentials for auto-login.
//
// Demo mode deliberately skips secure storage — demo sessions persist
// across restarts via the DB user row instead:
// - main.dart restores isDemoProvider by matching studentId == demoUsername
// - _reauthenticate() falls back to the demo username when secure
// storage is empty and _isDemo is true (mock ignores password)
if (!isDemo) {
await _secureStorage.write(key: _usernameKey, value: username);
await _secureStorage.write(key: _passwordKey, value: password);
}
final user = await _database.transaction(() async {
await _database.delete(_database.users).go();
return _database
.into(_database.users)
.insertReturning(
UsersCompanion.insert(
studentId: username,
nameZh: userDto.name ?? '',
avatarFilename: userDto.avatarFilename ?? '',
email: userDto.email ?? '',
passwordExpiresInDays: Value(userDto.passwordExpiresInDays),
),
);
});
_onSessionCreated();
return user;
}