import 'dart:async'; import 'dart:io'; // import 'package:firebase_core/firebase_core.dart'; // import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:appsflyer_sdk/appsflyer_sdk.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:app_links/app_links.dart'; import 'package:uae_stat/config/connectivity_provider.dart' as legacy_connectivity; import 'package:uae_stat/config/injector.dart'; import 'package:uae_stat/lib/core/routing/app_router.dart'; import 'package:uae_stat/lib/core/theme/app_theme.dart'; import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import 'package:uae_stat/l10n/app_localizations.dart'; Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); } void main() async { WidgetsFlutterBinding.ensureInitialized(); await Hive.initFlutter(); if (!Hive.isBoxOpen('indicator_cache')) { await Hive.openBox('indicator_cache'); } if (kIsWeb) { await Firebase.initializeApp( options: const FirebaseOptions( apiKey: 'AIzaSyB-QQbnq366f7rrKLVVFJKnSZgR9LcDWFI', appId: '1:534109861443:android:25d267f0c986b2042f46f6', messagingSenderId: '534109861443', projectId: 'fcsc-uae-stats-9cb1e', ), ); } else { await Firebase.initializeApp(); } FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); await FirebaseMessaging.instance.requestPermission( alert: true, badge: true, sound: true, ); FirebaseMessaging.onMessage.listen((RemoteMessage _) {}); FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage _) {}); // final themeProvider = ThemeProvider(); // themeProvider.setUserId("USER_ID_FROM_LOGIN"); // set this properly // await themeProvider.loadThemeFromPocketBase(); // // Request permission for notifications // NotificationSettings settings = await messaging.requestPermission( // alert: true, // badge: true, // sound: true, // ); // configureDependencies(); runApp( const ProviderScope( child: MainApp(), ), ); } // Future triggerLocalNetworkAccess() async { // try { // await Socket.connect('127.0.0.1', 9).timeout(const Duration(seconds: 1)); // } catch (e) { // // Ignore errors – this is just to trigger the local network prompt // } // } class MainApp extends ConsumerStatefulWidget { const MainApp({super.key}); @override ConsumerState createState() => _MainAppState(); } class _MainAppState extends ConsumerState { late AppsflyerSdk appsflyerSdk; bool _listenerAttached = false; String? pendingDeepLink; Map? pendingDeepLinkData; StreamSubscription? _appLinkSubscription; Timer? _oneLinkFallbackTimer; @override void initState() { super.initState(); _initAppsFlyer(); _initUaePassDeepLinks(); } @override void dispose() { _appLinkSubscription?.cancel(); _oneLinkFallbackTimer?.cancel(); super.dispose(); } /// Handle deep links from AppsFlyer / custom URL schemes (no auth). void _initUaePassDeepLinks() { void handleIncomingLink(Uri? uri) { if (uri == null) return; // AppsFlyer OneLink fallback: fcscappstates://callback?deep_link_value=... if (uri.scheme == 'fcscappstates') { final deepLinkValue = uri.queryParameters['deep_link_value']; if (deepLinkValue != null && deepLinkValue.isNotEmpty) { _oneLinkFallbackTimer?.cancel(); final path = deepLinkValue.startsWith('/') ? deepLinkValue : '/$deepLinkValue'; appRouter.go('/?intendedPath=${Uri.encodeComponent(path)}'); } else { // Wait briefly for AppsFlyer SDK callback, then fallback to app root. _oneLinkFallbackTimer?.cancel(); _oneLinkFallbackTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) appRouter.go('/'); }); } return; } // AppsFlyer OneLink universal link can arrive directly as https://fcscstats.onelink.me/... // Handle it as a fallback in case SDK callback is delayed/missed in internal builds. if (uri.scheme == 'https' && uri.host == 'fcscstats.onelink.me') { final deepLinkValue = uri.queryParameters['deep_link_value']; if (deepLinkValue != null && deepLinkValue.isNotEmpty) { _oneLinkFallbackTimer?.cancel(); final path = deepLinkValue.startsWith('/') ? deepLinkValue : '/$deepLinkValue'; appRouter.go('/?intendedPath=${Uri.encodeComponent(path)}'); } else { // Some OneLink opens arrive without query params; AppsFlyer SDK usually // delivers deep_link_value shortly after. Delay root fallback to avoid // overriding the real deep-link route. _oneLinkFallbackTimer?.cancel(); _oneLinkFallbackTimer = Timer(const Duration(milliseconds: 1500), () { if (mounted) appRouter.go('/'); }); } return; } } WidgetsBinding.instance.addPostFrameCallback((_) async { final appLinks = AppLinks(); final initial = await appLinks.getInitialLink(); handleIncomingLink(initial); _appLinkSubscription = appLinks.uriLinkStream.listen((uri) { handleIncomingLink(uri); }); }); } _initAppsFlyer() async { // AppsFlyer dev key; OneLink template in share uses https://fcscstats.onelink.me/V6mR // In AppsFlyer dashboard (OneLink template V6mR): set Android package ae.gov.fcsc.stats, // iOS App ID (numeric from App Store Connect), and store fallback URLs to avoid app_not_found: // Android: https://play.google.com/store/apps/details?id=ae.gov.fcsc.stats // iOS: https://apps.apple.com/app/id1234567890 (replace with real App Store ID when live) final options = Platform.isIOS ? AppsFlyerOptions( afDevKey: 'K2unWqVxgJo84PDFVLiebC', appId: '6744919453', // Apple numeric App ID from App Store Connect (replace for production) showDebug: true, ) : AppsFlyerOptions( afDevKey: 'K2unWqVxgJo84PDFVLiebC', showDebug: true, ); appsflyerSdk = AppsflyerSdk(options); await appsflyerSdk.initSdk( registerOnDeepLinkingCallback: true, registerConversionDataCallback: true, ); appsflyerSdk.startSDK(); // await appsflyerSdk.setDebugLog(true); // Direct Deep Link (App already installed) appsflyerSdk.onDeepLinking((DeepLinkResult result) { if (result.status == Status.FOUND && result.deepLink != null) { final deepLinkValue = result.deepLink!.deepLinkValue; final data = result.deepLink!.clickEvent; _handleDeepLink(deepLinkValue, data); } }); // Deferred Deep Link (First install case) appsflyerSdk.onInstallConversionData((data) { if (data['deep_link_value'] != null) { _handleDeepLink( data['deep_link_value'], data, ); } }); } void _handleDeepLink(String? deepLinkValue, Map data) { _oneLinkFallbackTimer?.cancel(); String? resolved = deepLinkValue; // AppsFlyer may not always populate deepLinkValue for complex links. // Fallback to clickEvent payload keys. resolved ??= data['deep_link_value']?.toString(); resolved ??= data['af_dp']?.toString(); resolved = resolved?.trim(); if (resolved == null || resolved.isEmpty) return; // If AppsFlyer gives a full URL, preserve path+query only. if (resolved.startsWith('http://') || resolved.startsWith('https://')) { final parsed = Uri.tryParse(resolved); if (parsed != null) { resolved = parsed.path + (parsed.hasQuery ? '?${parsed.query}' : ''); } } // Decode once so encoded query paths like // %2FpopulationGrowth%2Fgdp%3FbgColor%3D... become route-friendly. try { resolved = Uri.decodeComponent(resolved); } catch (_) {} // Use full path as intendedPath so shared links open the right screen. final String intendedPath = resolved!.startsWith('/') ? resolved : '/$resolved'; context.go('/?intendedPath=${Uri.encodeComponent(intendedPath)}'); } @override Widget build(BuildContext context) { final locale = ref.watch(localeProvider); // final connectivityProvider = Provider((ref) { // return true; // Replace with actual logic // }); // // ref.listen(connectivityProvider, (previous, hasInternet) { // print('Previous Internet Status: $previous'); // print('Current Internet Status: $hasInternet'); // if (previous != null && hasInternet != previous) { // handleConnectivityChange(context, hasInternet); // } // }); /// ✅ SAFE PLACE FOR ref.listen if (!_listenerAttached) { _listenerAttached = true; ref.listen>(legacy_connectivity.connectivityProvider, ( previous, next, ) { final hasInternet = next.valueOrNull; if (hasInternet == null) return; handleConnectivityChange(hasInternet); }); } return MaterialApp.router( debugShowCheckedModeBanner: false, routerConfig: appRouter, locale: locale, theme: AppTheme.light, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, ); } void handleConnectivityChange(bool hasInternet) { final currentPath = GoRouter.of(context).routerDelegate.currentConfiguration.fullPath; if (!hasInternet && currentPath != AppRoutes.internetCheck) { context.go(AppRoutes.internetCheck); } } }