Gestión de usuarios y avatares en la aplicación. Gestión de traducciones de las palabras.

This commit is contained in:
2026-05-04 20:58:02 +02:00
parent 7dd6c7bd74
commit 957b42ea0c
58 changed files with 22603 additions and 21 deletions
@@ -0,0 +1,115 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../modelos/partida.dart';
class ResultadoPartidaGuardado {
final String id;
final DateTime fecha;
final bool modoMultimovil;
final int jugadores;
final int impostores;
final int rondas;
final String ganador;
final String palabra;
final String categoria;
const ResultadoPartidaGuardado({
required this.id,
required this.fecha,
required this.modoMultimovil,
required this.jugadores,
required this.impostores,
required this.rondas,
required this.ganador,
required this.palabra,
required this.categoria,
});
factory ResultadoPartidaGuardado.desdePartida(Partida partida) {
return ResultadoPartidaGuardado(
id: DateTime.now().microsecondsSinceEpoch.toString(),
fecha: DateTime.now(),
modoMultimovil: partida.config.modoMultimovil,
jugadores: partida.jugadores.length,
impostores: partida.impostoresTotales,
rondas: partida.rondaActual,
ganador: partida.ganador ?? 'sin_resultado',
palabra: partida.palabraSecreta,
categoria: partida.categoriaReal,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'fecha': fecha.toIso8601String(),
'modoMultimovil': modoMultimovil,
'jugadores': jugadores,
'impostores': impostores,
'rondas': rondas,
'ganador': ganador,
'palabra': palabra,
'categoria': categoria,
};
factory ResultadoPartidaGuardado.fromJson(Map<String, dynamic> json) {
return ResultadoPartidaGuardado(
id: json['id'] as String,
fecha: DateTime.parse(json['fecha'] as String),
modoMultimovil: json['modoMultimovil'] as bool? ?? false,
jugadores: json['jugadores'] as int? ?? 0,
impostores: json['impostores'] as int? ?? 0,
rondas: json['rondas'] as int? ?? 0,
ganador: json['ganador'] as String? ?? 'sin_resultado',
palabra: json['palabra'] as String? ?? '',
categoria: json['categoria'] as String? ?? '',
);
}
}
class ServicioHistorialPartidas extends ChangeNotifier {
static const _clave = 'historial.partidas';
final List<ResultadoPartidaGuardado> _partidas = [];
bool _cargado = false;
List<ResultadoPartidaGuardado> get partidas => List.unmodifiable(_partidas);
bool get cargado => _cargado;
Future<void> cargar() async {
final prefs = await SharedPreferences.getInstance();
final raw = prefs.getString(_clave);
_partidas.clear();
if (raw != null) {
final lista = json.decode(raw) as List<dynamic>;
_partidas.addAll(
lista.map((e) => ResultadoPartidaGuardado.fromJson(e as Map<String, dynamic>)),
);
}
_cargado = true;
notifyListeners();
}
Future<void> guardarPartida(Partida partida) async {
if (partida.ganador == null) return;
_partidas.insert(0, ResultadoPartidaGuardado.desdePartida(partida));
if (_partidas.length > 100) _partidas.removeRange(100, _partidas.length);
await _persistir();
notifyListeners();
}
Future<void> limpiar() async {
_partidas.clear();
await _persistir();
notifyListeners();
}
Future<void> _persistir() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(
_clave,
json.encode(_partidas.map((p) => p.toJson()).toList()),
);
}
}
+18 -2
View File
@@ -204,7 +204,12 @@ class ServicioNearby extends ChangeNotifier {
// ==================== HOST ====================
Future<bool> iniciarHost(String nombreSala, String miNombre) async {
Future<bool> iniciarHost(
String nombreSala,
String miNombre, {
String? miNick,
String? miAvatar,
}) async {
_nombreSala = nombreSala;
_miNombre = miNombre;
_roomId = DateTime.now().microsecondsSinceEpoch.toString();
@@ -222,6 +227,9 @@ class ServicioNearby extends ChangeNotifier {
final usuarioHost = Usuario(
id: 'u-${_roomId!}-host',
nombre: miNombre,
nick: miNick,
avatar: miAvatar,
foto: miAvatar,
creadoPorClienteId: _hostClientId,
clienteIdSeleccionado: _hostClientId,
);
@@ -654,13 +662,21 @@ class ServicioNearby extends ChangeNotifier {
}
}
Future<void> crearUsuarioSala(String nombre, {bool seleccionar = true}) async {
Future<void> crearUsuarioSala(
String nombre, {
bool seleccionar = true,
String? nick,
String? avatar,
}) async {
final nombreLimpio = nombre.trim();
if (nombreLimpio.isEmpty) return;
final clientId = _miClientId;
final usuario = Usuario(
id: 'u-${DateTime.now().microsecondsSinceEpoch}',
nombre: nombreLimpio,
nick: nick,
avatar: avatar,
foto: avatar,
creadoPorClienteId: clientId,
);
if (_esHost && _estadoSala != null && clientId != null) {
+104
View File
@@ -0,0 +1,104 @@
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
class PerfilUsuario {
final String nombre;
final String nick;
final String avatarAsset;
const PerfilUsuario({
required this.nombre,
required this.nick,
required this.avatarAsset,
});
PerfilUsuario copiar({String? nombre, String? nick, String? avatarAsset}) {
return PerfilUsuario(
nombre: nombre ?? this.nombre,
nick: nick ?? this.nick,
avatarAsset: avatarAsset ?? this.avatarAsset,
);
}
}
class ServicioPerfilUsuario extends ChangeNotifier {
static const _claveNombre = 'perfil.nombre';
static const _claveNick = 'perfil.nick';
static const _claveAvatar = 'perfil.avatar';
static const avatares = [
'assets/avatars/avatar_01.png',
'assets/avatars/avatar_02.png',
'assets/avatars/avatar_03.png',
'assets/avatars/avatar_04.png',
'assets/avatars/avatar_05.png',
'assets/avatars/avatar_06.png',
'assets/avatars/avatar_07.png',
'assets/avatars/avatar_08.png',
'assets/avatars/avatar_09.png',
'assets/avatars/avatar_10.png',
'assets/avatars/avatar_11.png',
'assets/avatars/avatar_12.png',
'assets/avatars/avatar_13.png',
'assets/avatars/avatar_14.png',
'assets/avatars/avatar_15.png',
'assets/avatars/avatar_16.png',
'assets/avatars/avatar_17.png',
'assets/avatars/avatar_18.png',
'assets/avatars/avatar_19.png',
'assets/avatars/avatar_20.png',
'assets/avatars/avatar_21.png',
'assets/avatars/avatar_22.png',
'assets/avatars/avatar_23.png',
'assets/avatars/avatar_24.png',
'assets/avatars/avatar_25.png',
'assets/avatars/avatar_26.png',
'assets/avatars/avatar_27.png',
'assets/avatars/avatar_28.png',
'assets/avatars/avatar_29.png',
'assets/avatars/avatar_30.png',
];
PerfilUsuario _perfil = const PerfilUsuario(
nombre: 'Jugador',
nick: 'farolero',
avatarAsset: 'assets/avatars/avatar_01.png',
);
bool _cargado = false;
PerfilUsuario get perfil => _perfil;
bool get cargado => _cargado;
Future<void> cargar() async {
final prefs = await SharedPreferences.getInstance();
_perfil = PerfilUsuario(
nombre: prefs.getString(_claveNombre) ?? _perfil.nombre,
nick: prefs.getString(_claveNick) ?? _perfil.nick,
avatarAsset: prefs.getString(_claveAvatar) ?? _perfil.avatarAsset,
);
_cargado = true;
notifyListeners();
}
Future<void> guardar({
required String nombre,
required String nick,
required String avatarAsset,
}) async {
final nombreLimpio = nombre.trim().isEmpty ? 'Jugador' : nombre.trim();
final nickLimpio = nick.trim().isEmpty ? 'farolero' : nick.trim();
final avatarSeguro = avatares.contains(avatarAsset)
? avatarAsset
: avatares.first;
_perfil = PerfilUsuario(
nombre: nombreLimpio,
nick: nickLimpio,
avatarAsset: avatarSeguro,
);
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_claveNombre, _perfil.nombre);
await prefs.setString(_claveNick, _perfil.nick);
await prefs.setString(_claveAvatar, _perfil.avatarAsset);
notifyListeners();
}
}