tempo e reservas e outos

This commit is contained in:
2026-04-03 01:33:29 +01:00
parent c6255759c5
commit 1b08ed7d07
3 changed files with 628 additions and 862 deletions

View File

@@ -24,20 +24,17 @@ class ShotRecord {
this.points,
});
// 👇 Para o Auto-Save converter em Texto
Map<String, dynamic> toJson() => {
'relativeX': relativeX, 'relativeY': relativeY, 'isMake': isMake,
'playerId': playerId, 'playerName': playerName, 'zone': zone, 'points': points,
};
// 👇 Para o Auto-Save ler do Texto
factory ShotRecord.fromJson(Map<String, dynamic> json) => ShotRecord(
relativeX: json['relativeX'], relativeY: json['relativeY'], isMake: json['isMake'],
playerId: json['playerId'], playerName: json['playerName'], zone: json['zone'], points: json['points'],
);
}
// 👇 AGORA É UM CHANGENOTIFIER (Gestor de Estado Profissional) 👇
class PlacarController extends ChangeNotifier {
final String gameId;
final String myTeam;
@@ -80,8 +77,10 @@ class PlacarController extends ChangeNotifier {
String? pendingAction;
String? pendingPlayerId;
List<ShotRecord> matchShots = [];
// 👇 LISTA PARA O HISTÓRICO (PLAY-BY-PLAY)
List<String> playByPlay = [];
// 👇 O CRONÓMETRO AGORA TEM VIDA PRÓPRIA (ValueNotifier) PARA NÃO ENCRAVAR A APP 👇
ValueNotifier<Duration> durationNotifier = ValueNotifier(const Duration(minutes: 10));
Timer? timer;
bool isRunning = false;
@@ -98,7 +97,7 @@ class PlacarController extends ChangeNotifier {
myCourt.clear(); myBench.clear(); oppCourt.clear(); oppBench.clear();
playerNames.clear(); playerStats.clear(); playerNumbers.clear();
matchShots.clear(); myFouls = 0; opponentFouls = 0;
matchShots.clear(); playByPlay.clear(); myFouls = 0; opponentFouls = 0;
final gameResponse = await supabase.from('games').select().eq('id', gameId).single();
@@ -179,11 +178,10 @@ class PlacarController extends ChangeNotifier {
));
}
// 👇 AUTO-SAVE: SE O JOGO FOI ABAIXO A MEIO, RECUPERA TUDO AQUI! 👇
await _loadLocalBackup();
isLoading = false;
notifyListeners(); // Substitui o antigo onUpdate!
notifyListeners();
} catch (e) {
debugPrint("Erro ao retomar jogo: $e");
isLoading = false;
@@ -215,9 +213,6 @@ class PlacarController extends ChangeNotifier {
}
}
// =========================================================================
// 👇 AS DUAS FUNÇÕES MÁGICAS DO AUTO-SAVE 👇
// =========================================================================
Future<void> _saveLocalBackup() async {
try {
final prefs = await SharedPreferences.getInstance();
@@ -229,6 +224,7 @@ class PlacarController extends ChangeNotifier {
'playerStats': playerStats,
'myCourt': myCourt, 'myBench': myBench, 'oppCourt': oppCourt, 'oppBench': oppBench,
'matchShots': matchShots.map((s) => s.toJson()).toList(),
'playByPlay': playByPlay, // 👇 Guarda o histórico
};
await prefs.setString('backup_$gameId', jsonEncode(backupData));
} catch (e) {
@@ -257,6 +253,8 @@ class PlacarController extends ChangeNotifier {
List<dynamic> decodedShots = data['matchShots'];
matchShots = decodedShots.map((s) => ShotRecord.fromJson(s)).toList();
playByPlay = List<String>.from(data['playByPlay'] ?? []); // 👇 Carrega o histórico
debugPrint("🔄 AUTO-SAVE RECUPERADO COM SUCESSO!");
}
@@ -268,11 +266,11 @@ class PlacarController extends ChangeNotifier {
void toggleTimer(BuildContext context) {
if (isRunning) {
timer?.cancel();
_saveLocalBackup(); // Grava no telemóvel quando pausa!
_saveLocalBackup();
} else {
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (durationNotifier.value.inSeconds > 0) {
durationNotifier.value -= const Duration(seconds: 1); // 👈 Só o relógio atualiza, a app não pisca!
durationNotifier.value -= const Duration(seconds: 1);
} else {
timer.cancel();
isRunning = false;
@@ -281,9 +279,9 @@ class PlacarController extends ChangeNotifier {
durationNotifier.value = const Duration(minutes: 10);
myFouls = 0; opponentFouls = 0;
myTimeoutsUsed = 0; opponentTimeoutsUsed = 0;
_saveLocalBackup(); // Grava mudança de período
_saveLocalBackup();
}
notifyListeners(); // Aqui sim, redesenhamos o ecrã para mudar o Quarto
notifyListeners();
}
});
}
@@ -364,7 +362,7 @@ class PlacarController extends ChangeNotifier {
matchShots.add(ShotRecord(relativeX: relativeX, relativeY: relativeY, isMake: isMake, playerId: playerId, playerName: name, zone: zone, points: points));
_saveLocalBackup(); // 👈 Grava logo para não perder o cesto!
_saveLocalBackup();
notifyListeners();
}
@@ -389,7 +387,7 @@ class PlacarController extends ChangeNotifier {
commitStat(pendingAction!, pendingPlayerId!);
isSelectingShotLocation = false; pendingAction = null; pendingPlayerId = null;
_saveLocalBackup(); // 👈 Grava logo
_saveLocalBackup();
notifyListeners();
}
@@ -428,6 +426,9 @@ class PlacarController extends ChangeNotifier {
bool isOpponent = playerData.startsWith("player_opp_");
String playerId = playerData.replaceAll("player_my_", "").replaceAll("player_opp_", "");
final stats = playerStats[playerId]!;
final name = playerNames[playerId] ?? "Jogador";
String logText = "";
if (action.startsWith("add_pts_")) {
int pts = int.parse(action.split("_").last);
@@ -435,6 +436,7 @@ class PlacarController extends ChangeNotifier {
stats["pts"] = stats["pts"]! + pts;
if (pts == 2 || pts == 3) { stats["fgm"] = stats["fgm"]! + 1; stats["fga"] = stats["fga"]! + 1; }
if (pts == 1) { stats["ftm"] = stats["ftm"]! + 1; stats["fta"] = stats["fta"]! + 1; }
logText = "marcou $pts pontos 🏀";
}
else if (action.startsWith("sub_pts_")) {
int pts = int.parse(action.split("_").last);
@@ -449,24 +451,33 @@ class PlacarController extends ChangeNotifier {
if (stats["ftm"]! > 0) stats["ftm"] = stats["ftm"]! - 1;
if (stats["fta"]! > 0) stats["fta"] = stats["fta"]! - 1;
}
logText = "teve $pts pontos retirados ❌";
}
else if (action == "miss_1") { stats["fta"] = stats["fta"]! + 1; }
else if (action == "miss_2" || action == "miss_3") { stats["fga"] = stats["fga"]! + 1; }
else if (action == "add_orb") { stats["orb"] = stats["orb"]! + 1; stats["rbs"] = stats["rbs"]! + 1; }
else if (action == "add_drb") { stats["drb"] = stats["drb"]! + 1; stats["rbs"] = stats["rbs"]! + 1; }
else if (action == "add_ast") { stats["ast"] = stats["ast"]! + 1; }
else if (action == "add_stl") { stats["stl"] = stats["stl"]! + 1; }
else if (action == "add_tov") { stats["tov"] = stats["tov"]! + 1; }
else if (action == "add_blk") { stats["blk"] = stats["blk"]! + 1; }
else if (action == "miss_1") { stats["fta"] = stats["fta"]! + 1; logText = "falhou lance livre ❌"; }
else if (action == "miss_2" || action == "miss_3") { stats["fga"] = stats["fga"]! + 1; logText = "falhou lançamento ❌"; }
else if (action == "add_orb") { stats["orb"] = stats["orb"]! + 1; stats["rbs"] = stats["rbs"]! + 1; logText = "ganhou ressalto ofensivo 🔄"; }
else if (action == "add_drb") { stats["drb"] = stats["drb"]! + 1; stats["rbs"] = stats["rbs"]! + 1; logText = "ganhou ressalto defensivo 🛡️"; }
else if (action == "add_ast") { stats["ast"] = stats["ast"]! + 1; logText = "fez uma assistência 🤝"; }
else if (action == "add_stl") { stats["stl"] = stats["stl"]! + 1; logText = "roubou a bola 🥷"; }
else if (action == "add_tov") { stats["tov"] = stats["tov"]! + 1; logText = "perdeu a bola (turnover) 🤦"; }
else if (action == "add_blk") { stats["blk"] = stats["blk"]! + 1; logText = "fez um desarme (bloco) ✋"; }
else if (action == "add_foul") {
stats["fls"] = stats["fls"]! + 1;
if (isOpponent) { opponentFouls++; } else { myFouls++; }
logText = "cometeu falta ⚠️";
}
else if (action == "sub_foul") {
if (stats["fls"]! > 0) stats["fls"] = stats["fls"]! - 1;
if (isOpponent) { if (opponentFouls > 0) opponentFouls--; } else { if (myFouls > 0) myFouls--; }
logText = "teve falta anulada 🔄";
}
_saveLocalBackup(); // 👈 Grava na memória!
if (logText.isNotEmpty) {
String time = "${durationNotifier.value.inMinutes.toString().padLeft(2, '0')}:${durationNotifier.value.inSeconds.remainder(60).toString().padLeft(2, '0')}";
playByPlay.insert(0, "P$currentQuarter - $time: $name $logText");
}
_saveLocalBackup();
}
Future<void> saveGameStats(BuildContext context) async {
@@ -570,7 +581,6 @@ class PlacarController extends ChangeNotifier {
await supabase.from('shot_locations').delete().eq('game_id', gameId);
if (batchShots.isNotEmpty) await supabase.from('shot_locations').insert(batchShots);
// 👇 SE O SUPABASE GUARDOU COM SUCESSO, LIMPA A MEMÓRIA DO TELEMÓVEL! 👇
final prefs = await SharedPreferences.getInstance();
await prefs.remove('backup_$gameId');
@@ -590,5 +600,4 @@ class PlacarController extends ChangeNotifier {
timer?.cancel();
super.dispose();
}
}