import 'package:flutter/material.dart'; import 'wordle_page.dart'; import '../../services/puzzle_service.dart'; import '../../services/user_service.dart'; import '../../models/puzzle_models.dart'; class PuzzlesPage extends StatefulWidget { const PuzzlesPage({Key? key}) : super(key: key); @override _PuzzlesPageState createState() => _PuzzlesPageState(); } class _PuzzlesPageState extends State { String _currentView = 'main'; // main, leaderboard DailyChallenge? _dailyChallenge; bool _isLoading = false; String? _errorMessage; void _showLeaderboardView() { setState(() { _currentView = 'leaderboard'; }); } void _showMainView() { setState(() { _currentView = 'main'; }); } @override void initState() { super.initState(); _loadDailyChallenge(); } Future _loadDailyChallenge() async { setState(() { _isLoading = true; _errorMessage = null; }); try { final challenge = await PuzzleService.getDailyChallenge(); setState(() { _dailyChallenge = challenge; _isLoading = false; }); } catch (e) { setState(() { _errorMessage = 'Failed to load daily challenge'; _isLoading = false; }); } } void _startGame() async { if (_dailyChallenge == null) { _loadDailyChallenge(); return; } Navigator.push( context, MaterialPageRoute( builder: (context) => WordlePage( targetWord: _dailyChallenge!.wordle, dailyChallenge: _dailyChallenge!, ), ), ).then((_) { // Refresh daily challenge when returning from game _loadDailyChallenge(); }); } @override Widget build(BuildContext context) { if (_currentView == 'leaderboard') { return _buildLeaderboardView(); } return _buildMainView(); } Widget _buildMainView() { return Scaffold( backgroundColor: Colors.white, body: SafeArea( child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFF32B0A5), Color(0xFF4600B9)], ), ), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // App Logo Text( 'وصال', style: TextStyle( fontSize: 72, fontWeight: FontWeight.w200, fontFamily: 'Blaka', color: Colors.white, ), ), SizedBox(height: 40), // Daily Challenge Title Text( 'Daily Challenge', style: TextStyle( fontSize: 36, fontWeight: FontWeight.bold, color: Colors.white, ), ), SizedBox(height: 16), // Challenge timing info Container( padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(20), ), child: Text( 'Daily Wordle Challenge Open Until 11 AM', style: TextStyle( fontSize: 16, color: Colors.white, fontWeight: FontWeight.w500, ), ), ), SizedBox(height: 60), // Big gamified PLAY button GestureDetector( onTap: _isLoading ? null : _startGame, child: Container( padding: EdgeInsets.symmetric(horizontal: 40, vertical: 20), decoration: BoxDecoration( gradient: LinearGradient( colors: [Colors.white, Colors.white.withOpacity(0.9)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(25), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 20, offset: Offset(0, 8), ), BoxShadow( color: Colors.white.withOpacity(0.5), blurRadius: 10, offset: Offset(-5, -5), ), ], border: Border.all( color: Color(0xFF6A4C93).withOpacity(0.2), width: 2, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.sports_esports, size: 32, color: Color(0xFF6A4C93), ), SizedBox(width: 12), _isLoading ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( Color(0xFF6A4C93), ), ), ) : Text( _dailyChallenge?.attempted == true ? 'VIEW RESULTS' : 'PLAY NOW', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Color(0xFF6A4C93), letterSpacing: 1.5, ), ), SizedBox(width: 8), Icon( Icons.arrow_forward_ios, size: 20, color: Color(0xFF6A4C93), ), ], ), ), ), // Show error message if any if (_errorMessage != null) ...[ SizedBox(height: 20), Container( padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), decoration: BoxDecoration( color: Colors.red.withOpacity(0.2), borderRadius: BorderRadius.circular(20), ), child: Text( _errorMessage!, style: TextStyle( fontSize: 14, color: Colors.white, fontWeight: FontWeight.w500, ), ), ), ], // Show completion status if attempted if (_dailyChallenge?.attempted == true) ...[ SizedBox(height: 20), Container( padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), decoration: BoxDecoration( color: (_dailyChallenge!.solved ? Colors.green : Colors.orange).withOpacity(0.2), borderRadius: BorderRadius.circular(20), ), child: Text( _dailyChallenge!.solved ? 'You solved this at ${PuzzleService.formatSolveTime(_dailyChallenge!.solveTime!)} in ${_dailyChallenge!.attempts} attempts!' : 'You attempted this challenge but didn\'t solve it in ${_dailyChallenge!.attempts} attempts', style: TextStyle( fontSize: 14, color: Colors.white, fontWeight: FontWeight.w500, ), textAlign: TextAlign.center, ), ), ], SizedBox(height: 40), // Leaderboard button GestureDetector( onTap: _showLeaderboardView, child: Container( padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(30), border: Border.all(color: Colors.white, width: 2), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.leaderboard, color: Colors.white, size: 24), SizedBox(width: 8), Text( 'Leaderboard', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white, ), ), ], ), ), ), ], ), ), ), ), ); } Widget _buildLeaderboardView() { return Scaffold( backgroundColor: Colors.white, appBar: AppBar( backgroundColor: Colors.white, elevation: 0, leading: IconButton( icon: Icon(Icons.arrow_back, color: Color(0xFF6A4C93)), onPressed: _showMainView, ), centerTitle: true, title: Row( mainAxisSize: MainAxisSize.min, children: [ Text( 'وصال', style: TextStyle( fontSize: 28, fontWeight: FontWeight.w200, fontFamily: 'Blaka', color: Color(0xFF6A4C93), ), ), SizedBox(width: 8), Text( 'Leaderboard', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, color: Colors.grey[600], ), ), ], ), bottom: PreferredSize( preferredSize: Size.fromHeight(1), child: Container(height: 1, color: Colors.grey[200]), ), ), body: _LeaderboardContent(), ); } } // Wrapper class for Leaderboard content without the scaffold class _LeaderboardContent extends StatefulWidget { @override _LeaderboardContentState createState() => _LeaderboardContentState(); } class _LeaderboardContentState extends State<_LeaderboardContent> { List? _leaderboardData; bool _isLoading = true; String? _errorMessage; String? _currentUsername; @override void initState() { super.initState(); _loadLeaderboard(); } Future _loadLeaderboard() async { setState(() { _isLoading = true; _errorMessage = null; }); try { // Get current user and leaderboard data in parallel final results = await Future.wait([ UserService.getCurrentUser(), PuzzleService.getLeaderboard(), ]); final userResponse = results[0] as Map; final leaderboardResponse = results[1] as LeaderboardResponse; setState(() { _isLoading = false; if (userResponse['success'] == true && userResponse['data'] != null) { _currentUsername = userResponse['data']['username']; } if (leaderboardResponse.status) { _leaderboardData = leaderboardResponse.data; } else { _errorMessage = leaderboardResponse.message ?? 'Failed to load leaderboard'; } }); } catch (e) { setState(() { _isLoading = false; _errorMessage = 'Error loading leaderboard'; }); } } @override Widget build(BuildContext context) { if (_isLoading) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Color(0xFF6A4C93)), ), SizedBox(height: 16), Text( 'Loading leaderboard...', style: TextStyle( fontSize: 16, color: Colors.grey[600], ), ), ], ), ); } if (_errorMessage != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, size: 64, color: Colors.grey[400], ), SizedBox(height: 16), Text( _errorMessage!, style: TextStyle( fontSize: 16, color: Colors.grey[600], ), textAlign: TextAlign.center, ), SizedBox(height: 16), ElevatedButton( onPressed: _loadLeaderboard, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, ), child: Text('Try Again'), ), ], ), ); } if (_leaderboardData == null || _leaderboardData!.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.emoji_events_outlined, size: 64, color: Colors.grey[400], ), SizedBox(height: 16), Text( 'No one solved the puzzle today.', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.grey[600], ), ), SizedBox(height: 8), Text( 'Be the first one!', style: TextStyle( fontSize: 16, color: Colors.grey[500], ), ), ], ), ); } // Sort leaderboard by time (fastest first) final sortedData = List.from(_leaderboardData!); sortedData.sort((a, b) => a.submittedAt.compareTo(b.submittedAt)); return Column( children: [ // Leaderboard list Expanded( child: ListView.builder( padding: EdgeInsets.all(16), itemCount: sortedData.length, itemBuilder: (context, index) { final entry = sortedData[index]; final rank = index + 1; final isCurrentUser = entry.username == _currentUsername; // Format time display final time = entry.submittedAt; final timeFormatted = '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}:${time.second.toString().padLeft(2, '0')}'; // Determine rank styling Color rankColor = Colors.grey[600]!; IconData? rankIcon; if (rank == 1) { rankColor = Color(0xFFFFD700); // Gold rankIcon = Icons.workspace_premium; } else if (rank == 2) { rankColor = Color(0xFFC0C0C0); // Silver rankIcon = Icons.workspace_premium; } else if (rank == 3) { rankColor = Color(0xFFCD7F32); // Bronze rankIcon = Icons.workspace_premium; } return Container( margin: EdgeInsets.only(bottom: 12), padding: EdgeInsets.all(16), decoration: BoxDecoration( color: isCurrentUser ? Color(0xFF6A4C93).withOpacity(0.05) : Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 8, offset: Offset(0, 2), ), ], border: isCurrentUser ? Border.all(color: Color(0xFF6A4C93), width: 2) : rank <= 3 ? Border.all(color: rankColor.withOpacity(0.3), width: 2) : Border.all(color: Colors.grey[200]!, width: 1), ), child: Row( children: [ // Rank indicator Container( width: 40, child: Column( children: [ if (rankIcon != null) Icon(rankIcon, color: rankColor, size: 24) else Text( '#$rank', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: rankColor, ), ), ], ), ), SizedBox(width: 16), // User avatar CircleAvatar( radius: 25, backgroundColor: Color(0xFF6A4C93).withOpacity(0.1), backgroundImage: entry.avatar != null && entry.avatar!.isNotEmpty ? NetworkImage(entry.avatar!) : null, child: entry.avatar == null || entry.avatar!.isEmpty ? Text( entry.displayName.isNotEmpty ? entry.displayName[0].toUpperCase() : '?', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Color(0xFF6A4C93), ), ) : null, ), SizedBox(width: 16), // User details Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( entry.displayName, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black87, ), ), if (isCurrentUser) ...[ SizedBox(width: 8), Container( padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Color(0xFF6A4C93), borderRadius: BorderRadius.circular(8), ), child: Text( 'YOU', style: TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), ], ], ), SizedBox(height: 4), Row( children: [ Icon( Icons.access_time, size: 16, color: Colors.grey[600], ), SizedBox(width: 4), Text( timeFormatted, style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), SizedBox(width: 16), Icon( Icons.try_sms_star, size: 16, color: Colors.grey[600], ), SizedBox(width: 4), Text( '${entry.attempts} attempts', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), ), ], ), ], ), ), ], ), ); }, ), ), ], ); } } enum LetterState { empty, absent, present, correct }