import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; import '../features/auth/view/login_page.dart'; import '../features/profile/view/profile_edit_page.dart'; import '../features/profile/view/profile_setup_page.dart'; import '../features/rating/data/rating_repository.dart'; import '../features/rewards/view/rewards_page.dart'; import '../features/rating/view/rating_page.dart'; import '../features/settings/view/settings_page.dart'; import '../features/splash/view/splash_page.dart'; import '../features/support/view/support_page.dart'; import '../features/trip/view/history_page.dart'; import '../features/chat/view/chat_page.dart'; import '../features/onboarding/view/onboarding_page.dart'; import '../features/trip/view/duty_page.dart'; import '../features/wallet/view/earnings_page.dart'; import 'di.dart'; import 'session/session_cubit.dart'; import 'session/session_state.dart'; class Routes { const Routes._(); static const splash = '/'; static const login = '/login'; static const profile = '/profile'; static const home = '/home'; static const rating = '/rating'; static const wallet = '/wallet'; static const history = '/history'; static const profileEdit = '/account'; static const settings = '/settings'; static const support = '/support'; static const rewards = '/rewards'; static const chat = '/chat'; static const onboarding = '/onboarding'; } /// التوجيه يُشتقّ من `SessionCubit` وحده — لا `Navigator.push` بعد الدخول /// موزّعة في الشاشات، فلا تتناقض حالتان. final appRouter = GoRouter( initialLocation: Routes.splash, refreshListenable: _CubitListenable(sl().stream), redirect: (context, state) { final session = sl().state; final loc = state.matchedLocation; // قبل أن نعرف: نبقى على الإقلاع ولا نقرّر شيئاً. if (!session.isReady) return loc == Routes.splash ? null : Routes.splash; return switch (session.status) { SessionStatus.unauthenticated => loc == Routes.login ? null : Routes.login, SessionStatus.needsProfile => loc == Routes.profile ? null : Routes.profile, // التقييم المعلّق يحجز التطبيق كله: لا شاشة أخرى قبل حسمه // (docs/38 §7 — يُفرض عند الفتح). SessionStatus.authenticated when session.pendingRating != null => loc == Routes.rating ? null : Routes.rating, // السائق غير المعتمد يُحجز في التسجيل: دوره لا يزال `rider` على // الخادم، فكل نقاط السائق ترجّع 403 (docs/38 §5). SessionStatus.authenticated when session.user?.role != 'driver' && loc != Routes.onboarding => Routes.onboarding, SessionStatus.authenticated => (loc == Routes.login || loc == Routes.profile || loc == Routes.splash || loc == Routes.rating) ? Routes.home : null, SessionStatus.unknown => Routes.splash, }; }, routes: [ GoRoute( path: Routes.splash, builder: (context, state) => const SplashPage(), ), GoRoute( path: Routes.login, builder: (context, state) => const LoginPage(), ), GoRoute( path: Routes.profile, builder: (context, state) => const ProfileSetupPage(), ), GoRoute( path: Routes.rating, builder: (context, state) { final pending = sl().state.pendingRating!; return RatingPage( pending: PendingRating( tripId: pending.tripId, role: pending.role, ), onDone: sl().onRatingResolved, ); }, ), GoRoute( path: Routes.wallet, builder: (context, state) => const EarningsPage(), ), GoRoute( path: Routes.history, builder: (context, state) => const HistoryPage(), ), GoRoute( path: Routes.profileEdit, builder: (context, state) => const ProfileEditPage(), ), GoRoute( path: Routes.settings, builder: (context, state) => const SettingsPage(), ), GoRoute( path: Routes.chat, builder: (context, state) => ChatPage(tripId: state.uri.queryParameters['trip'] ?? ''), ), GoRoute( path: Routes.onboarding, builder: (context, state) => const OnboardingPage(), ), GoRoute( path: Routes.rewards, builder: (context, state) => const RewardsPage(), ), GoRoute( path: Routes.support, builder: (context, state) => const SupportPage(), ), GoRoute( path: Routes.home, builder: (context, state) => const DutyPage(), ), ], ); /// جسر بين تيّار الـCubit و`refreshListenable` الذي يطلبه go_router. class _CubitListenable extends ChangeNotifier { _CubitListenable(Stream stream) { _sub = stream.listen((_) => notifyListeners()); } late final StreamSubscription _sub; @override void dispose() { _sub.cancel(); super.dispose(); } }