Files
farolero/lib/pantallas/pantalla_debate_cliente.dart

287 lines
9.4 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/pantallas/pantalla_votacion_cliente.dart';
import 'package:farolero/servicios/servicio_nearby.dart';
import 'package:farolero/tema/tema_app.dart';
import 'package:provider/provider.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;
OnMensajeCallback? _listener;
ServicioNearby? _nearby;
@override
void initState() {
super.initState();
_listener = (endpointId, mensaje) {
if (!mounted || mensaje.tipo != TipoMensaje.fase) return;
final fase = mensaje.datos['fase'] as String?;
if (fase == 'votacion') {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) => PantallaVotacionCliente(
jugadores: widget.jugadores,
jugadoresControlados: widget.jugadoresControlados,
partidaId: widget.partidaId,
pistaCategoria: widget.pistaCategoria,
onVotos: _enviarVotos,
),
),
);
}
};
WidgetsBinding.instance.addPostFrameCallback((_) {
final listener = _listener;
if (listener != null && mounted) {
_nearby = context.read<ServicioNearby>();
_nearby!.onMensaje(listener);
}
});
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();
final listener = _listener;
if (listener != null) {
_nearby?.removeMensajeListener(listener);
}
super.dispose();
}
void _enviarVotos(Map<String, String> votos) {
final nearby = context.read<ServicioNearby>();
if (nearby.hostEndpointId == null) return;
for (final entry in votos.entries) {
nearby.enviarMensaje(
nearby.hostEndpointId!,
MensajeP2P(
tipo: TipoMensaje.voto,
datos: {
'votanteId': entry.key,
'votadoId': entry.value,
'votoporId': entry.value,
},
),
);
}
}
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;
}
}