Auto-sync: 20260321_000000

This commit is contained in:
Paolo 2026-03-21 00:00:01 +01:00
parent 209027b221
commit 5b99d5f0bb
18 changed files with 619 additions and 133 deletions

BIN
.DS_Store vendored

Binary file not shown.

View file

@ -5,31 +5,31 @@
<application <application
android:label="tetraq" android:label="tetraq"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
<activity android:usesCleartextTraffic="true"> <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:taskAffinity="" android:taskAffinity=""
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<meta-data <meta-data
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"
/> />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tetraq" android:host="join" /> <data android:scheme="tetraq" android:host="join" />
</intent-filter> </intent-filter>
</activity> </activity>
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />

View file

@ -2,61 +2,67 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Tetraq</string> <string>Tetraq</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>tetraq</string> <string>tetraq</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string> <string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
<array> <array>
<dict> <dict>
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Editor</string> <string>Editor</string>
<key>CFBundleURLName</key> <key>CFBundleURLName</key>
<string>com.sanza.tetraq</string> <string>com.sanza.tetraq</string>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>tetraq</string> <string>tetraq</string>
</array> </array>
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string> <string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
</dict> </dict>
</plist> </plist>

View file

@ -19,5 +19,18 @@
"joinMatch": "JOIN", "joinMatch": "JOIN",
"gameOver": "GAME OVER", "gameOver": "GAME OVER",
"mainMenu": "BACK TO MENU", "mainMenu": "BACK TO MENU",
"exit": "EXIT" "exit": "EXIT",
"roomSettings": "ROOM SETTINGS",
"arenaShape": "ARENA SHAPE",
"arenaSize": "SIZE",
"timeAndOptions": "TIME & OPTIONS",
"timeLabel": "TIME",
"btnStart": "START",
"btnCancel": "CANCEL",
"wordOr": "OR",
"codeHint": "CODE",
"publicLobbyTitle": "PUBLIC LOBBY",
"emptyLobbyMsg": "No public rooms right now.\nCreate one!",
"roomOf": "Room of",
"btnEnter": "ENTER"
} }

View file

@ -19,5 +19,18 @@
"joinMatch": "UNISCITI", "joinMatch": "UNISCITI",
"gameOver": "FINE PARTITA", "gameOver": "FINE PARTITA",
"mainMenu": "TORNA AL MENU", "mainMenu": "TORNA AL MENU",
"exit": "ESCI" "exit": "ESCI",
"roomSettings": "IMPOSTAZIONI STANZA",
"arenaShape": "FORMA ARENA",
"arenaSize": "GRANDEZZA",
"timeAndOptions": "TEMPO E OPZIONI",
"timeLabel": "TEMPO",
"btnStart": "AVVIA",
"btnCancel": "ANNULLA",
"wordOr": "OPPURE",
"codeHint": "CODICE",
"publicLobbyTitle": "LOBBY PUBBLICA",
"emptyLobbyMsg": "Nessuna stanza pubblica al momento.\nCreane una tu!",
"roomOf": "Stanza di",
"btnEnter": "ENTRA"
} }

View file

