import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:tugas_akhir_supabase/services/session_manager.dart'; import 'package:tugas_akhir_supabase/widgets/session_expired_dialog.dart'; /// A widget that enforces session validation for authenticated routes. /// /// This widget should wrap any screen that requires authentication. /// It will automatically check if the session is valid and redirect to login /// if the session has expired. class SessionGuardWrapper extends StatefulWidget { final Widget child; final bool enforceAuthentication; const SessionGuardWrapper({ Key? key, required this.child, this.enforceAuthentication = true, }) : super(key: key); @override State createState() => _SessionGuardWrapperState(); } class _SessionGuardWrapperState extends State { bool _isCheckingSession = false; bool _sessionExpired = false; bool _showingDialog = false; @override void initState() { super.initState(); // Check session on widget initialization if (widget.enforceAuthentication) { _checkSession(); } } @override void didChangeDependencies() { super.didChangeDependencies(); // Check session when dependencies change (e.g., after navigation) if (widget.enforceAuthentication && !_isCheckingSession) { _checkSession(); } } Future _checkSession() async { // Skip check if not enforcing authentication if (!widget.enforceAuthentication) return; // Skip check if already checking if (_isCheckingSession) return; // Skip check if no user is logged in final currentUser = Supabase.instance.client.auth.currentUser; if (currentUser == null) return; _isCheckingSession = true; try { // Check if session is already marked as expired if (SessionManager.isExpired) { if (mounted && !_sessionExpired) { setState(() { _sessionExpired = true; }); _showExpiredDialog(); } _isCheckingSession = false; return; } // Check session validity final isValid = await SessionManager.isSessionValid(); if (mounted && !isValid && !_sessionExpired) { setState(() { _sessionExpired = true; }); _showExpiredDialog(); } } catch (e) { debugPrint('SessionGuard: Error checking session - $e'); } finally { _isCheckingSession = false; } } void _showExpiredDialog() { if (_showingDialog) return; _showingDialog = true; // Show dialog on next frame to avoid build phase issues WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) { _showingDialog = false; return; } showDialog( context: context, barrierDismissible: false, builder: (context) => const SessionExpiredDialog(), ).then((_) { _showingDialog = false; }); }); } @override Widget build(BuildContext context) { // If not enforcing authentication, just show the child if (!widget.enforceAuthentication) { return widget.child; } // If session is expired, show a restricted UI if (_sessionExpired) { return Material( child: Stack( children: [ // Blur the background content Opacity( opacity: 0.3, child: AbsorbPointer( child: widget.child, ), ), // Show a loading indicator if dialog is not showing yet if (!_showingDialog) const Center( child: CircularProgressIndicator(), ), ], ), ); } // Session is valid, show the child with a gesture detector to update activity return GestureDetector( onTap: () => SessionManager.updateLastUserInteraction(), onPanDown: (_) => SessionManager.updateLastUserInteraction(), behavior: HitTestBehavior.translucent, child: widget.child, ); } }