import 'dart:developer' as developer; import 'package:eatvio_app/core/routing/auth_routes.dart'; import 'package:eatvio_app/core/routing/content_routes.dart'; import 'package:eatvio_app/core/routing/main_navigation_routes.dart'; import 'package:eatvio_app/core/routing/other_routes.dart'; import 'package:eatvio_app/core/routing/route_constants.dart'; import 'package:eatvio_app/core/routing/settings_routes.dart'; import 'package:eatvio_app/features/auth/entities/auth_result_entity.dart'; import 'package:flutter/material.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:go_router/go_router.dart'; /// Главный роутер приложения class AppRouter { static GlobalKey navigatorKey = GlobalKey(); // Домены для deep links (в т.ч. ссылки из пушей dev2.eatvio.com) static const List deepLinkHosts = ['eatvio.ru', 'eatvio.com']; static GoRouter appRouter = GoRouter( initialLocation: RouteConstants.updateScreen, navigatorKey: navigatorKey, redirect: _handleRedirect, routes: [ ...AuthRoutes.createRoutes(), ...MainNavigationRoutes.createRoutes(), ...ContentRoutes.createRoutes(), ...SettingsRoutes.createRoutes(), ...OtherRoutes.createRoutes(), ], ); static String? _resolveDeepLinkUri(Uri uri) { final host = uri.host.toLowerCase(); final segments = uri.pathSegments; // Проверяем, что хост заканчивается на один из разрешенных доменов final isDeepLink = deepLinkHosts.any((allowedHost) => host == allowedHost || host.endsWith('.$allowedHost')); if (!isDeepLink) { return null; } developer.log('Deep link detected: host=$host, path=${uri.path}, segments=$segments', name: 'AppRouter'); if (segments.isEmpty) { developer.log('Empty segments in deep link, redirecting to feed', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.feedScreen; } // Обработка api ссылок (например, из пушей чата) if (segments.length >= 4 && segments[0] == 'api' && segments[1] == 'v2' && segments[2] == 'chat' && segments[3] == 'show') { developer.log('Deep link: api/v2/chat/show → chat-select-screen', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.chatSelectScreen; } // Определяем, есть ли в начале сегмент языка (ru/en). Если есть — пропускаем его. int startIndex = 0; if (segments[0] == 'ru' || segments[0] == 'en') { startIndex = 1; } else { developer.log('Deep link without language segment: ${segments[0]}', name: 'AppRouter'); } final effectiveSegments = segments.sublist(startIndex); if (effectiveSegments.isEmpty) { developer.log('No effective segments after language check, redirecting to feed', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.feedScreen; } final type = effectiveSegments[0]; if (effectiveSegments.length == 3 && type == 'consultations' && effectiveSegments[1] == 'status') { final consultationId = effectiveSegments[2]; developer.log('Deep link: consultations/status/$consultationId', name: 'AppRouter'); FlutterNativeSplash.remove(); return '${RouteConstants.consultationsScreen}?id=$consultationId'; } if (effectiveSegments.length == 2) { final contentIdentifier = effectiveSegments[1]; switch (type) { case 'recipe': developer.log('Deep link: recipe/$contentIdentifier', name: 'AppRouter'); FlutterNativeSplash.remove(); return '/about-recipes-screen?slug=$contentIdentifier'; case 'blog_post': developer.log('Deep link: blog_post/$contentIdentifier', name: 'AppRouter'); FlutterNativeSplash.remove(); return '/blog-post-screen?id=$contentIdentifier'; case 'plan': developer.log('Deep link: plan/$contentIdentifier', name: 'AppRouter'); FlutterNativeSplash.remove(); return '/about-plan-screen?slug=$contentIdentifier'; case 'diet': { final dietId = int.tryParse(contentIdentifier); if (dietId != null) { developer.log('Deep link: diet/id=$dietId', name: 'AppRouter'); FlutterNativeSplash.remove(); return '/about-diet-screen?id=$dietId'; } developer.log('Deep link: diet/slug=$contentIdentifier', name: 'AppRouter'); FlutterNativeSplash.remove(); return '/about-diet-screen?slug=$contentIdentifier'; } case 'chat': developer.log('Deep link: chat/$contentIdentifier', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.chatSelectScreen; case 'profile': developer.log('Deep link: profile/$contentIdentifier', name: 'AppRouter'); FlutterNativeSplash.remove(); return '/profile-screen?slug=$contentIdentifier'; case 'diary': developer.log('Deep link: diary/$contentIdentifier', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.diaryScreen; case 'consultant_diary_comment': developer.log('Deep link: consultant_diary_comment/$contentIdentifier → diary', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.diaryScreen; } } if (effectiveSegments.length == 1) { if (type == 'chat') { developer.log('Deep link: chat', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.chatSelectScreen; } if (type == 'diary') { developer.log('Deep link: diary', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.diaryScreen; } if (type == 'badge') { developer.log('Deep link: badge → my-profile tab', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.myProfileScreen; } } developer.log('Unknown deep link format: ${effectiveSegments.length} effective segments, type=$type, redirecting to feed', name: 'AppRouter'); FlutterNativeSplash.remove(); return RouteConstants.feedScreen; } static String? _handleRedirect(BuildContext context, GoRouterState state) { return _resolveDeepLinkUri(state.uri); } static void navigateByDeepLinkUrl(String url) { if (url.isEmpty) { developer.log('navigateByDeepLinkUrl: empty url, redirecting to feed', name: 'AppRouter'); appRouter.go(RouteConstants.feedScreen); return; } Uri? uri; try { uri = Uri.tryParse(url); } catch (e) { developer.log('navigateByDeepLinkUrl: failed to parse url=$url, error=$e, redirecting to feed', name: 'AppRouter'); appRouter.go(RouteConstants.feedScreen); return; } if (uri == null) { developer.log('navigateByDeepLinkUrl: uri is null for url=$url, redirecting to feed', name: 'AppRouter'); appRouter.go(RouteConstants.feedScreen); return; } if (uri.host.isEmpty && uri.pathSegments.isNotEmpty) { uri = Uri( scheme: 'https', host: deepLinkHosts.first, pathSegments: uri.pathSegments, queryParameters: uri.queryParameters.isEmpty ? null : uri.queryParameters, ); } final target = _resolveDeepLinkUri(uri); if (target != null) { appRouter.go(target); } else { developer.log('navigateByDeepLinkUrl: unresolved url=$url (normalized=${uri.toString()}), redirecting to feed', name: 'AppRouter'); appRouter.go(RouteConstants.feedScreen); } } static void navigateToRegisterViaSocial({required AuthResultEntity authResult, required String provider}) { appRouter.pushNamed('registerViaSocialScreen', extra: {'authResult': authResult, 'provider': provider}); } }