diff --git a/lib/ui/home/dialog.dart b/lib/ui/home/dialog.dart index 0291d45..2b6f403 100644 --- a/lib/ui/home/dialog.dart +++ b/lib/ui/home/dialog.dart @@ -122,7 +122,7 @@ class QuestsDialog extends StatelessWidget { // 2. DIALOGO CLASSIFICA (LEADERBOARD) CON CALLBACK SFIDA // =========================================================================== class LeaderboardDialog extends StatelessWidget { - final Function(String uid, String name)? onChallenge; // <-- Aggiunto Callback per inviare i dati alla HomeScreen + final Function(String uid, String name)? onChallenge; const LeaderboardDialog({super.key, this.onChallenge}); @@ -165,7 +165,8 @@ class LeaderboardDialog extends StatelessWidget { final filteredDocs = rawDocs.where((doc) { var data = doc.data() as Map; String name = (data['name'] ?? '').toString().toUpperCase(); - return name != 'PAOLO'; + // Nascondiamo PIPPO dalla classifica + return name != 'PIPPO'; }).toList(); if (filteredDocs.isEmpty) { @@ -224,7 +225,6 @@ class LeaderboardDialog extends StatelessWidget { themeType: themeType, onTap: () { Navigator.pop(context); - // Chiama la funzione passata dalla HomeScreen! if (onChallenge != null) { onChallenge!(doc.id, playerName); } diff --git a/lib/ui/home/home_modals.dart b/lib/ui/home/home_modals.dart index c01922f..21a5270 100644 --- a/lib/ui/home/home_modals.dart +++ b/lib/ui/home/home_modals.dart @@ -19,7 +19,6 @@ import '../../l10n/app_localizations.dart'; import '../../widgets/painters.dart'; import '../../widgets/cyber_border.dart'; import '../game/game_screen.dart'; -import '../multiplayer/lobby_screen.dart'; import '../multiplayer/lobby_widgets.dart'; class HomeModals { @@ -62,25 +61,16 @@ class HomeModals { final fakeEmail = "${name.toLowerCase().replaceAll(' ', '')}@tetraq.game"; final currentUser = FirebaseAuth.instance.currentUser; - // CATTURIAMO IL FANTASMA: Se siamo in un account anonimo provvisorio, ci salviamo il suo ID final ghostUid = (currentUser != null && currentUser.isAnonymous) ? currentUser.uid : null; try { if (isLogin) { - // ========================================== - // 1. TENTA IL LOGIN UFFICIALE - // ========================================== - - // --- LA MOSSA DEL NINJA: ELIMINIAMO IL GHOST PRIMA DEL LOGIN --- - // Lo eliminiamo ORA perché abbiamo ancora i permessi dell'utente anonimo. if (ghostUid != null) { await FirebaseFirestore.instance.collection('leaderboard').doc(ghostUid).delete().catchError((e) => null); } - // Ora effettuiamo l'accesso await FirebaseAuth.instance.signInWithEmailAndPassword(email: fakeEmail, password: password); - // Recupera i dati storici dal Cloud per sovrascrivere quelli in locale final doc = await FirebaseFirestore.instance.collection('leaderboard').doc(FirebaseAuth.instance.currentUser!.uid).get(); if (doc.exists) { final data = doc.data() as Map; @@ -95,22 +85,16 @@ class HomeModals { } } else { - // ========================================== - // 2. TENTA LA REGISTRAZIONE / UPGRADE - // ========================================== if (currentUser != null && currentUser.isAnonymous) { - // L'utente aveva un account anonimo, lo promuoviamo ad account con password final credential = EmailAuthProvider.credential(email: fakeEmail, password: password); await currentUser.linkWithCredential(credential); if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Profilo Cloud protetto con successo!"), backgroundColor: Colors.green)); } else { - // Se per caso si trova senza nessun account, ne creiamo uno nuovo await FirebaseAuth.instance.createUserWithEmailAndPassword(email: fakeEmail, password: password); if (context.mounted) ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Account creato con successo!"), backgroundColor: Colors.green)); } } - // Salva il nome e lancia il sync await StorageService.instance.savePlayerName(name); StorageService.instance.syncLeaderboard(); @@ -123,7 +107,6 @@ class HomeModals { msg = "Nome già registrato!\nSe sei tu, clicca su ACCEDI."; } else if (e.code == 'user-not-found' || e.code == 'wrong-password' || e.code == 'invalid-credential') { msg = "Nome o Password errati!"; - // Siccome avevamo cancellato il ghost prima, se il login fallisce lo ricreiamo per non perdere i dati locali if (isLogin && ghostUid != null) StorageService.instance.syncLeaderboard(); } else if (e.code == 'requires-recent-login') { msg = "Errore di sessione. Riavvia l'app."; @@ -534,7 +517,6 @@ class HomeModals { ), const SizedBox(height: 25), Divider(color: inkColor.withOpacity(0.3), thickness: 2.5), const SizedBox(height: 20), - // TEMPO È ORA ESCLUSIVO PER IL MULTIPLAYER Text("TEMPO", style: getSharedTextStyle(themeType, TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: inkColor.withOpacity(0.6), letterSpacing: 1.5))), const SizedBox(height: 10), Row( children: [ diff --git a/lib/ui/home/home_screen.dart b/lib/ui/home/home_screen.dart index 94df23e..2feff8f 100644 --- a/lib/ui/home/home_screen.dart +++ b/lib/ui/home/home_screen.dart @@ -106,7 +106,6 @@ class _HomeScreenState extends State with WidgetsBindingObserver { _checkClipboardForInvite(); _listenToFavoritesOnline(); } else if (state == AppLifecycleState.detached) { - // --- FIX BUG WHATSAPP: Rimossa l'eliminazione della stanza durante lo stato "paused" --- _cleanupGhostRoom(); } } @@ -581,8 +580,8 @@ class _HomeScreenState extends State with WidgetsBindingObserver { onTap: () async { _debugTapCount++; - // CHEAT LOCALE VIVO SOLO IN DEBUG MODE - if (kDebugMode && playerName.toUpperCase() == 'PAOLO' && _debugTapCount == 5) { + // CHEAT LOCALE VIVO SOLO IN DEBUG MODE (ORA CON PIPPO!) + if (kDebugMode && playerName.toUpperCase() == 'PIPPO' && _debugTapCount == 5) { StorageService.instance.addXP(2000); setState(() {}); ScaffoldMessenger.of(context).showSnackBar( @@ -593,7 +592,7 @@ class _HomeScreenState extends State with WidgetsBindingObserver { else if (_debugTapCount >= 7) { _debugTapCount = 0; - if (kDebugMode && playerName.toUpperCase() == 'PAOLO') { + if (kDebugMode && playerName.toUpperCase() == 'PIPPO') { Navigator.push(context, MaterialPageRoute(builder: (_) => const AdminScreen())); } else { bool isAdmin = await StorageService.instance.isUserAdmin(); diff --git a/lib/ui/multiplayer/lobby_screen.dart b/lib/ui/multiplayer/lobby_screen.dart index 2e7e16b..77bcef0 100644 --- a/lib/ui/multiplayer/lobby_screen.dart +++ b/lib/ui/multiplayer/lobby_screen.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; -import 'package:tetraq/l10n/app_localizations.dart'; // <-- IMPORT DEL DIZIONARIO! +import 'package:tetraq/l10n/app_localizations.dart'; import '../../logic/game_controller.dart'; import '../../models/game_board.dart'; @@ -16,8 +16,8 @@ import '../../core/app_colors.dart'; import '../../services/multiplayer_service.dart'; import '../../services/storage_service.dart'; import '../game/game_screen.dart'; -import '../../widgets/painters.dart'; import '../../widgets/cyber_border.dart'; +import '../../widgets/painters.dart'; // <--- ECCO L'IMPORT MANCANTE! import 'lobby_widgets.dart'; class LobbyScreen extends StatefulWidget { @@ -45,7 +45,6 @@ class _LobbyScreenState extends State with WidgetsBindingObserver { String _timeModeSetting = 'fixed'; bool _isPublicRoom = true; - bool _roomStarted = false; @override @@ -220,17 +219,17 @@ class _LobbyScreenState extends State with WidgetsBindingObserver { showDialog( context: context, barrierDismissible: false, - builder: (context) { - final theme = context.watch().currentColors; - final themeType = context.read().currentThemeType; + builder: (dialogContext) { + final theme = dialogContext.watch().currentColors; + final themeType = dialogContext.read().currentThemeType; final loc = AppLocalizations.of(context)!; Widget dialogContent = Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator(color: theme.playerRed), const SizedBox(height: 25), - Text(loc.codeHint, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: theme.text.withOpacity(0.6), letterSpacing: 2))), - Text(code, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 40, fontWeight: FontWeight.w900, color: theme.playerRed, letterSpacing: 8, shadows: themeType == AppThemeType.doodle ? [] : [Shadow(color: theme.playerRed.withOpacity(0.5), blurRadius: 10)]))), + Text(loc.codeHint, style: getSharedTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: theme.text.withOpacity(0.6), letterSpacing: 2))), + Text(code, style: getSharedTextStyle(themeType, TextStyle(fontSize: 40, fontWeight: FontWeight.w900, color: theme.playerRed, letterSpacing: 8, shadows: themeType == AppThemeType.doodle ? [] : [Shadow(color: theme.playerRed.withOpacity(0.5), blurRadius: 10)]))), const SizedBox(height: 25), Transform.rotate( angle: themeType == AppThemeType.doodle ? 0.02 : 0, @@ -247,9 +246,9 @@ class _LobbyScreenState extends State with WidgetsBindingObserver { child: Column( children: [ Icon(_isPublicRoom ? Icons.podcasts : Icons.share, color: theme.playerBlue, size: 32), const SizedBox(height: 12), - Text(_isPublicRoom ? "Sei in Bacheca!" : "Condividi link", textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.w900, fontSize: 18))), + Text(_isPublicRoom ? "Sei in Bacheca!" : "Invito inviato", textAlign: TextAlign.center, style: getSharedTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.w900, fontSize: 18))), const SizedBox(height: 8), - Text(_isPublicRoom ? "Aspettiamo che uno sfidante si unisca dalla lobby pubblica." : "Condividi il codice. La partita inizierà appena si unirà.", textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.8), fontSize: 14, height: 1.5))), + Text(_isPublicRoom ? "Aspettiamo che uno sfidante si unisca dalla lobby pubblica." : "Attendi che il tuo amico accetti la sfida. Non chiudere questa finestra.", textAlign: TextAlign.center, style: getSharedTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.8), fontSize: 14, height: 1.5))), ], ), ), @@ -274,13 +273,13 @@ class _LobbyScreenState extends State with WidgetsBindingObserver { return StreamBuilder( stream: _multiplayerService.listenToRoom(code), - builder: (context, snapshot) { + builder: (ctx, snapshot) { if (snapshot.hasData && snapshot.data!.exists) { var data = snapshot.data!.data() as Map; if (data['status'] == 'playing') { _roomStarted = true; WidgetsBinding.instance.addPostFrameCallback((_) { - Navigator.pop(context); + Navigator.pop(ctx); context.read().startNewGame(_selectedRadius, isOnline: true, roomCode: code, isHost: true, shape: _selectedShape, timeMode: _timeModeSetting); Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => const GameScreen())); }); @@ -292,7 +291,7 @@ class _LobbyScreenState extends State with WidgetsBindingObserver { onPopInvoked: (didPop) { if (didPop) return; _cleanupGhostRoom(); - Navigator.pop(context); + Navigator.pop(ctx); }, child: Dialog( backgroundColor: Colors.transparent, @@ -305,9 +304,9 @@ class _LobbyScreenState extends State with WidgetsBindingObserver { TextButton( onPressed: () { _cleanupGhostRoom(); - Navigator.pop(context); + Navigator.pop(ctx); }, - child: Text(loc.btnCancel.toUpperCase(), style: getLobbyTextStyle(themeType, TextStyle(color: Colors.red, fontWeight: FontWeight.w900, fontSize: 20, letterSpacing: 2.0, shadows: themeType == AppThemeType.doodle ? [] : [const Shadow(color: Colors.black, blurRadius: 2)]))), + child: Text(loc.btnCancel.toUpperCase(), style: getSharedTextStyle(themeType, TextStyle(color: Colors.red, fontWeight: FontWeight.w900, fontSize: 20, letterSpacing: 2.0, shadows: themeType == AppThemeType.doodle ? [] : [const Shadow(color: Colors.black, blurRadius: 2)]))), ), ], ), @@ -350,7 +349,7 @@ class _LobbyScreenState extends State with WidgetsBindingObserver { final themeManager = context.watch(); final themeType = themeManager.currentThemeType; final theme = themeManager.currentColors; - final loc = AppLocalizations.of(context)!; // <-- CHIAMATA AL DIZIONARIO + final loc = AppLocalizations.of(context)!; String? bgImage; if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg'; @@ -731,4 +730,20 @@ class _LobbyScreenState extends State with WidgetsBindingObserver { ), ); } +} + +class FullScreenGridPainter extends CustomPainter { + final Color gridColor; + FullScreenGridPainter(this.gridColor); + + @override + void paint(Canvas canvas, Size size) { + final Paint paperGridPaint = Paint()..color = gridColor..strokeWidth = 1.0..style = PaintingStyle.stroke; + double paperStep = 20.0; + for (double i = 0; i <= size.width; i += paperStep) canvas.drawLine(Offset(i, 0), Offset(i, size.height), paperGridPaint); + for (double i = 0; i <= size.height; i += paperStep) canvas.drawLine(Offset(0, i), Offset(size.width, i), paperGridPaint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } \ No newline at end of file