import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'theme/app_theme.dart'; import 'screens/login_screen.dart'; import 'screens/home_shell.dart'; // Re-export so other files can `import '../main.dart'` for these. export 'package:supabase_flutter/supabase_flutter.dart' show AuthException, UserAttributes; // ── Supabase credentials ────────────────────────────────────────────── const _supabaseUrl = 'https://yaopcyubateifnicpywp.supabase.co'; const _supabaseAnonKey = 'sb_publishable_a7czIl7-TGeBJvid9z2XZA_3ElImliL'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await Supabase.initialize( url: _supabaseUrl, anonKey: _supabaseAnonKey, ); runApp(const HWHubApp()); } /// Convenience accessor used throughout the app. final supabase = Supabase.instance.client; /// Global keys so dialogs & snackbars survive widget-tree rebuilds. final navigatorKey = GlobalKey(); final scaffoldMessengerKey = GlobalKey(); /// Show a snackbar safely through the global key. void showGlobalSnackBar(String message, {bool isError = false}) { scaffoldMessengerKey.currentState?.showSnackBar( SnackBar( content: Text(message), backgroundColor: isError ? Colors.red : null, ), ); } /// Show a dialog safely through the global navigator key. Future showGlobalDialog({required WidgetBuilder builder}) { return showDialog( context: navigatorKey.currentContext!, builder: builder, ); } // ── Root App Widget ─────────────────────────────────────────────────── class HWHubApp extends StatelessWidget { const HWHubApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'HW Collector Hub', debugShowCheckedModeBanner: false, navigatorKey: navigatorKey, scaffoldMessengerKey: scaffoldMessengerKey, theme: AppTheme.light, darkTheme: AppTheme.dark, themeMode: ThemeMode.system, home: const AuthGate(), ); } } // ── Auth Gate ───────────────────────────────────────────────────────── class AuthGate extends StatefulWidget { const AuthGate({super.key}); @override State createState() => _AuthGateState(); } class _AuthGateState extends State { bool _isLoading = true; Session? _session; @override void initState() { super.initState(); _session = supabase.auth.currentSession; supabase.auth.onAuthStateChange.listen( (AuthState authState) { if (!mounted) return; final wasLoggedIn = _session != null; final isLoggedIn = authState.session != null; _session = authState.session; if (wasLoggedIn != isLoggedIn) { setState(() {}); } if (authState.event == AuthChangeEvent.passwordRecovery) { _showResetPasswordDialog(); } }, onError: (error) { showGlobalSnackBar('Auth error: $error', isError: true); }, ); setState(() => _isLoading = false); } Future _showResetPasswordDialog() async { await showDialog( context: navigatorKey.currentContext!, barrierDismissible: false, builder: (_) => const _ResetPasswordDialog(), ); } @override Widget build(BuildContext context) { if (_isLoading) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } return _session != null ? const HomeShell() : const LoginScreen(); } } // ── Reset Password Dialog ───────────────────────────────────────────── class _ResetPasswordDialog extends StatefulWidget { const _ResetPasswordDialog(); @override State<_ResetPasswordDialog> createState() => _ResetPasswordDialogState(); } class _ResetPasswordDialogState extends State<_ResetPasswordDialog> { final _passwordController = TextEditingController(); bool _isSaving = false; @override void dispose() { _passwordController.dispose(); super.dispose(); } Future _save() async { final newPassword = _passwordController.text.trim(); if (newPassword.length < 6) { showGlobalSnackBar('Password must be at least 6 characters.'); return; } setState(() => _isSaving = true); try { await supabase.auth.updateUser( UserAttributes(password: newPassword), ); if (!mounted) return; Navigator.of(context).pop(); showGlobalSnackBar('Password updated successfully!'); } on AuthException catch (e) { if (!mounted) return; setState(() => _isSaving = false); showGlobalSnackBar(e.message, isError: true); } } @override Widget build(BuildContext context) { return AlertDialog( title: const Text('Set New Password'), content: TextField( controller: _passwordController, obscureText: true, decoration: const InputDecoration(labelText: 'New password'), onSubmitted: (_) => _save(), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel'), ), ElevatedButton( onPressed: _isSaving ? null : _save, child: _isSaving ? const SizedBox( height: 18, width: 18, child: CircularProgressIndicator(strokeWidth: 2), ) : const Text('Save'), ), ], ); } }