import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_quill/flutter_quill.dart' as flutter_quill; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:app_links/app_links.dart'; import 'config/firebase_web_config.dart'; import 'services/notification_service.dart'; import 'services/auth_service.dart'; import 'services/secure_chat_service.dart'; import 'services/simple_e2ee_service.dart'; import 'services/sync_manager.dart'; import 'package:google_fonts/google_fonts.dart'; import 'theme/app_theme.dart'; import 'providers/theme_provider.dart' as theme_provider; import 'providers/auth_provider.dart'; import 'routes/app_routes.dart'; Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp(); } void main() async { WidgetsFlutterBinding.ensureInitialized(); if (kIsWeb) { await Firebase.initializeApp(options: FirebaseWebConfig.options); } else { await Firebase.initializeApp(); } FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); GoogleFonts.pendingFonts([ GoogleFonts.notoColorEmoji(), ]); runApp( const ProviderScope( child: sojornApp(), ), ); } class sojornApp extends ConsumerStatefulWidget { const sojornApp({super.key}); @override ConsumerState createState() => _sojornAppState(); } class _sojornAppState extends ConsumerState { late AppLinks _appLinks; StreamSubscription? _linkSub; StreamSubscription? _authSub; late final AuthService _authService = AuthService(); SyncManager? _syncManager; @override void initState() { super.initState(); _initDeepLinks(); _initE2ee(); _initNotifications(); _listenForAuth(); _initSyncManagerIfAuthenticated(); } @override void dispose() { _linkSub?.cancel(); _authSub?.cancel(); _notificationSub?.cancel(); _syncManager?.dispose(); super.dispose(); } Future _initDeepLinks() async { _appLinks = AppLinks(); try { final initialUri = await _appLinks.getInitialLink(); if (initialUri != null) { _handleUri(initialUri); } } catch (_) {} _linkSub = _appLinks.uriLinkStream.listen( _handleUri, onError: (_) {}, ); } void _handleUri(Uri uri) { if (uri.scheme != 'sojorn') return; if (uri.host == 'beacon') { final lat = double.tryParse(uri.queryParameters['lat'] ?? ''); final long = double.tryParse(uri.queryParameters['long'] ?? ''); if (lat != null && long != null) { AppRoutes.router.go( '/beacon?lat=${lat.toStringAsFixed(6)}&long=${long.toStringAsFixed(6)}', ); } } else if (uri.host == 'verified') { ref.read(emailVerifiedEventProvider.notifier).state = true; if (_authService.isAuthenticated) { _authService.refreshSession(); } } } void _initE2ee() { if (_authService.isAuthenticated) { SimpleE2EEService().initialize(); } } void _initNotifications() { if (_authService.isAuthenticated) { NotificationService.instance.init(); _listenForNotifications(); } } void _listenForAuth() { _authSub = _authService.authStateChanges.listen((data) { if (data.event == AuthChangeEvent.signedIn || data.event == AuthChangeEvent.tokenRefreshed) { SimpleE2EEService().initialize(); NotificationService.instance.init(); _ensureSyncManager(); } else if (data.event == AuthChangeEvent.signedOut) { _syncManager?.dispose(); _syncManager = null; } }); } void _initSyncManagerIfAuthenticated() { if (_authService.isAuthenticated) { _ensureSyncManager(); } } void _ensureSyncManager() { if (_syncManager != null) return; _syncManager = SyncManager( secureChatService: SecureChatService.instance, authService: _authService, ); _syncManager!.init(); } StreamSubscription? _notificationSub; void _listenForNotifications() { _notificationSub?.cancel(); _notificationSub = NotificationService.instance.foregroundMessages.listen((message) { final context = AppRoutes.rootNavigatorKey.currentContext; if (context != null) { NotificationService.instance.showNotificationBanner(context, message); } }); } @override Widget build(BuildContext context) { final themeMode = ref.watch(theme_provider.themeProvider); AppTheme.setThemeType(themeMode == theme_provider.ThemeMode.pop ? AppThemeType.pop : AppThemeType.basic); return MaterialApp.router( title: 'sojorn', theme: AppTheme.lightTheme, debugShowCheckedModeBanner: false, localizationsDelegates: const [ GlobalWidgetsLocalizations.delegate, GlobalMaterialLocalizations.delegate, flutter_quill.FlutterQuillLocalizations.delegate, ], routerConfig: AppRoutes.router, ); } }