feat: Phase 1 complete — Matrix login, rooms, chat, profile
- Direct m.login.password auth against matrix.m8chat.au - Room list with unread badges, last message, timestamps - Chat timeline (text, images, files, replies, reactions) - Profile screen with expandable Notifications and Security sections - Olm E2EE initialisation (web WASM bootstrap) - Global error handler preventing Matrix SDK crashes - GoRouter with refreshListenable (no recreation on auth change) - Feature-first clean architecture: Riverpod + GoRouter + Drift - Deployed to https://app2.m8chat.au Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
95
lib/features/auth/data/auth_repository.dart
Normal file
95
lib/features/auth/data/auth_repository.dart
Normal file
@@ -0,0 +1,95 @@
|
||||
// Version: 1.0.3 | Created: 2026-04-01
|
||||
// Auth repository: handles all Matrix login/logout API interactions.
|
||||
// Uses the Matrix Dart SDK — no raw HTTP calls for auth.
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import '../../../core/config/app_config.dart';
|
||||
import '../../../core/network/matrix_client.dart';
|
||||
import '../domain/auth_failure.dart';
|
||||
|
||||
part 'auth_repository.g.dart';
|
||||
|
||||
@riverpod
|
||||
AuthRepository authRepository(Ref ref) {
|
||||
return AuthRepository(client: ref.watch(matrixClientProvider));
|
||||
}
|
||||
|
||||
/// Handles authentication interactions with the Matrix homeserver.
|
||||
class AuthRepository {
|
||||
AuthRepository({required Client client}) : _client = client;
|
||||
|
||||
final Client _client;
|
||||
|
||||
/// Attempts password login. Returns [LoginResponse] on success,
|
||||
/// throws [AuthFailure] on failure.
|
||||
///
|
||||
/// Matrix error codes mapped:
|
||||
/// M_FORBIDDEN → [InvalidCredentials]
|
||||
/// M_USER_DEACTIVATED → [AccountDisabled]
|
||||
/// Network error → [NetworkError]
|
||||
Future<LoginResponse> login({
|
||||
required String username,
|
||||
required String password,
|
||||
}) async {
|
||||
try {
|
||||
// Set homeserver directly — avoids network round-trips and version checks
|
||||
// that checkHomeserver() makes. The setter is public in matrix 0.33.0.
|
||||
_client.homeserver = Uri.parse(AppConfig.matrixBaseUrl);
|
||||
return await _client.login(
|
||||
LoginType.mLoginPassword,
|
||||
identifier: AuthenticationUserIdentifier(user: username),
|
||||
password: password,
|
||||
initialDeviceDisplayName: 'M8Chat',
|
||||
);
|
||||
} on MatrixException catch (e) {
|
||||
throw switch (e.errcode) {
|
||||
'M_FORBIDDEN' => const AuthFailure.invalidCredentials(),
|
||||
'M_USER_DEACTIVATED' => const AuthFailure.accountDisabled(),
|
||||
_ => AuthFailure.serverError(
|
||||
statusCode: e.response?.statusCode,
|
||||
message: e.errorMessage,
|
||||
),
|
||||
};
|
||||
} on Exception catch (e) {
|
||||
// Covers SocketException, TimeoutException, etc.
|
||||
final msg = e.toString().toLowerCase();
|
||||
if (msg.contains('socket') ||
|
||||
msg.contains('connection') ||
|
||||
msg.contains('host lookup') ||
|
||||
msg.contains('timeout')) {
|
||||
throw AuthFailure.networkError(message: e.toString());
|
||||
}
|
||||
throw AuthFailure.unknown(message: e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/// Logs out the current session on the homeserver.
|
||||
/// Silently succeeds if the token is already invalid (network-first logout).
|
||||
Future<void> logout() async {
|
||||
try {
|
||||
await _client.logout();
|
||||
} on MatrixException {
|
||||
// Token already invalid — treat as successful logout.
|
||||
} on Exception {
|
||||
// Network offline — proceed with local cleanup regardless.
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores an existing Matrix session using a stored access token.
|
||||
Future<void> restoreSession({
|
||||
required String accessToken,
|
||||
required String userId,
|
||||
required String deviceId,
|
||||
}) async {
|
||||
await _client.init(
|
||||
newToken: accessToken,
|
||||
newUserID: userId,
|
||||
newDeviceID: deviceId,
|
||||
newDeviceName: 'M8Chat',
|
||||
newHomeserver: Uri.parse(AppConfig.matrixBaseUrl),
|
||||
newOlmAccount: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user