@ -229,6 +229,84 @@ abstract class AppLocalizations {
/// In it, this message translates to: /// In it, this message translates to:
/// **'ESCI'** /// **'ESCI'**
String get exit; String get exit;
/// No description provided for @roomSettings.
///
/// In it, this message translates to:
/// **'IMPOSTAZIONI STANZA'**
String get roomSettings;
/// No description provided for @arenaShape.
///
/// In it, this message translates to:
/// **'FORMA ARENA'**
String get arenaShape;
/// No description provided for @arenaSize.
///
/// In it, this message translates to:
/// **'GRANDEZZA'**
String get arenaSize;
/// No description provided for @timeAndOptions.
///
/// In it, this message translates to:
/// **'TEMPO E OPZIONI'**
String get timeAndOptions;
/// No description provided for @timeLabel.
///
/// In it, this message translates to:
/// **'TEMPO'**
String get timeLabel;
/// No description provided for @btnStart.
///
/// In it, this message translates to:
/// **'AVVIA'**
String get btnStart;
/// No description provided for @btnCancel.
///
/// In it, this message translates to:
/// **'ANNULLA'**
String get btnCancel;
/// No description provided for @wordOr.
///
/// In it, this message translates to:
/// **'OPPURE'**
String get wordOr;
/// No description provided for @codeHint.
///
/// In it, this message translates to:
/// **'CODICE'**
String get codeHint;
/// No description provided for @publicLobbyTitle.
///
/// In it, this message translates to:
/// **'LOBBY PUBBLICA'**
String get publicLobbyTitle;
/// No description provided for @emptyLobbyMsg.
///
/// In it, this message translates to:
/// **'Nessuna stanza pubblica al momento.\nCreane una tu!'**
String get emptyLobbyMsg;
/// No description provided for @roomOf.
///
/// In it, this message translates to:
/// **'Stanza di'**
String get roomOf;
/// No description provided for @btnEnter.
///
/// In it, this message translates to:
/// **'ENTRA'**
String get btnEnter;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

View file

@ -67,4 +67,44 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get exit => 'BEENDEN'; String get exit => 'BEENDEN';
@override
String get roomSettings => 'IMPOSTAZIONI STANZA';
@override
String get arenaShape => 'FORMA ARENA';
@override
String get arenaSize => 'GRANDEZZA';
@override
String get timeAndOptions => 'TEMPO E OPZIONI';
@override
String get timeLabel => 'TEMPO';
@override
String get btnStart => 'AVVIA';
@override
String get btnCancel => 'ANNULLA';
@override
String get wordOr => 'OPPURE';
@override
String get codeHint => 'CODICE';
@override
String get publicLobbyTitle => 'LOBBY PUBBLICA';
@override
String get emptyLobbyMsg =>
'Nessuna stanza pubblica al momento.\nCreane una tu!';
@override
String get roomOf => 'Stanza di';
@override
String get btnEnter => 'ENTRA';
} }

View file

@ -67,4 +67,43 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get exit => 'EXIT'; String get exit => 'EXIT';
@override
String get roomSettings => 'ROOM SETTINGS';
@override
String get arenaShape => 'ARENA SHAPE';
@override
String get arenaSize => 'SIZE';
@override
String get timeAndOptions => 'TIME & OPTIONS';
@override
String get timeLabel => 'TIME';
@override
String get btnStart => 'START';
@override
String get btnCancel => 'CANCEL';
@override
String get wordOr => 'OR';
@override
String get codeHint => 'CODE';
@override
String get publicLobbyTitle => 'PUBLIC LOBBY';
@override
String get emptyLobbyMsg => 'No public rooms right now.\nCreate one!';
@override
String get roomOf => 'Room of';
@override
String get btnEnter => 'ENTER';
} }

View file

@ -67,4 +67,44 @@ class AppLocalizationsEs extends AppLocalizations {
@override @override
String get exit => 'SALIR'; String get exit => 'SALIR';
@override
String get roomSettings => 'IMPOSTAZIONI STANZA';
@override
String get arenaShape => 'FORMA ARENA';
@override
String get arenaSize => 'GRANDEZZA';
@override
String get timeAndOptions => 'TEMPO E OPZIONI';
@override
String get timeLabel => 'TEMPO';
@override
String get btnStart => 'AVVIA';
@override
String get btnCancel => 'ANNULLA';
@override
String get wordOr => 'OPPURE';
@override
String get codeHint => 'CODICE';
@override
String get publicLobbyTitle => 'LOBBY PUBBLICA';
@override
String get emptyLobbyMsg =>
'Nessuna stanza pubblica al momento.\nCreane una tu!';
@override
String get roomOf => 'Stanza di';
@override
String get btnEnter => 'ENTRA';
} }

View file

@ -67,4 +67,44 @@ class AppLocalizationsFr extends AppLocalizations {
@override @override
String get exit => 'QUITTER'; String get exit => 'QUITTER';
@override
String get roomSettings => 'IMPOSTAZIONI STANZA';
@override
String get arenaShape => 'FORMA ARENA';
@override
String get arenaSize => 'GRANDEZZA';
@override
String get timeAndOptions => 'TEMPO E OPZIONI';
@override
String get timeLabel => 'TEMPO';
@override
String get btnStart => 'AVVIA';
@override
String get btnCancel => 'ANNULLA';
@override
String get wordOr => 'OPPURE';
@override
String get codeHint => 'CODICE';
@override
String get publicLobbyTitle => 'LOBBY PUBBLICA';
@override
String get emptyLobbyMsg =>
'Nessuna stanza pubblica al momento.\nCreane una tu!';
@override
String get roomOf => 'Stanza di';
@override
String get btnEnter => 'ENTRA';
} }

