1import 'package:flutter/foundation.dart'; 2import 'package:flutter/material.dart'; 3import 'package:flutter/services.dart'; 4import 'package:go_router/go_router.dart'; 5import 'package:provider/provider.dart'; 6 7import 'config/oauth_config.dart'; 8import 'providers/auth_provider.dart'; 9import 'providers/feed_provider.dart'; 10import 'providers/vote_provider.dart'; 11import 'screens/auth/login_screen.dart'; 12import 'screens/home/main_shell_screen.dart'; 13import 'screens/landing_screen.dart'; 14import 'services/vote_service.dart'; 15 16void main() async { 17 WidgetsFlutterBinding.ensureInitialized(); 18 19 // Set system UI overlay style (Android navigation bar) 20 SystemChrome.setSystemUIOverlayStyle( 21 const SystemUiOverlayStyle( 22 systemNavigationBarColor: Color(0xFF0B0F14), 23 systemNavigationBarIconBrightness: Brightness.light, 24 ), 25 ); 26 27 // Initialize auth provider 28 final authProvider = AuthProvider(); 29 await authProvider.initialize(); 30 31 // Initialize vote service with auth callbacks for direct PDS writes 32 // Uses DPoP authentication (not Bearer tokens!) 33 final voteService = VoteService( 34 sessionGetter: () async => authProvider.session, 35 didGetter: () => authProvider.did, 36 pdsUrlGetter: authProvider.getPdsUrl, 37 ); 38 39 runApp( 40 MultiProvider( 41 providers: [ 42 ChangeNotifierProvider.value(value: authProvider), 43 ChangeNotifierProvider( 44 create: (_) => VoteProvider( 45 voteService: voteService, 46 authProvider: authProvider, 47 ), 48 ), 49 ChangeNotifierProxyProvider2<AuthProvider, VoteProvider, FeedProvider>( 50 create: (context) => FeedProvider( 51 authProvider, 52 voteProvider: context.read<VoteProvider>(), 53 voteService: voteService, 54 ), 55 update: (context, auth, vote, previous) { 56 // Reuse existing provider to maintain state across rebuilds 57 return previous ?? 58 FeedProvider( 59 auth, 60 voteProvider: vote, 61 voteService: voteService, 62 ); 63 }, 64 ), 65 ], 66 child: const CovesApp(), 67 ), 68 ); 69} 70 71class CovesApp extends StatelessWidget { 72 const CovesApp({super.key}); 73 74 @override 75 Widget build(BuildContext context) { 76 final authProvider = Provider.of<AuthProvider>(context, listen: false); 77 78 return MaterialApp.router( 79 title: 'Coves', 80 theme: ThemeData( 81 colorScheme: ColorScheme.fromSeed( 82 seedColor: const Color(0xFFFF6B35), 83 brightness: Brightness.dark, 84 ), 85 useMaterial3: true, 86 ), 87 routerConfig: _createRouter(authProvider), 88 debugShowCheckedModeBanner: false, 89 ); 90 } 91} 92 93// GoRouter configuration factory 94GoRouter _createRouter(AuthProvider authProvider) { 95 return GoRouter( 96 routes: [ 97 GoRoute(path: '/', builder: (context, state) => const LandingScreen()), 98 GoRoute(path: '/login', builder: (context, state) => const LoginScreen()), 99 GoRoute( 100 path: '/feed', 101 builder: (context, state) => const MainShellScreen(), 102 ), 103 ], 104 refreshListenable: authProvider, 105 redirect: (context, state) { 106 final isAuthenticated = authProvider.isAuthenticated; 107 final isLoading = authProvider.isLoading; 108 final currentPath = state.uri.path; 109 110 // Don't redirect while loading initial auth state 111 if (isLoading) { 112 return null; 113 } 114 115 // If authenticated and on landing/login screen, redirect to feed 116 if (isAuthenticated && (currentPath == '/' || currentPath == '/login')) { 117 if (kDebugMode) { 118 print('🔄 User authenticated, redirecting to /feed'); 119 } 120 return '/feed'; 121 } 122 123 // Allow anonymous users to access /feed for browsing 124 // Sign-out redirect is handled explicitly in the sign-out action 125 return null; 126 }, 127 errorBuilder: (context, state) { 128 // Check if this is an OAuth callback 129 if (state.uri.scheme == OAuthConfig.customScheme) { 130 if (kDebugMode) { 131 print( 132 '⚠️ OAuth callback in errorBuilder - ' 133 'flutter_web_auth_2 should handle it', 134 ); 135 print(' URI: ${state.uri}'); 136 } 137 // Return nothing - just stay on current screen 138 // flutter_web_auth_2 will process the callback at native level 139 return const SizedBox.shrink(); 140 } 141 142 // For other errors, show landing page 143 if (kDebugMode) { 144 print('⚠️ Router error: ${state.uri}'); 145 } 146 return const LandingScreen(); 147 }, 148 ); 149}