Auto-sync: 20260312_210001

This commit is contained in:
Paolo 2026-03-12 21:00:08 +01:00
parent 99ae270818
commit 7c7bf0ef2d
15 changed files with 325 additions and 8 deletions

BIN
.DS_Store vendored

Binary file not shown.

View file

@ -0,0 +1,2 @@
index.html,1773344753424,0d5d4b835a7d632ad11d249230a15561286f2bfcd1da8305c6fb294d37e5da09
404.html,1773344753356,05cbc6f94d7a69ce2e29646eab13be2c884e61ba93e3094df5028866876d18b3

5
.firebaserc Normal file
View file

@ -0,0 +1,5 @@
{
"projects": {
"default": "tetraq-32a4a"
}
}

View file

@ -1 +1,47 @@
{"flutter":{"platforms":{"android":{"default":{"projectId":"tetraq-32a4a","appId":"1:705460445314:android:ceac21bb06b7a9f07b949b","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"tetraq-32a4a","appId":"1:705460445314:ios:54d64cb7592954327b949b","uploadDebugSymbols":false,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"tetraq-32a4a","appId":"1:705460445314:ios:da11cbca5d1f6bc27b949b","uploadDebugSymbols":false,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"tetraq-32a4a","configurations":{"android":"1:705460445314:android:ceac21bb06b7a9f07b949b","ios":"1:705460445314:ios:54d64cb7592954327b949b","macos":"1:705460445314:ios:da11cbca5d1f6bc27b949b"}}}}}}
{
"flutter": {
"platforms": {
"android": {
"default": {
"projectId": "tetraq-32a4a",
"appId": "1:705460445314:android:ceac21bb06b7a9f07b949b",
"fileOutput": "android/app/google-services.json"
}
},
"ios": {
"default": {
"projectId": "tetraq-32a4a",
"appId": "1:705460445314:ios:54d64cb7592954327b949b",
"uploadDebugSymbols": false,
"fileOutput": "ios/Runner/GoogleService-Info.plist"
}
},
"macos": {
"default": {
"projectId": "tetraq-32a4a",
"appId": "1:705460445314:ios:da11cbca5d1f6bc27b949b",
"uploadDebugSymbols": false,
"fileOutput": "macos/Runner/GoogleService-Info.plist"
}
},
"dart": {
"lib/firebase_options.dart": {
"projectId": "tetraq-32a4a",
"configurations": {
"android": "1:705460445314:android:ceac21bb06b7a9f07b949b",
"ios": "1:705460445314:ios:54d64cb7592954327b949b",
"macos": "1:705460445314:ios:da11cbca5d1f6bc27b949b"
}
}
}
}
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}

BIN
ios/.DS_Store vendored

Binary file not shown.

View file

@ -52,6 +52,7 @@
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
4867B86862DC650EC26D5F9C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
52CB81B72F635109004C3F43 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = "<group>"; };
5C30E1EF56D9EC1CAEADBE23 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@ -146,6 +147,7 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
52CB81B72F635109004C3F43 /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
@ -473,6 +475,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = 2BX6QRR7GG;
ENABLE_BITCODE = NO;
@ -485,6 +490,7 @@
MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
@ -658,6 +664,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = 2BX6QRR7GG;
ENABLE_BITCODE = NO;
@ -670,6 +679,7 @@
MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -683,6 +693,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = 2BX6QRR7GG;
ENABLE_BITCODE = NO;
@ -695,6 +708,7 @@
MARKETING_VERSION = 1.0.2;
PRODUCT_BUNDLE_IDENTIFIER = com.sanza.tetraq;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";

View file

@ -22,6 +22,19 @@
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.sanza.tetraq</string>
<key>CFBundleURLSchemes</key>
<array>
<string>tetraq</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:tetraq-32a4a.web.app</string>
</array>
</dict>
</plist>

View file

@ -50,6 +50,12 @@ class GameController extends ChangeNotifier {
bool opponentWantsRematch = false;
int lastMatchXP = 0;
// --- VARIABILI PER IL LEVEL UP ---
bool hasLeveledUp = false;
int newlyReachedLevel = 1;
List<String> unlockedFeatures = [];
// ---------------------------------
bool isSetupPhase = true;
bool myJokerPlaced = false;
bool oppJokerPlaced = false;
@ -81,6 +87,10 @@ class GameController extends ChangeNotifier {
_hasSavedResult = false;
lastMatchXP = 0;
// Reset Level Up vars
hasLeveledUp = false;
unlockedFeatures.clear();
myReaction = null;
opponentReaction = null;
_lastOpponentReactionTime = null;
@ -503,6 +513,22 @@ class GameController extends ChangeNotifier {
}
}
// --- LOGICA LEVEL UP E SBLOCCHI ---
List<String> _getUnlocks(int oldLevel, int newLevel) {
List<String> unlocks = [];
for(int i = oldLevel + 1; i <= newLevel; i++) {
if (i == 3) unlocks.add("Tema: Legno & Fiammiferi");
if (i == 5) unlocks.add("Tema: Quaderno (Doodle)");
if (i == 7) unlocks.add("Tema: Cyberpunk");
if (i == 10) {
unlocks.add("Tema: 8-Bit Arcade");
unlocks.add("Forma Arena: Caos");
}
if (i == 15) unlocks.add("Tema: Grimorio");
}
return unlocks;
}
void _saveMatchResult() {
if (_hasSavedResult) return;
_hasSavedResult = true;
@ -512,6 +538,8 @@ class GameController extends ChangeNotifier {
String myRealName = StorageService.instance.playerName;
if (myRealName.isEmpty) myRealName = "IO";
int oldLevel = StorageService.instance.playerLevel; // Salviamo il vecchio livello
if (isOnline) {
bool isWin = isHost ? board.scoreRed > board.scoreBlue : board.scoreBlue > board.scoreRed;
calculatedXP = isWin ? 20 : (isDraw ? 5 : 2);
@ -520,7 +548,7 @@ class GameController extends ChangeNotifier {
int oppScore = isHost ? board.scoreBlue : board.scoreRed;
StorageService.instance.saveMatchToHistory(myName: myRealName, opponent: oppName, myScore: myScore, oppScore: oppScore, isOnline: true);
if (isWin) StorageService.instance.updateQuestProgress(0, 1); // Missione: Vinci Online
if (isWin) StorageService.instance.updateQuestProgress(0, 1);
} else if (isVsCPU) {
int myScore = board.scoreRed; int cpuScore = board.scoreBlue;
@ -529,7 +557,7 @@ class GameController extends ChangeNotifier {
if (isWin) {
StorageService.instance.addWin();
StorageService.instance.updateQuestProgress(1, 1); // Missione: Vinci vs CPU
StorageService.instance.updateQuestProgress(1, 1);
} else if (cpuScore > myScore) {
StorageService.instance.addLoss();
}
@ -539,12 +567,22 @@ class GameController extends ChangeNotifier {
StorageService.instance.saveMatchToHistory(myName: myRealName, opponent: "Ospite (Locale)", myScore: board.scoreRed, oppScore: board.scoreBlue, isOnline: false);
}
// Se si sta giocando in una forma speciale (non classica)
if (board.shape != ArenaShape.classic) {
StorageService.instance.updateQuestProgress(2, 1); // Missione: Usa forme speciali
StorageService.instance.updateQuestProgress(2, 1);
}
lastMatchXP = calculatedXP; StorageService.instance.addXP(calculatedXP); notifyListeners();
lastMatchXP = calculatedXP;
StorageService.instance.addXP(calculatedXP);
// --- CONTROLLO LEVEL UP DOPO AVER DATO GLI XP ---
int newLevel = StorageService.instance.playerLevel;
if (newLevel > oldLevel) {
hasLeveledUp = true;
newlyReachedLevel = newLevel;
unlockedFeatures = _getUnlocks(oldLevel, newLevel);
}
notifyListeners();
}
void increaseLevelAndRestart() {

View file

@ -63,7 +63,10 @@ class MultiplayerService {
}
void shareInviteLink(String roomCode) {
String message = "Ehi! Giochiamo a TetraQ? 🎮\nCopia questo intero messaggio e apri l'app per entrare direttamente, oppure inserisci manualmente il codice: $roomCode";
String message = "Ehi! Giochiamo a TetraQ? 🎮\n\n"
"Clicca su questo link per entrare direttamente in stanza:\n"
"tetraq://join?code=$roomCode\n\n"
"Oppure apri l'app e inserisci manualmente il codice: $roomCode";
Share.share(message);
}

View file

@ -23,6 +23,8 @@ import 'history_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:tetraq/l10n/app_localizations.dart';
import '../admin/admin_screen.dart';
import 'dart:async';
import 'package:app_links/app_links.dart';
TextStyle _getTextStyle(AppThemeType themeType, TextStyle baseStyle) {
if (themeType == AppThemeType.doodle) {
@ -391,20 +393,25 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
int _debugTapCount = 0;
late AppLinks _appLinks;
StreamSubscription<Uri>? _linkSubscription;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((_) {
_checkPlayerName();
StorageService.instance.syncLeaderboard(); // <--- AGGIUNTO: Segna l'ultimo accesso ORA!
StorageService.instance.syncLeaderboard();
});
_checkClipboardForInvite();
_initDeepLinks(); // <--- AGGIUNGI QUESTO
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_linkSubscription?.cancel(); // <--- AGGIUNGI QUESTO
super.dispose();
}
@ -534,6 +541,44 @@ class _HomeScreenState extends State<HomeScreen> with WidgetsBindingObserver {
);
}
// --- LOGICA DEEP LINKS ---
Future<void> _initDeepLinks() async {
_appLinks = AppLinks();
// 1. Controlla se l'app è stata aperta DA CHIUSA tramite un link
try {
final initialUri = await _appLinks.getInitialLink(); // <--- ECCO LA PAROLA CORRETTA!
if (initialUri != null) {
_handleDeepLink(initialUri);
}
} catch (e) {
debugPrint("Errore lettura link iniziale: $e");
}
// 2. Rimane in ascolto se l'app era IN BACKGROUND
_linkSubscription = _appLinks.uriLinkStream.listen((uri) {
_handleDeepLink(uri);
}, onError: (err) {
debugPrint("Errore stream link: $err");
});
}
void _handleDeepLink(Uri uri) {
debugPrint("Link ricevuto: $uri");
if (uri.scheme == 'tetraq' && uri.host == 'join') {
String? code = uri.queryParameters['code'];
if (code != null && code.length == 5) {
// Usa un piccolo delay per assicurarsi che l'app sia pronta
Future.delayed(const Duration(milliseconds: 500), () {
if (mounted) {
_promptJoinRoom(code.toUpperCase());
}
});
}
}
}
// -------------------------
Future<void> _checkClipboardForInvite() async {
try {
ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);

33
public/404.html Normal file
View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page Not Found</title>
<style media="screen">
body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
#message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px 16px; border-radius: 3px; }
#message h3 { color: #888; font-weight: normal; font-size: 16px; margin: 16px 0 12px; }
#message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
#message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
#message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
#message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
#message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
#load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
@media (max-width: 600px) {
body, #message { margin-top: 0; background: white; box-shadow: none; }
body { border-top: 16px solid #ffa100; }
}
</style>
</head>
<body>
<div id="message">
<h2>404</h2>
<h1>Page Not Found</h1>
<p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p>
<h3>Why am I seeing this?</h3>
<p>This page was generated by the Firebase Command-Line Interface. To modify it, edit the <code>404.html</code> file in your project's configured <code>public</code> directory.</p>
</div>
</body>
</html>

89
public/index.html Normal file
View file

@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Welcome to Firebase Hosting</title>
<!-- update the version number as needed -->
<script defer src="/__/firebase/12.10.0/firebase-app-compat.js"></script>
<!-- include only the Firebase features as you need -->
<script defer src="/__/firebase/12.10.0/firebase-auth-compat.js"></script>
<script defer src="/__/firebase/12.10.0/firebase-database-compat.js"></script>
<script defer src="/__/firebase/12.10.0/firebase-firestore-compat.js"></script>
<script defer src="/__/firebase/12.10.0/firebase-functions-compat.js"></script>
<script defer src="/__/firebase/12.10.0/firebase-messaging-compat.js"></script>
<script defer src="/__/firebase/12.10.0/firebase-storage-compat.js"></script>
<script defer src="/__/firebase/12.10.0/firebase-analytics-compat.js"></script>
<script defer src="/__/firebase/12.10.0/firebase-remote-config-compat.js"></script>
<script defer src="/__/firebase/12.10.0/firebase-performance-compat.js"></script>
<!--
initialize the SDK after all desired features are loaded, set useEmulator to false
to avoid connecting the SDK to running emulators.
-->
<script defer src="/__/firebase/init.js?useEmulator=true"></script>
<style media="screen">
body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; }
#message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px; border-radius: 3px; }
#message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; }
#message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;}
#message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; }
#message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; }
#message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); }
#load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; }
@media (max-width: 600px) {
body, #message { margin-top: 0; background: white; box-shadow: none; }
body { border-top: 16px solid #ffa100; }
}
</style>
</head>
<body>
<div id="message">
<h2>Welcome</h2>
<h1>Firebase Hosting Setup Complete</h1>
<p>You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!</p>
<a target="_blank" href="https://firebase.google.com/docs/hosting/">Open Hosting Documentation</a>
</div>
<p id="load">Firebase SDK Loading&hellip;</p>
<script>
document.addEventListener('DOMContentLoaded', function() {
const loadEl = document.querySelector('#load');
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
// // The Firebase SDK is initialized and available here!
//
// firebase.auth().onAuthStateChanged(user => { });
// firebase.database().ref('/path/to/ref').on('value', snapshot => { });
// firebase.firestore().doc('/foo/bar').get().then(() => { });
// firebase.functions().httpsCallable('yourFunction')().then(() => { });
// firebase.messaging().requestPermission().then(() => { });
// firebase.storage().ref('/path/to/ref').getDownloadURL().then(() => { });
// firebase.analytics(); // call to activate
// firebase.analytics().logEvent('tutorial_completed');
// firebase.performance(); // call to activate
//
// // 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
try {
let app = firebase.app();
let features = [
'auth',
'database',
'firestore',
'functions',
'messaging',
'storage',
'analytics',
'remoteConfig',
'performance',
].filter(feature => typeof app[feature] === 'function');
loadEl.textContent = `Firebase SDK loaded with ${features.join(', ')}`;
} catch (e) {
console.error(e);
loadEl.textContent = 'Error loading the Firebase SDK, check the console.';
}
});
</script>
</body>
</html>

View file

@ -0,0 +1,11 @@
{
"applinks": {
"apps": [],
"details": [
{
"appID": "2BX6QRR7GG.com.sanza.tetraq",
"paths": [ "/join" ]
}
]
}
}

View file

@ -0,0 +1,8 @@
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.amastra.tetraq",
"sha256_cert_fingerprints": ["DA_INSERIRE_IN_FUTURO"]
}
}]