fix(alarma-sonando): add the schedule pill and restyle the station name

Audit 9.3 (t4 line 415): the ringing screen never showed its schedule
pill. Reuses the alarmScheduleOnce/alarmScheduleWeekdays ARB keys that
already existed with no consumer, plus a new alarmScheduleDaily key
(translated to all 13 locales) for the recurring-alarm case shown in
the prototype.

Audit 9.7 (t4 line 423): the station name was cardTitle (14.5px/w700),
5.5px and 100 weight units under the prototype's 20px/w800.

Audit 9.9 (snooze tiles as number-over-unit) is intentionally NOT
included in this commit: it would require the tile to stop rendering
as a single flat "{minutes} min" Text node, which is exactly what
pantalla_alarma_sonando_dismiss_guard_test.dart taps via
find.text(l10n.alarmSnoozeOptionLabel(N)) in four places. That file
must stay untouched, so this restyle is deferred pending a decision
on how to restructure the tap target safely.
This commit is contained in:
2026-07-29 23:33:17 +02:00
parent dcb716cbc3
commit 9840205a83
29 changed files with 215 additions and 1 deletions
+99 -1
View File
@@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
import '../estado/estado_alarmas.dart';
import '../l10n/display_names.dart';
import '../l10n/formato_fechas.dart';
import '../l10n/gen/app_localizations.dart';
import '../modelos/alarma_musical.dart';
import '../tema/pluri_animate.dart';
@@ -204,6 +205,14 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
child: Column(
children: [
const Spacer(flex: 2),
// Audit 9.3 (t4 line 415): the schedule pill was missing
// entirely — built from `alarma.tipoProgramacion` (a field
// already on the domain model), no new plumbing.
_PildoraProgramacion(
texto: _resumenProgramacion(context, l10n, alarma),
tokens: tokens,
),
const SizedBox(height: 22),
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
@@ -244,7 +253,15 @@ class _PantallaAlarmaSonandoState extends State<PantallaAlarmaSonando> {
? localizedStationName(l10n, alarma.emisora!.nombre)
: l10n.alarmRingingNotificationTitle,
textAlign: TextAlign.center,
style: type.cardTitle,
// Audit 9.7 (t4 line 423): 20px/w800/ls-.3 — this used to
// be `cardTitle` (14.5px/w700), 5.5px and 100 weight
// units under spec for the station name on a full-screen
// ringing surface.
style: type.cardTitle.copyWith(
fontSize: 20,
fontWeight: FontWeight.w800,
letterSpacing: -0.3,
),
),
// WU11 (native-alarms delta — Ringing Screen Shows a
// Static Status Label): only rendered while this alarm was
@@ -360,6 +377,87 @@ String _hora(AlarmaMusical alarma) =>
/// call site (mirrors `_ArteEscuchar._radio` in `pantalla_inicio.dart`).
const _stopButtonRadius = 24.0;
/// Audit 9.3: the schedule pill's text, built only from
/// [AlarmaMusical.tipoProgramacion] and the fields it already carries per
/// case (`diasSemana`, `fechaUnica`) — no new domain plumbing. Reuses the
/// `alarmScheduleOnce`/`alarmScheduleWeekdays` ARB keys, which existed
/// already but had no consumer anywhere in the app.
String _resumenProgramacion(
BuildContext context,
AppLocalizations l10n,
AlarmaMusical alarma,
) {
switch (alarma.tipoProgramacion) {
case TipoProgramacionAlarma.diaria:
return l10n.alarmScheduleDaily;
case TipoProgramacionAlarma.unica:
final localeTag = Localizations.localeOf(context).toString();
return l10n.alarmScheduleOnce(
fechaCortaLocalizada(localeTag, alarma.fechaUnica ?? DateTime.now()),
);
case TipoProgramacionAlarma.diasSemana:
final dias = (List<int>.from(alarma.diasSemana)
..sort()).map((d) => _weekdayShort(l10n, d)).join(', ');
return l10n.alarmScheduleWeekdays(dias);
}
}
// Mirrors `pantalla_alarmas.dart`'s private `_weekdayShort` (kept local
// rather than shared/exported: this screen's only other tie to the alarm
// editor is the domain model itself, and duplicating a 7-line switch is
// cheaper than adding a cross-screen import for it).
String _weekdayShort(AppLocalizations l10n, int day) => switch (day) {
DateTime.monday => l10n.weekdayShortMonday,
DateTime.tuesday => l10n.weekdayShortTuesday,
DateTime.wednesday => l10n.weekdayShortWednesday,
DateTime.thursday => l10n.weekdayShortThursday,
DateTime.friday => l10n.weekdayShortFriday,
DateTime.saturday => l10n.weekdayShortSaturday,
DateTime.sunday => l10n.weekdayShortSunday,
_ => '?',
};
/// Schedule pill (audit 9.3, t4 line 415): `alarm` icon + schedule summary
/// on a warmCoral-tinted pill, matching the prototype's
/// `rgba(244,184,96,.16)` fill / `rgba(244,184,96,.45)` border exactly.
class _PildoraProgramacion extends StatelessWidget {
const _PildoraProgramacion({required this.texto, required this.tokens});
final String texto;
final PluriWaveTokens tokens;
@override
Widget build(BuildContext context) {
return DecoratedBox(
key: const ValueKey('ringing-schedule-pill'),
decoration: BoxDecoration(
color: tokens.warmCoral.withValues(alpha: 0.16),
borderRadius: BorderRadius.circular(999),
border: Border.all(color: tokens.warmCoral.withValues(alpha: 0.45)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.alarm, size: 18, color: tokens.warmCoral),
const SizedBox(width: 6),
Text(
texto,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w800,
letterSpacing: 0.84,
color: tokens.warmCoral,
),
),
],
),
),
);
}
}
/// Full-bleed blurred backdrop (WU11, replaces the `PluriGlassSurface` card
/// container per task 11.3). This app has no per-station artwork/favicon
/// safe to render here: `Emisora.favicon` is a network URL, and rendering