Implementado:

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.
This commit is contained in:
2026-05-05 21:49:40 +02:00
parent 1abdeb2f56
commit 6e5e423ab4
12 changed files with 802 additions and 75 deletions

View File

@@ -10,6 +10,8 @@ import '../modelos/partida.dart';
import '../servicios/servicio_nearby.dart';
import '../tema/componentes_farolero.dart';
import '../tema/tema_app.dart';
import 'pantalla_notas_online.dart';
import 'pantalla_revision_palabra.dart';
import 'pantalla_votacion_cliente.dart';
import 'pantalla_palabras_cliente.dart';
@@ -145,6 +147,42 @@ class _PantallaGestorHostState extends State<PantallaGestorHost> {
title: Text(l10n.hostGame),
automaticallyImplyLeading: false,
actions: [
IconButton(
tooltip: l10n.seeYourWord,
icon: const Icon(Icons.visibility),
onPressed: partida.fase.index <= FaseJuego.verPalabra.index
? null
: () => mostrarRevisionPalabraOnline(
context: context,
jugadoresControlados: _jugadoresHostControlados(
partida,
nearby,
),
pistaCategoria: partida.config.pistaImpostor
? partida.categoriaReal
: null,
),
),
IconButton(
tooltip: l10n.notesTitle,
icon: const Icon(Icons.edit_note),
onPressed: partida.fase.index < FaseJuego.debate.index ||
nearby.roomId == null
? null
: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => PantallaNotasOnline(
partidaId: nearby.roomId!,
jugadores: partida.jugadoresActivos,
autoresControlados: _jugadoresHostControlados(
partida,
nearby,
),
),
),
),
),
IconButton(
icon: const Icon(Icons.close),
onPressed: () async {
@@ -316,13 +354,14 @@ class _PantallaGestorHostState extends State<PantallaGestorHost> {
);
}
void _mostrarPalabraHost(BuildContext context) {
final estado = context.read<EstadoJuego>();
final sala = context.read<ServicioNearby>().estadoSala;
final partida = estado.partida;
if (partida == null || sala == null) return;
List<JugadorInicioPartida> _jugadoresHostControlados(
Partida partida,
ServicioNearby nearby,
) {
final sala = nearby.estadoSala;
if (sala == null) return const [];
final jugadoresHost = sala
return sala
.usuariosPorCliente(sala.hostClientId)
.where((usuario) => partida.jugadores.any((j) => j.id == usuario.id))
.map((usuario) {
@@ -333,10 +372,19 @@ class _PantallaGestorHostState extends State<PantallaGestorHost> {
jugadorId: jugador.id,
nombre: jugador.nombre,
esImpostor: jugador.esImpostor,
palabra: jugador.palabra,
palabra: jugador.palabra ?? partida.palabraSecreta,
);
})
.toList();
}
void _mostrarPalabraHost(BuildContext context) {
final estado = context.read<EstadoJuego>();
final nearby = context.read<ServicioNearby>();
final partida = estado.partida;
if (partida == null) return;
final jugadoresHost = _jugadoresHostControlados(partida, nearby);
if (jugadoresHost.length > 1) {
Navigator.push(
@@ -759,6 +807,10 @@ class _PantallaGestorHostState extends State<PantallaGestorHost> {
builder: (_) => PantallaVotacionCliente(
jugadores: partida.jugadoresActivos,
jugadoresControlados: jugadoresHost,
partidaId: context.read<ServicioNearby>().roomId,
pistaCategoria: partida.config.pistaImpostor
? partida.categoriaReal
: null,
onVotos: (votos) {
for (final entry in votos.entries) {
estado.registrarVoto(entry.key, entry.value);
@@ -874,6 +926,7 @@ class _PantallaRevelarPalabraHost extends StatefulWidget {
class _PantallaRevelarPalabraHostState
extends State<_PantallaRevelarPalabraHost> {
bool _manteniendo = false;
bool _haRevelado = false;
@override
Widget build(BuildContext context) {
@@ -966,7 +1019,10 @@ class _PantallaRevelarPalabraHostState
),
const SizedBox(height: 24),
GestureDetector(
onLongPressStart: (_) => setState(() => _manteniendo = true),
onLongPressStart: (_) => setState(() {
_manteniendo = true;
_haRevelado = true;
}),
onLongPressEnd: (_) => setState(() => _manteniendo = false),
child: Container(
width: double.infinity,
@@ -996,12 +1052,16 @@ class _PantallaRevelarPalabraHostState
width: double.infinity,
height: 56,
child: ElevatedButton.icon(
onPressed: () {
widget.onVisto();
Navigator.of(context).pop();
},
onPressed: _haRevelado
? () {
widget.onVisto();
Navigator.of(context).pop();
}
: null,
icon: const Icon(Icons.check),
label: Text(l10n.iveSeenIt),
label: Text(
_haRevelado ? l10n.iveSeenIt : l10n.tapToSee,
),
),
),
],