287 lines
9.4 KiB
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;
|
|
}
|
|
}
|