f655adfbea
Migrate app code into canonical feature slices, add phone-only AI digest scheduling and review, wire local notification/background task support, and cover the flow with tests.
77 lines
2.3 KiB
Dart
77 lines
2.3 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:relationship_saver/core/auth/token_store.dart';
|
|
import 'package:relationship_saver/integrations/backend/backend_gateway.dart';
|
|
import 'package:relationship_saver/integrations/backend/backend_gateway_provider.dart';
|
|
import 'package:relationship_saver/integrations/backend/models/backend_models.dart';
|
|
|
|
/// Manages authenticated session lifecycle for app-level navigation.
|
|
class SessionController extends AsyncNotifier<AuthSession?> {
|
|
@override
|
|
Future<AuthSession?> build() async {
|
|
return ref.read(tokenStoreProvider).read();
|
|
}
|
|
|
|
Future<void> signIn({
|
|
required SignInMethod method,
|
|
String? email,
|
|
String? password,
|
|
String? idToken,
|
|
}) async {
|
|
state = const AsyncLoading<AuthSession?>();
|
|
|
|
try {
|
|
final BackendGateway gateway = ref.read(backendGatewayProvider);
|
|
final AuthSession session = await gateway.signIn(
|
|
SignInRequest(
|
|
method: method,
|
|
email: email,
|
|
password: password,
|
|
idToken: idToken,
|
|
),
|
|
);
|
|
|
|
// Ensure fake gateway sessions are also persisted.
|
|
await ref.read(tokenStoreProvider).write(session);
|
|
state = AsyncData<AuthSession?>(session);
|
|
} catch (error, stackTrace) {
|
|
state = AsyncError<AuthSession?>(error, stackTrace);
|
|
}
|
|
}
|
|
|
|
Future<void> signOut() async {
|
|
final BackendGateway gateway = ref.read(backendGatewayProvider);
|
|
final TokenStore tokenStore = ref.read(tokenStoreProvider);
|
|
|
|
try {
|
|
await gateway.signOut();
|
|
} catch (_) {
|
|
// Offline-first sign-out should always clear local session.
|
|
}
|
|
|
|
await tokenStore.clear();
|
|
state = const AsyncData<AuthSession?>(null);
|
|
}
|
|
|
|
Future<void> refreshProfile() async {
|
|
final AuthSession? current = state.asData?.value;
|
|
if (current == null) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
final UserProfile profile = await ref.read(backendGatewayProvider).me();
|
|
final AuthSession updated = current.copyWith(user: profile);
|
|
await ref.read(tokenStoreProvider).write(updated);
|
|
state = AsyncData<AuthSession?>(updated);
|
|
} catch (_) {
|
|
// Keep current state if profile refresh fails.
|
|
}
|
|
}
|
|
}
|
|
|
|
final AsyncNotifierProvider<SessionController, AuthSession?>
|
|
sessionControllerProvider =
|
|
AsyncNotifierProvider<SessionController, AuthSession?>(
|
|
SessionController.new,
|
|
);
|