View file

@ -67,4 +67,44 @@ class AppLocalizationsIt extends AppLocalizations {
@override @override
String get exit => 'ESCI'; String get exit => 'ESCI';
@override
String get roomSettings => 'IMPOSTAZIONI STANZA';
@override
String get arenaShape => 'FORMA ARENA';
@override
String get arenaSize => 'GRANDEZZA';
@override
String get timeAndOptions => 'TEMPO E OPZIONI';
@override
String get timeLabel => 'TEMPO';
@override
String get btnStart => 'AVVIA';
@override
String get btnCancel => 'ANNULLA';
@override
String get wordOr => 'OPPURE';
@override
String get codeHint => 'CODICE';
@override
String get publicLobbyTitle => 'LOBBY PUBBLICA';
@override
String get emptyLobbyMsg =>
'Nessuna stanza pubblica al momento.\nCreane una tu!';
@override
String get roomOf => 'Stanza di';
@override
String get btnEnter => 'ENTRA';
} }

View file

@ -67,4 +67,44 @@ class AppLocalizationsPt extends AppLocalizations {
@override @override
String get exit => 'SAIR'; String get exit => 'SAIR';
@override
String get roomSettings => 'IMPOSTAZIONI STANZA';
@override
String get arenaShape => 'FORMA ARENA';
@override
String get arenaSize => 'GRANDEZZA';
@override
String get timeAndOptions => 'TEMPO E OPZIONI';
@override
String get timeLabel => 'TEMPO';
@override
String get btnStart => 'AVVIA';
@override
String get btnCancel => 'ANNULLA';
@override
String get wordOr => 'OPPURE';
@override
String get codeHint => 'CODICE';
@override
String get publicLobbyTitle => 'LOBBY PUBBLICA';
@override
String get emptyLobbyMsg =>
'Nessuna stanza pubblica al momento.\nCreane una tu!';
@override
String get roomOf => 'Stanza di';
@override
String get btnEnter => 'ENTRA';
} }

View file

@ -67,4 +67,44 @@ class AppLocalizationsRu extends AppLocalizations {
@override @override
String get exit => 'ВЫХОД'; String get exit => 'ВЫХОД';
@override
String get roomSettings => 'IMPOSTAZIONI STANZA';
@override
String get arenaShape => 'FORMA ARENA';
@override
String get arenaSize => 'GRANDEZZA';
@override
String get timeAndOptions => 'TEMPO E OPZIONI';
@override
String get timeLabel => 'TEMPO';
@override
String get btnStart => 'AVVIA';
@override
String get btnCancel => 'ANNULLA';
@override
String get wordOr => 'OPPURE';
@override
String get codeHint => 'CODICE';
@override
String get publicLobbyTitle => 'LOBBY PUBBLICA';
@override
String get emptyLobbyMsg =>
'Nessuna stanza pubblica al momento.\nCreane una tu!';
@override
String get roomOf => 'Stanza di';
@override
String get btnEnter => 'ENTRA';
} }

View file

@ -67,4 +67,44 @@ class AppLocalizationsZh extends AppLocalizations {
@override @override
String get exit => '退出'; String get exit => '退出';
@override
String get roomSettings => 'IMPOSTAZIONI STANZA';
@override
String get arenaShape => 'FORMA ARENA';
@override
String get arenaSize => 'GRANDEZZA';
@override
String get timeAndOptions => 'TEMPO E OPZIONI';
@override
String get timeLabel => 'TEMPO';
@override
String get btnStart => 'AVVIA';
@override
String get btnCancel => 'ANNULLA';
@override
String get wordOr => 'OPPURE';
@override
String get codeHint => 'CODICE';
@override
String get publicLobbyTitle => 'LOBBY PUBBLICA';
@override
String get emptyLobbyMsg =>
'Nessuna stanza pubblica al momento.\nCreane una tu!';
@override
String get roomOf => 'Stanza di';
@override
String get btnEnter => 'ENTRA';
} }

View file

