6e5e423ab4
No se puede marcar “vista” sin revelar la palabra antes. Se puede volver a ver la palabra durante debate/votación/resultado. Notas online privadas por partida y jugador. Tests añadidos para notas scoped. Ajusté roomId en el payload de inicio para que las notas no se mezclen entre partidas.
236 lines
7.8 KiB
Dart
236 lines
7.8 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:farolero/l10n/generated/app_localizations.dart';
|
|
import 'package:farolero/modelos/inicio_partida_multijugador.dart';
|
|
import 'package:farolero/modelos/jugador.dart';
|
|
import 'package:farolero/pantallas/pantalla_notas_online.dart';
|
|
import 'package:farolero/pantallas/pantalla_revision_palabra.dart';
|
|
import 'package:farolero/tema/tema_app.dart';
|
|
|
|
/// Pantalla que ve el jugador durante la fase de debate (multidispositivo).
|
|
/// El cliente recibe el cambio de fase via Nearby y se navega aquí.
|
|
class PantallaDebateCliente extends StatefulWidget {
|
|
final int? tiempoDebateSegundos;
|
|
final String? primerTurnoNombre;
|
|
final String? partidaId;
|
|
final String? pistaCategoria;
|
|
final List<Jugador> jugadores;
|
|
final List<JugadorInicioPartida> jugadoresControlados;
|
|
final VoidCallback onSolicitarVotacion;
|
|
|
|
const PantallaDebateCliente({
|
|
super.key,
|
|
this.tiempoDebateSegundos,
|
|
this.primerTurnoNombre,
|
|
this.partidaId,
|
|
this.pistaCategoria,
|
|
this.jugadores = const [],
|
|
this.jugadoresControlados = const [],
|
|
required this.onSolicitarVotacion,
|
|
});
|
|
|
|
@override
|
|
State<PantallaDebateCliente> createState() => _PantallaDebateClienteState();
|
|
}
|
|
|
|
class _PantallaDebateClienteState extends State<PantallaDebateCliente> {
|
|
Timer? _timer;
|
|
int _segundosRestantes = 0;
|
|
bool _votacionSolicitada = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
if (widget.tiempoDebateSegundos != null) {
|
|
_segundosRestantes = widget.tiempoDebateSegundos!;
|
|
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
|
if (_segundosRestantes > 0) {
|
|
setState(() => _segundosRestantes--);
|
|
} else {
|
|
timer.cancel();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_timer?.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
String _formatearTiempo(int segundos) {
|
|
final min = segundos ~/ 60;
|
|
final seg = segundos % 60;
|
|
return "${min.toString().padLeft(2, '0')}:${seg.toString().padLeft(2, '0')}";
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context)!;
|
|
|
|
return Scaffold(
|
|
backgroundColor: TemaApp.colorFondo,
|
|
appBar: AppBar(
|
|
title: Text(l10n.debate),
|
|
automaticallyImplyLeading: false,
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
actions: [
|
|
IconButton(
|
|
tooltip: l10n.seeYourWord,
|
|
icon: const Icon(Icons.visibility),
|
|
onPressed: widget.jugadoresControlados.isEmpty
|
|
? null
|
|
: () => mostrarRevisionPalabraOnline(
|
|
context: context,
|
|
jugadoresControlados: widget.jugadoresControlados,
|
|
pistaCategoria: widget.pistaCategoria,
|
|
),
|
|
),
|
|
IconButton(
|
|
tooltip: l10n.notesTitle,
|
|
icon: const Icon(Icons.edit_note),
|
|
onPressed: _puedeAbrirNotas
|
|
? () => Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (_) => PantallaNotasOnline(
|
|
partidaId: widget.partidaId!,
|
|
jugadores: widget.jugadores,
|
|
autoresControlados: widget.jugadoresControlados,
|
|
),
|
|
),
|
|
)
|
|
: null,
|
|
),
|
|
],
|
|
),
|
|
body: Padding(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
children: [
|
|
const Spacer(),
|
|
|
|
// Timer
|
|
if (widget.tiempoDebateSegundos != null) ...[
|
|
Container(
|
|
padding: const EdgeInsets.all(32),
|
|
decoration: BoxDecoration(
|
|
color: _segundosRestantes == 0
|
|
? TemaApp.colorAcento.withValues(alpha: 0.3)
|
|
: TemaApp.colorTarjeta,
|
|
borderRadius: BorderRadius.circular(24),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Text(
|
|
_segundosRestantes == 0
|
|
? l10n.timeUp
|
|
: l10n.timeRemaining,
|
|
style: Theme.of(context).textTheme.titleMedium,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
_formatearTiempo(_segundosRestantes),
|
|
style: Theme.of(context).textTheme.displayMedium?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
color: _segundosRestantes == 0
|
|
? TemaApp.colorAcento
|
|
: TemaApp.colorTexto,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 32),
|
|
] else ...[
|
|
Text(
|
|
l10n.debatePhaseActive,
|
|
style: Theme.of(context).textTheme.headlineMedium,
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 16),
|
|
],
|
|
|
|
// Instrucciones
|
|
if (widget.primerTurnoNombre != null) ...[
|
|
Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: TemaApp.colorNaranja.withValues(alpha: 0.18),
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(
|
|
color: TemaApp.colorNaranja.withValues(alpha: 0.65),
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
const Icon(
|
|
Icons.record_voice_over,
|
|
color: TemaApp.colorNaranja,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
'Empieza ${widget.primerTurnoNombre} diciendo su palabra.',
|
|
style: Theme.of(context).textTheme.titleMedium,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
],
|
|
|
|
Text(
|
|
l10n.debateInstructions,
|
|
textAlign: TextAlign.center,
|
|
style: TextStyle(
|
|
color: TemaApp.colorTextoSecundario,
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
|
|
const Spacer(),
|
|
|
|
// Botón solicitar votación
|
|
SizedBox(
|
|
width: double.infinity,
|
|
height: 56,
|
|
child: ElevatedButton.icon(
|
|
onPressed: _votacionSolicitada
|
|
? null
|
|
: () {
|
|
setState(() => _votacionSolicitada = true);
|
|
widget.onSolicitarVotacion();
|
|
},
|
|
icon: Icon(_votacionSolicitada ? Icons.hourglass_empty : Icons.how_to_vote),
|
|
label: Text(
|
|
_votacionSolicitada
|
|
? l10n.votacionSolicitada
|
|
: l10n.solicitarVotacion,
|
|
),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: _votacionSolicitada
|
|
? TemaApp.colorTarjeta
|
|
: TemaApp.colorAcento,
|
|
foregroundColor: Colors.white,
|
|
textStyle: const TextStyle(fontSize: 16),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
bool get _puedeAbrirNotas {
|
|
return widget.partidaId != null &&
|
|
widget.jugadores.isNotEmpty &&
|
|
widget.jugadoresControlados.isNotEmpty;
|
|
}
|
|
}
|