import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'dart:async'; import '../../services/notification_service.dart'; import '../../services/user_service.dart'; import '../../services/auth_service.dart'; import '../../services/post_service.dart'; import '../../services/app_lifecycle_service.dart'; import '../../models/post_models.dart'; import '../../widgets/posts_list.dart'; import '../../utils/password_generator.dart'; import '../edit_profile_screen.dart'; import '../support_screen.dart'; import '../information_screen.dart'; class ProfilePage extends StatefulWidget { @override _ProfilePageState createState() => _ProfilePageState(); } class _ProfilePageState extends State { String? fcmToken; final TextEditingController _tokenController = TextEditingController(); bool isLoading = false; Map? userData; bool isLoadingUser = true; int totalLikes = 0; int totalPosts = 0; StreamSubscription>? _userStreamSubscription; @override void initState() { super.initState(); _loadFCMToken(); _loadUserData(); _loadUserStats(); _setupUserStream(); } @override void dispose() { _tokenController.dispose(); _userStreamSubscription?.cancel(); super.dispose(); } void _setupUserStream() { print('Setting up user profile stream'); _userStreamSubscription = UserService.getUserStream().listen( (updatedUserData) { print('📱 User profile stream received data update'); if (mounted) { // Update UI directly with stream data instead of making API call setState(() { userData = updatedUserData; isLoadingUser = false; }); // Only reload stats which are from posts service _loadUserStats(); } }, onError: (error) { print('User profile stream error: $error'); }, ); // Trigger stream with any existing cached data Future.delayed(Duration(milliseconds: 100), () { if (mounted) { UserService.notifyStreamWithCurrentData(); } }); } Future _loadFCMToken() async { setState(() { isLoading = true; }); try { final token = await NotificationService().getToken(); setState(() { fcmToken = token; _tokenController.text = token ?? 'Token not available'; isLoading = false; }); } catch (e) { setState(() { fcmToken = null; _tokenController.text = 'Error loading token: $e'; isLoading = false; }); } } Future _loadUserData({bool forceRefresh = false}) async { // Don't show loading for cached data unless forcing refresh final isInitialLoad = userData == null; if (isInitialLoad || forceRefresh) { setState(() { isLoadingUser = true; }); } final result = await UserService.getCurrentUser(forceRefresh: forceRefresh); if (mounted) { setState(() { if (result['success'] == true) { userData = result['data']; } else { userData = null; if (isInitialLoad) { _showErrorAlert(result['message'] ?? 'Failed to load user data'); } } isLoadingUser = false; }); } } Future _loadUserStats() async { final result = await PostService.getUserPosts(); if (mounted) { setState(() { if (result['success'] == true) { final posts = result['posts'] as List; totalPosts = posts.length; totalLikes = posts.fold(0, (sum, post) => sum + post.likes); } else { totalPosts = 0; totalLikes = 0; } }); } } void _showErrorAlert(String message) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Error'), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text('OK', style: TextStyle(color: Color(0xFF6A4C93))), ), ], ), ); } Future _copyToClipboard() async { if (fcmToken != null && fcmToken!.isNotEmpty) { await Clipboard.setData(ClipboardData(text: fcmToken!)); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('FCM Token copied to clipboard'), backgroundColor: Color(0xFF6A4C93), ), ); } } void _navigateToSettings() { Navigator.of( context, ).push(MaterialPageRoute(builder: (context) => SettingsPage())); } void _navigateToEditProfile() async { final result = await Navigator.of(context).push( MaterialPageRoute( builder: (context) => EditProfileScreen(userData: userData), ), ); if (result == true) { // Profile was updated, refresh user data _loadUserData(forceRefresh: true); } } @override Widget build(BuildContext context) { return PopScope( canPop: false, // Prevent back navigation from profile page onPopInvoked: (bool didPop) { // Prevent any pop behavior, including iOS back gesture if (!didPop) { // Do nothing - stay on profile page } }, child: Scaffold( appBar: AppBar( title: Text('Profile', style: TextStyle(fontWeight: FontWeight.w600)), backgroundColor: Colors.white, foregroundColor: Color(0xFF6A4C93), elevation: 0, centerTitle: true, actions: [ if (userData != null) IconButton( onPressed: _navigateToEditProfile, icon: Icon(Icons.edit), tooltip: 'Edit Profile', ), IconButton( onPressed: _navigateToSettings, icon: Icon(Icons.settings), ), ], bottom: PreferredSize( preferredSize: Size.fromHeight(1), child: Container(height: 1, color: Colors.grey[200]), ), automaticallyImplyLeading: false, ), body: SingleChildScrollView( child: Column( children: [ Container( padding: EdgeInsets.all(24), child: Column( children: [ if (isLoadingUser) CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( Color(0xFF6A4C93), ), ) else if (userData != null) ...[ CircleAvatar( radius: 50, backgroundColor: Color(0xFF6A4C93), child: Text( (userData!['displayName'] ?? 'U') .substring(0, 1) .toUpperCase(), style: TextStyle( color: Colors.white, fontSize: 36, fontWeight: FontWeight.bold, ), ), ), SizedBox(height: 16), Text( userData!['displayName'] ?? 'Unknown User', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87, ), ), SizedBox(height: 4), Text( '@${userData!['username'] ?? 'unknown'}', style: TextStyle(fontSize: 16, color: Colors.grey[600]), ), ] else ...[ Icon( Icons.error_outline, size: 50, color: Colors.grey[400], ), SizedBox(height: 16), Text( 'Failed to load user data', style: TextStyle(fontSize: 18, color: Colors.grey[600]), ), SizedBox(height: 8), ElevatedButton( onPressed: _loadUserData, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, ), child: Text('Retry'), ), ], SizedBox(height: 20), if (userData != null) Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Column( children: [ Text( '$totalPosts', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Color(0xFF6A4C93), ), ), Text( 'Posts', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ), Column( children: [ Text( '$totalLikes', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Color(0xFF6A4C93), ), ), Text( 'Likes Received', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ), ], ), ], ), ), if (userData != null) ...[ Container( height: 1, color: Colors.grey[200], margin: EdgeInsets.symmetric(horizontal: 16), ), SizedBox(height: 16), ProfilePostsList( fetchPosts: () => PostService.getUserPosts(), onStatsUpdate: (posts, likes) { setState(() { totalPosts = posts; totalLikes = likes; }); }, ), ], ], ), ), ), ); } } class SettingsPage extends StatefulWidget { @override _SettingsPageState createState() => _SettingsPageState(); } class _SettingsPageState extends State { // Admin user creation Map? userData; bool isLoadingUser = true; bool isCreatingUser = false; final TextEditingController _emailController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); final TextEditingController _displayNameController = TextEditingController(); @override void initState() { super.initState(); _loadUserData(); _generatePassword(); } @override void dispose() { _emailController.dispose(); _passwordController.dispose(); _displayNameController.dispose(); super.dispose(); } Future _loadUserData() async { setState(() { isLoadingUser = true; }); final result = await UserService.getCurrentUser(); setState(() { isLoadingUser = false; if (result['success'] == true) { userData = result['data']; } else { userData = null; } }); } void _generatePassword() { _passwordController.text = PasswordGenerator.generateReadablePassword(); } bool get _isAdmin { return userData?['role'] == 'ADMIN'; } Future> _createUserAccount() async { if (_emailController.text.trim().isEmpty || _displayNameController.text.trim().isEmpty) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Please fill in all fields'), backgroundColor: Colors.red, ), ); return {'success': false, 'message': 'Please fill in all fields'}; } setState(() { isCreatingUser = true; }); try { final result = await UserService.createUser( email: _emailController.text.trim(), password: _passwordController.text, displayName: _displayNameController.text.trim(), ); setState(() { isCreatingUser = false; }); return result; } catch (e) { setState(() { isCreatingUser = false; }); return {'success': false, 'message': 'Error creating user: $e'}; } } void _signOut() async { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text('Sign Out'), content: Text('Are you sure you want to sign out?'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text('Cancel'), ), TextButton( onPressed: () async { Navigator.of(context).pop(); // Stop polling services before logout AppLifecycleService.dispose(); await AuthService.logout(); Navigator.of( context, ).pushNamedAndRemoveUntil('/', (route) => false); }, child: Text('Sign Out', style: TextStyle(color: Colors.red)), ), ], ); }, ); } void _showCredentialsDialog(String email, String password) { final credentialsText = 'Email: $email\nPassword: $password'; final TextEditingController credentialsController = TextEditingController( text: credentialsText, ); showDialog( context: context, builder: (BuildContext dialogContext) { return AlertDialog( title: Text('Account Created Successfully!'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Share these credentials with the user:', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), ), SizedBox(height: 16), TextField( controller: credentialsController, decoration: InputDecoration( border: OutlineInputBorder(), suffixIcon: IconButton( icon: Icon(Icons.copy), onPressed: () async { await Clipboard.setData( ClipboardData(text: credentialsText), ); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Credentials copied to clipboard'), backgroundColor: Color(0xFF6A4C93), ), ); }, tooltip: 'Copy credentials', ), ), maxLines: 2, readOnly: true, style: TextStyle(fontFamily: 'monospace', fontSize: 14), ), ], ), actions: [ ElevatedButton( onPressed: () => Navigator.of(dialogContext).pop(), style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, ), child: Text('Done'), ), ], ); }, ); } void _showCreateAccountDialog(BuildContext context) { showDialog( context: context, builder: (BuildContext dialogContext) { return StatefulBuilder( builder: (context, setDialogState) { return AlertDialog( title: Text('Create Account'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: _emailController, decoration: InputDecoration( labelText: 'Email', border: OutlineInputBorder(), prefixIcon: Icon(Icons.email), ), keyboardType: TextInputType.emailAddress, ), SizedBox(height: 16), TextField( controller: _displayNameController, decoration: InputDecoration( labelText: 'Display Name', border: OutlineInputBorder(), prefixIcon: Icon(Icons.person), ), ), SizedBox(height: 16), TextField( controller: _passwordController, decoration: InputDecoration( labelText: 'Password', border: OutlineInputBorder(), prefixIcon: Icon(Icons.lock), suffixIcon: IconButton( icon: Icon(Icons.refresh), onPressed: () { setDialogState(() { _generatePassword(); }); }, tooltip: 'Generate new password', ), ), readOnly: true, style: TextStyle( fontFamily: 'monospace', fontWeight: FontWeight.bold, ), ), SizedBox(height: 8), Text( 'Password is auto-generated for security', style: TextStyle(fontSize: 12, color: Colors.grey[600]), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(dialogContext).pop(), child: Text('Cancel'), ), ElevatedButton( onPressed: isCreatingUser ? null : () async { final email = _emailController.text.trim(); final password = _passwordController.text; final result = await _createUserAccount(); if (mounted) { Navigator.of(dialogContext).pop(); if (result['success']) { _showCredentialsDialog(email, password); // Clear form _emailController.clear(); _displayNameController.clear(); _generatePassword(); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( result['message'] ?? 'Failed to create user', ), backgroundColor: Colors.red, ), ); } } }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, ), child: isCreatingUser ? SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( Colors.white, ), ), ) : Text('Create'), ), ], ); }, ); }, ); } @override Widget build(BuildContext context) { return PopScope( canPop: true, // Allow back navigation to go back to profile page only child: Scaffold( appBar: AppBar( title: Text('Settings', style: TextStyle(fontWeight: FontWeight.w600)), backgroundColor: Colors.white, foregroundColor: Color(0xFF6A4C93), elevation: 0, bottom: PreferredSize( preferredSize: Size.fromHeight(1), child: Container(height: 1, color: Colors.grey[200]), ), automaticallyImplyLeading: true, ), body: Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (!isLoadingUser && _isAdmin) ...[ Text( 'Admin Tools', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w600, color: Color(0xFF6A4C93), ), ), SizedBox(height: 16), Container( width: double.infinity, height: 56, child: ElevatedButton.icon( onPressed: () => _showCreateAccountDialog(context), icon: Icon(Icons.person_add, size: 20), label: Text('Create an Account'), style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: EdgeInsets.symmetric(horizontal: 16), ), ), ), SizedBox(height: 32), ], Text( 'App Information', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w600, color: Color(0xFF6A4C93), ), ), SizedBox(height: 16), Container( width: double.infinity, height: 56, child: ElevatedButton.icon( onPressed: () => Navigator.of(context).push( MaterialPageRoute(builder: (context) => SupportScreen()), ), icon: Icon(Icons.help_outline, size: 20), label: Text('Support'), style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: EdgeInsets.symmetric(horizontal: 16), ), ), ), SizedBox(height: 16), Container( width: double.infinity, height: 56, child: ElevatedButton.icon( onPressed: () => Navigator.of(context).push( MaterialPageRoute(builder: (context) => InformationScreen()), ), icon: Icon(Icons.info_outline, size: 20), label: Text('Information'), style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: EdgeInsets.symmetric(horizontal: 16), ), ), ), SizedBox(height: 32), Container( width: double.infinity, height: 56, child: ElevatedButton.icon( onPressed: () => Navigator.of(context).push( MaterialPageRoute( builder: (context) => EditProfileScreen(userData: userData), ), ), icon: Icon(Icons.edit, size: 20), label: Text('Update your Profile'), style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: EdgeInsets.symmetric(horizontal: 16), ), ), ), SizedBox(height: 16), Container( width: double.infinity, height: 56, child: ElevatedButton.icon( onPressed: _signOut, icon: Icon(Icons.logout, color: Colors.red, size: 20), label: Text('Sign Out', style: TextStyle(color: Colors.red)), style: ElevatedButton.styleFrom( backgroundColor: Colors.white, side: BorderSide(color: Colors.red), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: EdgeInsets.symmetric(horizontal: 16), ), ), ), ], ), ), ), ); } }