@ -64,21 +64,48 @@ class StorageService {
int get totalXP => _prefs.getInt('totalXP') ?? 0; int get totalXP => _prefs.getInt('totalXP') ?? 0;
// --- SICUREZZA XP: Inviamo solo INCREMENTI al server ---
Future<void> addXP(int xp) async { Future<void> addXP(int xp) async {
// Aggiorniamo il locale per la UI
await _prefs.setInt('totalXP', totalXP + xp); await _prefs.setInt('totalXP', totalXP + xp);
syncLeaderboard();
// Aggiorniamo il server in modo sicuro tramite incremento relativo
final user = FirebaseAuth.instance.currentUser;
if (user != null) {
await FirebaseFirestore.instance.collection('leaderboard').doc(user.uid).set({
'xp': FieldValue.increment(xp),
'level': playerLevel,
}, SetOptions(merge: true));
}
} }
int get playerLevel => (totalXP / 100).floor() + 1; int get playerLevel => (totalXP / 100).floor() + 1;
int get wins => _prefs.getInt('wins') ?? 0; int get wins => _prefs.getInt('wins') ?? 0;
// --- SICUREZZA WINS: Inviamo solo INCREMENTI al server ---
Future<void> addWin() async { Future<void> addWin() async {
await _prefs.setInt('wins', wins + 1); await _prefs.setInt('wins', wins + 1);
syncLeaderboard(); final user = FirebaseAuth.instance.currentUser;
if (user != null) {
await FirebaseFirestore.instance.collection('leaderboard').doc(user.uid).set({
'wins': FieldValue.increment(1),
}, SetOptions(merge: true));
}
} }
int get losses => _prefs.getInt('losses') ?? 0; int get losses => _prefs.getInt('losses') ?? 0;
Future<void> addLoss() async => await _prefs.setInt('losses', losses + 1);
// --- SICUREZZA LOSSES: Inviamo solo INCREMENTI al server ---
Future<void> addLoss() async {
await _prefs.setInt('losses', losses + 1);
final user = FirebaseAuth.instance.currentUser;
if (user != null) {
await FirebaseFirestore.instance.collection('leaderboard').doc(user.uid).set({
'losses': FieldValue.increment(1),
}, SetOptions(merge: true));
}
}
int get cpuLevel => _prefs.getInt('cpuLevel') ?? 1; int get cpuLevel => _prefs.getInt('cpuLevel') ?? 1;
Future<void> saveCpuLevel(int level) async => await _prefs.setInt('cpuLevel', level); Future<void> saveCpuLevel(int level) async => await _prefs.setInt('cpuLevel', level);
@ -89,30 +116,25 @@ class StorageService {
syncLeaderboard(); syncLeaderboard();
} }
// --- SINCRONIZZAZIONE LEADERBOARD AGGIORNATA ---
Future<void> syncLeaderboard() async { Future<void> syncLeaderboard() async {
try { try {
final user = FirebaseAuth.instance.currentUser; final user = FirebaseAuth.instance.currentUser;
// BLOCCO TOTALE: Se non sei loggato, niente database!
if (user == null) return; if (user == null) return;
String name = playerName; String name = playerName;
if (name.isEmpty) name = "GIOCATORE"; // Fallback di sicurezza if (name.isEmpty) name = "GIOCATORE";
String targetUid = user.uid; String targetUid = user.uid;
// Prepara i dati base // --- SICUREZZA: Non inviamo PIÙ i valori assoluti di xp, wins e losses! ---
// Vengono aggiornati solo dagli incrementi protetti nelle funzioni sopra.
Map<String, dynamic> dataToSave = { Map<String, dynamic> dataToSave = {
'name': name, 'name': name,
'xp': totalXP,
'level': playerLevel, 'level': playerLevel,
'wins': wins,
'losses': losses,
'lastActive': FieldValue.serverTimestamp(), 'lastActive': FieldValue.serverTimestamp(),
}; };
// IL TRUCCO: Aggiungiamo la data di registrazione estraendola da Firebase Auth!
if (user.metadata.creationTime != null) { if (user.metadata.creationTime != null) {
dataToSave['accountCreated'] = Timestamp.fromDate(user.metadata.creationTime!); dataToSave['accountCreated'] = Timestamp.fromDate(user.metadata.creationTime!);
} }
@ -124,6 +146,19 @@ class StorageService {
} }
} }
Future<bool> isUserAdmin() async {
try {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return false;
final doc = await FirebaseFirestore.instance.collection('admins').doc(user.uid).get();
return doc.exists;
} catch (e) {
debugPrint("Errore verifica admin: $e");
return false;
}
}
List<Map<String, String>> get favorites { List<Map<String, String>> get favorites {
List<String> favs = _prefs.getStringList('favorites') ?? []; List<String> favs = _prefs.getStringList('favorites') ?? [];
return favs.map((e) => Map<String, String>.from(jsonDecode(e))).toList(); return favs.map((e) => Map<String, String>.from(jsonDecode(e))).toList();

View file

@ -7,6 +7,7 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_auth/firebase_auth.dart';
@ -64,7 +65,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
// MODIFICA QUI: Invece di currentUser == null, controlliamo se il nome è vuoto!
if (StorageService.instance.playerName.isEmpty) { if (StorageService.instance.playerName.isEmpty) {
HomeModals.showNameDialog(context, () { HomeModals.showNameDialog(context, () {
StorageService.instance.syncLeaderboard(); StorageService.instance.syncLeaderboard();
@ -81,6 +81,7 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
_initDeepLinks(); _initDeepLinks();
_listenToFavoritesOnline(); _listenToFavoritesOnline();
} }
void _checkThemeSafety() { void _checkThemeSafety() {
String themeStr = StorageService.instance.getTheme(); String themeStr = StorageService.instance.getTheme();
bool exists = AppThemeType.values.any((e) => e.toString() == themeStr); bool exists = AppThemeType.values.any((e) => e.toString() == themeStr);
@ -104,7 +105,8 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
if (state == AppLifecycleState.resumed) { if (state == AppLifecycleState.resumed) {
_checkClipboardForInvite(); _checkClipboardForInvite();
_listenToFavoritesOnline(); _listenToFavoritesOnline();
} else if (state == AppLifecycleState.paused || state == AppLifecycleState.detached) { } else if (state == AppLifecycleState.detached) {
// --- FIX BUG WHATSAPP: Rimossa l'eliminazione della stanza durante lo stato "paused" ---
_cleanupGhostRoom(); _cleanupGhostRoom();
} }
} }
@ -198,7 +200,6 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
late OverlayEntry entry; late OverlayEntry entry;
bool removed = false; bool removed = false;
// --- FIX OVERLAP POPUP: Più in basso (85) ---
entry = OverlayEntry( entry = OverlayEntry(
builder: (context) => Positioned( builder: (context) => Positioned(
top: MediaQuery.of(context).padding.top + 85, top: MediaQuery.of(context).padding.top + 85,
@ -577,18 +578,38 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
child: Transform.rotate( child: Transform.rotate(
angle: themeType == AppThemeType.doodle ? -0.04 : 0, angle: themeType == AppThemeType.doodle ? -0.04 : 0,
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () async {
if (playerName.toUpperCase() == 'PAOLO') { _debugTapCount++;
_debugTapCount++;
if (_debugTapCount == 5) { // CHEAT LOCALE VIVO SOLO IN DEBUG MODE
StorageService.instance.addXP(2000); if (kDebugMode && playerName.toUpperCase() == 'PAOLO' && _debugTapCount == 5) {
setState(() {}); StorageService.instance.addXP(2000);
ScaffoldMessenger.of(context).showSnackBar( setState(() {});
SnackBar(content: Text("🛠 DEBUG MODE: +20 Livelli!", style: getSharedTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.bold))), backgroundColor: Colors.purpleAccent, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))) ScaffoldMessenger.of(context).showSnackBar(
); SnackBar(content: Text("🛠 DEBUG MODE: +20 Livelli!", style: getSharedTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.bold))), backgroundColor: Colors.purpleAccent, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)))
} else if (_debugTapCount >= 7) { );
_debugTapCount = 0; }
Navigator.push(context, MaterialPageRoute(builder: (_) => AdminScreen())); // ACCESSO DASHBOARD
else if (_debugTapCount >= 7) {
_debugTapCount = 0;
if (kDebugMode && playerName.toUpperCase() == 'PAOLO') {
Navigator.push(context, MaterialPageRoute(builder: (_) => const AdminScreen()));
} else {
bool isAdmin = await StorageService.instance.isUserAdmin();
if (isAdmin && mounted) {
Navigator.push(context, MaterialPageRoute(builder: (_) => const AdminScreen()));
} else if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Accesso Negato: Non sei un Amministratore 🛑", style: getSharedTextStyle(themeType, const TextStyle(color: Colors.white, fontWeight: FontWeight.bold))),
backgroundColor: Colors.redAccent,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
)
);
}
} }
} }
}, },
@ -791,7 +812,6 @@ class _FavoriteOnlinePopupState extends State<FavoriteOnlinePopup> with SingleTi
position: _offsetAnimation, position: _offsetAnimation,
child: Material( child: Material(
color: Colors.transparent, color: Colors.transparent,
// --- FIX OVERLAP POPUP: Aggiunta Elevation Altissima (100) ---
elevation: 100, elevation: 100,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),

View file

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_auth/firebase_auth.dart';
import 'package:tetraq/l10n/app_localizations.dart'; // <-- IMPORT DEL DIZIONARIO!
import '../../logic/game_controller.dart'; import '../../logic/game_controller.dart';
import '../../models/game_board.dart'; import '../../models/game_board.dart';
@ -71,7 +72,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
@override @override
void didChangeAppLifecycleState(AppLifecycleState state) { void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused || state == AppLifecycleState.detached) { if (state == AppLifecycleState.detached) {
_cleanupGhostRoom(); _cleanupGhostRoom();
} }
} }
@ -222,12 +223,13 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
builder: (context) { builder: (context) {
final theme = context.watch<ThemeManager>().currentColors; final theme = context.watch<ThemeManager>().currentColors;
final themeType = context.read<ThemeManager>().currentThemeType; final themeType = context.read<ThemeManager>().currentThemeType;
final loc = AppLocalizations.of(context)!;
Widget dialogContent = Column( Widget dialogContent = Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
CircularProgressIndicator(color: theme.playerRed), const SizedBox(height: 25), CircularProgressIndicator(color: theme.playerRed), const SizedBox(height: 25),
Text("CODICE STANZA", style: getLobbyTextStyle(themeType, TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: theme.text.withOpacity(0.6), letterSpacing: 2))), 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(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)]))),
const SizedBox(height: 25), const SizedBox(height: 25),
Transform.rotate( Transform.rotate(
@ -305,7 +307,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
_cleanupGhostRoom(); _cleanupGhostRoom();
Navigator.pop(context); Navigator.pop(context);
}, },
child: Text("ANNULLA", 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: 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)]))),
), ),
], ],
), ),
@ -348,6 +350,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
final themeManager = context.watch<ThemeManager>(); final themeManager = context.watch<ThemeManager>();
final themeType = themeManager.currentThemeType; final themeType = themeManager.currentThemeType;
final theme = themeManager.currentColors; final theme = themeManager.currentColors;
final loc = AppLocalizations.of(context)!; // <-- CHIAMATA AL DIZIONARIO
String? bgImage; String? bgImage;
if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg'; if (themeType == AppThemeType.doodle) bgImage = 'assets/images/doodle_bg.jpg';
@ -369,7 +372,6 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
panelBackgroundColor = Colors.black.withOpacity(0.4); panelBackgroundColor = Colors.black.withOpacity(0.4);
} }
Widget hostPanel = Transform.rotate( Widget hostPanel = Transform.rotate(
angle: themeType == AppThemeType.doodle ? 0.01 : 0, angle: themeType == AppThemeType.doodle ? 0.01 : 0,
child: Container( child: Container(
@ -387,10 +389,10 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Center(child: Text("IMPOSTAZIONI STANZA", textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.6), letterSpacing: 2.0)))), Center(child: Text(loc.roomSettings, textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 12, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.6), letterSpacing: 2.0)))),
const SizedBox(height: 10), const SizedBox(height: 10),
Text("FORMA ARENA", style: getLobbyTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))), Text(loc.arenaShape, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))),
const SizedBox(height: 6), const SizedBox(height: 6),
Row( Row(
@ -412,7 +414,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
Divider(color: themeType == AppThemeType.doodle ? theme.text.withOpacity(0.5) : Colors.white.withOpacity(0.05), thickness: themeType == AppThemeType.doodle ? 2.5 : 1.5), Divider(color: themeType == AppThemeType.doodle ? theme.text.withOpacity(0.5) : Colors.white.withOpacity(0.05), thickness: themeType == AppThemeType.doodle ? 2.5 : 1.5),
const SizedBox(height: 12), const SizedBox(height: 12),
Text("GRANDEZZA", style: getLobbyTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))), Text(loc.arenaSize, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))),
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
@ -428,7 +430,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
Divider(color: themeType == AppThemeType.doodle ? theme.text.withOpacity(0.5) : Colors.white.withOpacity(0.05), thickness: themeType == AppThemeType.doodle ? 2.5 : 1.5), Divider(color: themeType == AppThemeType.doodle ? theme.text.withOpacity(0.5) : Colors.white.withOpacity(0.05), thickness: themeType == AppThemeType.doodle ? 2.5 : 1.5),
const SizedBox(height: 12), const SizedBox(height: 12),
Text("TEMPO E OPZIONI", style: getLobbyTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))), Text(loc.timeAndOptions, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 10, fontWeight: FontWeight.w900, color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), letterSpacing: 1.5))),
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
@ -470,7 +472,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
), ),
Expanded( Expanded(
child: Text("MULTIPLAYER", textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 20, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 2))), child: Text(loc.onlineTitle.toUpperCase(), textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(fontSize: 20, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 2))),
), ),
const SizedBox(width: 48), const SizedBox(width: 48),
], ],
@ -490,11 +492,11 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
Row( Row(
children: [ children: [
Expanded( Expanded(
child: NeonActionButton(label: "AVVIA", color: theme.playerRed, onTap: _createRoom, theme: theme, themeType: themeType), child: NeonActionButton(label: loc.btnStart.toUpperCase(), color: theme.playerRed, onTap: _createRoom, theme: theme, themeType: themeType),
), ),
const SizedBox(width: 10), const SizedBox(width: 10),
Expanded( Expanded(
child: NeonActionButton(label: "ANNULLA", color: Colors.grey.shade600, onTap: () => setState(() => _isCreatingRoom = false), theme: theme, themeType: themeType), child: NeonActionButton(label: loc.btnCancel.toUpperCase(), color: Colors.grey.shade600, onTap: () => setState(() => _isCreatingRoom = false), theme: theme, themeType: themeType),
), ),
], ],
), ),
@ -503,12 +505,12 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
: Column( : Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
NeonActionButton(label: "CREA PARTITA", color: theme.playerRed, onTap: () { FocusScope.of(context).unfocus(); setState(() => _isCreatingRoom = true); }, theme: theme, themeType: themeType), NeonActionButton(label: loc.createMatch.toUpperCase(), color: theme.playerRed, onTap: () { FocusScope.of(context).unfocus(); setState(() => _isCreatingRoom = true); }, theme: theme, themeType: themeType),
const SizedBox(height: 20), const SizedBox(height: 20),
Row( Row(
children: [ children: [
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)), Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: Text("OPPURE", style: getLobbyTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), fontWeight: FontWeight.bold, letterSpacing: 2.0, fontSize: 13)))), Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: Text(loc.wordOr.toUpperCase(), style: getLobbyTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), fontWeight: FontWeight.bold, letterSpacing: 2.0, fontSize: 13)))),
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)), Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
], ],
), ),
@ -530,7 +532,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
style: getLobbyTextStyle(themeType, TextStyle(fontSize: 28, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 12, shadows: themeType == AppThemeType.doodle ? [] : [Shadow(color: theme.playerBlue.withOpacity(0.5), blurRadius: 8)])), style: getLobbyTextStyle(themeType, TextStyle(fontSize: 28, fontWeight: FontWeight.w900, color: theme.text, letterSpacing: 12, shadows: themeType == AppThemeType.doodle ? [] : [Shadow(color: theme.playerBlue.withOpacity(0.5), blurRadius: 8)])),
decoration: InputDecoration( decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(vertical: 12), contentPadding: const EdgeInsets.symmetric(vertical: 12),
hintText: "CODICE", hintStyle: getLobbyTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.3), letterSpacing: 10, fontSize: 20)), counterText: "", hintText: loc.codeHint.toUpperCase(), hintStyle: getLobbyTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.3), letterSpacing: 10, fontSize: 20)), counterText: "",
filled: themeType != AppThemeType.doodle, filled: themeType != AppThemeType.doodle,
fillColor: themeType == AppThemeType.cyberpunk ? Colors.black.withOpacity(0.85) : theme.text.withOpacity(0.05), fillColor: themeType == AppThemeType.cyberpunk ? Colors.black.withOpacity(0.85) : theme.text.withOpacity(0.05),
enabledBorder: themeType == AppThemeType.doodle ? InputBorder.none : OutlineInputBorder(borderSide: BorderSide(color: theme.gridLine.withOpacity(0.5), width: 2.0), borderRadius: BorderRadius.circular(15)), enabledBorder: themeType == AppThemeType.doodle ? InputBorder.none : OutlineInputBorder(borderSide: BorderSide(color: theme.gridLine.withOpacity(0.5), width: 2.0), borderRadius: BorderRadius.circular(15)),
@ -540,7 +542,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
), ),
), ),
const SizedBox(height: 15), const SizedBox(height: 15),
NeonActionButton(label: "UNISCITI", color: theme.playerBlue, onTap: () => _joinRoomByCode(_codeController.text), theme: theme, themeType: themeType), NeonActionButton(label: loc.joinMatch.toUpperCase(), color: theme.playerBlue, onTap: () => _joinRoomByCode(_codeController.text), theme: theme, themeType: themeType),
], ],
), ),
), ),
@ -549,7 +551,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
Row( Row(
children: [ children: [
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)), Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: Text("LOBBY PUBBLICA", style: getLobbyTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), fontWeight: FontWeight.bold, letterSpacing: 2.0, fontSize: 13)))), Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: Text(loc.publicLobbyTitle.toUpperCase(), style: getLobbyTextStyle(themeType, TextStyle(color: themeType == AppThemeType.doodle ? theme.text : theme.text.withOpacity(0.5), fontWeight: FontWeight.bold, letterSpacing: 2.0, fontSize: 13)))),
Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)), Expanded(child: Divider(color: theme.text.withOpacity(0.4), thickness: themeType == AppThemeType.doodle ? 2 : 1.0)),
], ],
), ),
@ -565,7 +567,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) { if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 40.0), padding: const EdgeInsets.symmetric(vertical: 40.0),
child: Center(child: Text("Nessuna stanza pubblica al momento.\nCreane una tu!", textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), height: 1.5, fontSize: 16)))), child: Center(child: Text(loc.emptyLobbyMsg, textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), height: 1.5, fontSize: 16)))),
); );
} }
@ -591,7 +593,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
if (docs.isEmpty) { if (docs.isEmpty) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 40.0), padding: const EdgeInsets.symmetric(vertical: 40.0),
child: Center(child: Text("Nessuna stanza pubblica al momento.\nCreane una tu!", textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), height: 1.5, fontSize: 16)))), child: Center(child: Text(loc.emptyLobbyMsg, textAlign: TextAlign.center, style: getLobbyTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), height: 1.5, fontSize: 16)))),
); );
} }
@ -610,7 +612,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
itemBuilder: (context, index) { itemBuilder: (context, index) {
var doc = docs[index]; var doc = docs[index];
var data = doc.data() as Map<String, dynamic>; var data = doc.data() as Map<String, dynamic>;
String host = data['hostName'] ?? 'Sconosciuto'; String host = data['hostName'] ?? 'Guest';
int r = data['radius'] ?? 4; int r = data['radius'] ?? 4;
String shapeStr = data['shape'] ?? 'classic'; String shapeStr = data['shape'] ?? 'classic';
@ -644,7 +646,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text("Stanza di $host", style: getLobbyTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.bold, fontSize: 18))), Text("${loc.roomOf} $host", style: getLobbyTextStyle(themeType, TextStyle(color: theme.text, fontWeight: FontWeight.bold, fontSize: 18))),
const SizedBox(height: 6), const SizedBox(height: 6),
Text("Raggio: $r$prettyShape$prettyTime", style: getLobbyTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), fontSize: 12))), Text("Raggio: $r$prettyShape$prettyTime", style: getLobbyTextStyle(themeType, TextStyle(color: theme.text.withOpacity(0.6), fontSize: 12))),
], ],
@ -659,7 +661,7 @@ class _LobbyScreenState extends State<LobbyScreen> with WidgetsBindingObserver {
side: themeType == AppThemeType.doodle ? BorderSide(color: theme.text, width: 2) : BorderSide.none, side: themeType == AppThemeType.doodle ? BorderSide(color: theme.text, width: 2) : BorderSide.none,
), ),
onPressed: () => _joinRoomByCode(doc.id), onPressed: () => _joinRoomByCode(doc.id),
child: Text("ENTRA", style: getLobbyTextStyle(themeType, const TextStyle(fontWeight: FontWeight.w900, letterSpacing: 1.0))), child: Text(loc.btnEnter.toUpperCase(), style: getLobbyTextStyle(themeType, const TextStyle(fontWeight: FontWeight.w900, letterSpacing: 1.0))),
) )
], ],
), ),

View file

@ -1,7 +1,7 @@
name: tetraq name: tetraq
description: A new Flutter project. description: A new Flutter project.
publish_to: 'none' publish_to: 'none'
version: 1.1.5+7 version: 1.1.6+8
environment: environment:
sdk: ^3.10.7 sdk: ^3.10.7