From 163241d4a1dac8c6e7beb921f7f4afc9a4b52ff6 Mon Sep 17 00:00:00 2001 From: freetlab Date: Thu, 30 Jul 2026 12:36:27 +0200 Subject: [PATCH] fix(inicio): add the favourites count badge and plain-text Ver todas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tier 4 visual fidelity, audit 1.11/1.12 (t4 line 80): a pill badge next to "Tus emisoras" now shows the TOTAL favorite count (not the 8-capped grid size), and "Ver todas" is plain 12px/w800 brand-teal text instead of a Material TextButton with its own padding and splash. Item 1.4 (hero art radius) was already fixed as a side effect of an earlier commit — no change needed here. --- lib/pantallas/pantalla_inicio.dart | 76 ++++++++++++++++++++---- test/pantallas/pantalla_inicio_test.dart | 70 ++++++++++++++++++++++ 2 files changed, 136 insertions(+), 10 deletions(-) diff --git a/lib/pantallas/pantalla_inicio.dart b/lib/pantallas/pantalla_inicio.dart index 1f58f79..80e12f5 100644 --- a/lib/pantallas/pantalla_inicio.dart +++ b/lib/pantallas/pantalla_inicio.dart @@ -107,19 +107,46 @@ class _PantallaInicioState extends State { Row( children: [ Expanded( - child: Text( - l10n.yourStationsTitle, - style: theme.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w900, - ), + child: Row( + children: [ + Flexible( + child: Text( + l10n.yourStationsTitle, + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w900, + ), + overflow: TextOverflow.ellipsis, + ), + ), + // Audit 1.11 (t4 line 80): a count badge next to the + // section title — the TOTAL favorite count, not the + // 8-capped grid size shown below it. + if (favoritos.isNotEmpty) ...[ + const SizedBox(width: 10), + _InsigniaRecuento(cuenta: favoritos.length), + ], + ], ), ), - TextButton( - onPressed: - () => context.read().irA( - RaizPluriWave.favoritos, + // Audit 1.12 (t4 line 80): "Ver todas" is plain 12px/w800 + // brand-teal text — not a Material TextButton with its own + // padding and splash. + Semantics( + button: true, + child: GestureDetector( + onTap: + () => context.read().irA( + RaizPluriWave.favoritos, + ), + child: Text( + l10n.seeAllAction, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w800, + color: context.pluriTokens.electricMagenta, ), - child: Text(l10n.seeAllAction), + ), + ), ), ], ), @@ -574,6 +601,35 @@ String? _metaEscuchar(Emisora emisora) { return partes.isEmpty ? null : partes.join(' · '); } +/// Audit 1.11 (t4 line 80): the pill badge next to "Tus emisoras" showing +/// the total favorite count — `rgba(255,255,255,.08)` fill, 11px/w800/60%. +class _InsigniaRecuento extends StatelessWidget { + const _InsigniaRecuento({required this.cuenta}); + + final int cuenta; + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(999), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + child: Text( + '$cuenta', + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w800, + color: const Color(0xFFF2F7FA).withValues(alpha: 0.6), + ), + ), + ), + ); + } +} + /// Audit 1.10 (t4 lines 82-88): the "Tus emisoras" grid cell — a 44px /// square thumbnail (radius 11), name (13/w700/lh1.2) and genre /// (11/55%). Deliberately NOT `TarjetaEmisora(esCompacta: true)`: that diff --git a/test/pantallas/pantalla_inicio_test.dart b/test/pantallas/pantalla_inicio_test.dart index 8e9f8f7..4b0deeb 100644 --- a/test/pantallas/pantalla_inicio_test.dart +++ b/test/pantallas/pantalla_inicio_test.dart @@ -334,6 +334,76 @@ void main() { expect(find.byKey(const ValueKey('escuchar-hero-meta')), findsNothing); }, ); + + testWidgets('visual fidelity (audit 1.11): a count badge sits next to "Tus ' + 'emisoras", showing the TOTAL favorite count, not the 8-capped grid ' + 'size', (tester) async { + _setLargeSurfaceSize(tester); + final favoritos = FakeServicioFavoritos(); + final estado = EstadoRadio( + audio: FakeServicioAudio(), + favoritos: favoritos, + radio: FakeServicioRadio(), + servicioEcualizador: FakeServicioEcualizador(), + servicioGrabacion: FakeServicioGrabacionRadio(), + resolverArchivoCustom: _archivoCustomVacio, + iniciarAutomaticamente: false, + ); + addTearDown(estado.dispose); + await tester.runAsync(estado.inicializar); + for (var i = 0; i < 11; i++) { + await favoritos.agregar( + emisoraDemo(uuid: 'badge-$i', nombre: 'Badge Estacion $i'), + ); + } + await estado.cargarFavoritos(); + + await tester.pumpWidget( + _conProviders(estado, _testApp(const PantallaInicio())), + ); + await _pumpStableFrame(tester); + + expect( + find.text('11'), + findsOneWidget, + reason: + 'prototype t4 line 80: the badge reflects how many favorites ' + 'exist, even though the grid below caps display at 8', + ); + }); + + testWidgets( + 'visual fidelity (audit 1.12): "Ver todas" renders as plain brand-teal ' + 'text, not a Material TextButton', + (tester) async { + _setLargeSurfaceSize(tester); + final favoritos = FakeServicioFavoritos(); + final estado = EstadoRadio( + audio: FakeServicioAudio(), + favoritos: favoritos, + radio: FakeServicioRadio(), + servicioEcualizador: FakeServicioEcualizador(), + servicioGrabacion: FakeServicioGrabacionRadio(), + resolverArchivoCustom: _archivoCustomVacio, + iniciarAutomaticamente: false, + ); + addTearDown(estado.dispose); + await tester.runAsync(estado.inicializar); + await favoritos.agregar(emisoraDemo(uuid: 'f1', nombre: 'Favorita Uno')); + await estado.cargarFavoritos(); + + await tester.pumpWidget( + _conProviders(estado, _testApp(const PantallaInicio())), + ); + await _pumpStableFrame(tester); + + expect(find.byType(TextButton), findsNothing); + final texto = tester.widget(find.text('Ver todas')); + expect(texto.style?.fontSize, 12); + expect(texto.style?.fontWeight, FontWeight.w800); + expect(texto.style?.color, const Color(0xFF21D4D9)); + }, + ); } /// Mirrors the app.dart wiring: EstadoRadio owns the domain notifiers and