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

@@ -0,0 +1,133 @@
import 'package:flutter/material.dart';
import 'package:farolero/l10n/generated/app_localizations.dart';
import 'package:farolero/modelos/inicio_partida_multijugador.dart';
import 'package:farolero/tema/componentes_farolero.dart';
import 'package:farolero/tema/tema_app.dart';
Future<void> mostrarRevisionPalabraOnline({
required BuildContext context,
required List<JugadorInicioPartida> jugadoresControlados,
String? pistaCategoria,
}) async {
if (jugadoresControlados.isEmpty) return;
final jugador = jugadoresControlados.length == 1
? jugadoresControlados.first
: await showModalBottomSheet<JugadorInicioPartida>(
context: context,
backgroundColor: TemaApp.colorSuperficie,
builder: (sheetContext) => SafeArea(
child: ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(16),
children: [
Text(
AppLocalizations.of(sheetContext)!.seeYourWord,
style: Theme.of(sheetContext).textTheme.titleLarge,
),
const SizedBox(height: 12),
...jugadoresControlados.map(
(jugador) => Card(
color: TemaApp.colorTarjeta,
child: ListTile(
leading: const Icon(Icons.visibility),
title: Text(jugador.nombre),
onTap: () => Navigator.pop(sheetContext, jugador),
),
),
),
],
),
),
);
if (jugador == null || !context.mounted) return;
await showDialog<void>(
context: context,
builder: (dialogContext) => _DialogoRevisionPalabra(
jugador: jugador,
pistaCategoria: pistaCategoria,
),
);
}
class _DialogoRevisionPalabra extends StatelessWidget {
final JugadorInicioPartida jugador;
final String? pistaCategoria;
const _DialogoRevisionPalabra({
required this.jugador,
required this.pistaCategoria,
});
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
return Dialog(
backgroundColor: TemaApp.colorSuperficie,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
jugador.nombre,
style: Theme.of(context).textTheme.titleLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: TemaApp.decoracionPanel(
color: TemaApp.colorTarjeta,
borderColor: jugador.esImpostor
? TemaApp.colorAcento
: TemaApp.colorNaranja,
),
child: Column(
children: [
Icon(
jugador.esImpostor ? Icons.theater_comedy : Icons.key,
color: jugador.esImpostor
? TemaApp.colorAcento
: TemaApp.colorNaranja,
size: 36,
),
const SizedBox(height: 16),
if (jugador.esImpostor)
Text(
l10n.youAreImpostor,
style: Theme.of(context).textTheme.headlineSmall,
textAlign: TextAlign.center,
)
else
TarjetaPalabraFarolero(palabra: jugador.palabra ?? ''),
if (jugador.esImpostor && pistaCategoria != null) ...[
const SizedBox(height: 12),
Text(
l10n.clueIs(pistaCategoria!),
style: const TextStyle(color: TemaApp.colorNaranja),
textAlign: TextAlign.center,
),
],
],
),
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () => Navigator.pop(context),
icon: const Icon(Icons.check),
label: Text(l10n.back),
),
),
],
),
),
);
}
}