Phase 3.1 WIP: Riverpod migration (client_app Auth + ChatOpening)
- Add phase3.1 requirement and implementation plan docs - Add Riverpod dependencies to both client_app and mitra_app - Wrap both app roots with ProviderScope - Migrate client_app AuthBloc → AuthNotifier (@riverpod annotation) - Migrate client_app ChatOpeningBloc → chatPricingProvider (FutureProvider) - Update router to use Riverpod-based auth state for redirects - Update all auth screens (display name, register, OTP, force register) - Update home screen and pricing bottom sheet - Add android:usesCleartextTraffic for dev HTTP access on both apps - mitra_app prepared with ProviderScope + ApiClient provider (blocs next) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'core/auth/auth_bloc.dart';
|
||||
import 'core/auth/auth_notifier.dart';
|
||||
import 'features/auth/screens/welcome_screen.dart';
|
||||
import 'features/auth/screens/display_name_screen.dart';
|
||||
import 'features/auth/screens/register_screen.dart';
|
||||
@@ -16,38 +16,43 @@ import 'features/chat/screens/chat_screen.dart';
|
||||
import 'features/chat/screens/chat_history_screen.dart';
|
||||
import 'features/chat/screens/chat_transcript_screen.dart';
|
||||
|
||||
/// Converts a BLoC stream into a ChangeNotifier for GoRouter's refreshListenable.
|
||||
class _BlocRefreshNotifier extends ChangeNotifier {
|
||||
late final StreamSubscription _subscription;
|
||||
class RouterNotifier extends ChangeNotifier {
|
||||
final Ref _ref;
|
||||
|
||||
_BlocRefreshNotifier(AuthBloc bloc) {
|
||||
_subscription = bloc.stream.listen((_) => notifyListeners());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subscription.cancel();
|
||||
super.dispose();
|
||||
RouterNotifier(this._ref) {
|
||||
_ref.listen(authProvider, (_, __) => notifyListeners());
|
||||
}
|
||||
}
|
||||
|
||||
GoRouter buildRouter(AuthBloc authBloc) {
|
||||
final routerProvider = Provider<GoRouter>((ref) => buildRouter(ref));
|
||||
|
||||
GoRouter buildRouter(Ref ref) {
|
||||
final notifier = RouterNotifier(ref);
|
||||
|
||||
return GoRouter(
|
||||
initialLocation: '/splash',
|
||||
refreshListenable: _BlocRefreshNotifier(authBloc),
|
||||
refreshListenable: notifier,
|
||||
redirect: (context, state) {
|
||||
final authState = authBloc.state;
|
||||
final authState = ref.read(authProvider);
|
||||
final isSplash = state.matchedLocation == '/splash';
|
||||
final isAuthRoute = state.matchedLocation.startsWith('/auth') ||
|
||||
state.matchedLocation == '/welcome';
|
||||
|
||||
// Show splash while loading
|
||||
if (authState is AuthLoading) return isSplash ? null : '/splash';
|
||||
if (authState is AsyncLoading) return isSplash ? null : '/splash';
|
||||
|
||||
if (authState is AuthAuthenticated || authState is AuthAnonymous) {
|
||||
final data = authState.valueOrNull;
|
||||
if (data == null) {
|
||||
// Error state — show login
|
||||
if (!isAuthRoute && !isSplash) return '/welcome';
|
||||
if (isSplash) return '/welcome';
|
||||
return null;
|
||||
}
|
||||
|
||||
if (data is AuthAuthenticatedData || data is AuthAnonymousData) {
|
||||
return (isSplash || isAuthRoute) ? '/home' : null;
|
||||
}
|
||||
if (authState is AuthForceRegister) return '/auth/force-register';
|
||||
if (data is AuthForceRegisterData) return '/auth/force-register';
|
||||
if (!isAuthRoute && !isSplash) return '/welcome';
|
||||
if (isSplash) return '/welcome';
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user