fix(grabaciones): wire the recordings library into Settings navigation
WU15 shipped PantallaGrabaciones (the recordings library: storage bar, recording rows, the "..." Rename/Share/Delete menu) fully tested but reachable from nowhere in the app - a gap flagged in WU15's own apply-progress notes, not fixed there since it needed a design decision rather than a guess. Coordinator ruling applied: the approved mockup's "Ajustes > Grabaciones" screen depicts the library, not the folder/size settings form. So the GRABACIONES Y MUSICA group's "Grabaciones" row in pantalla_ajustes.dart now opens PantallaGrabaciones instead of PantallaAjustesGrabaciones. The settings form is not dropped - it stays reachable, now from within the library via a settings icon in its PluriPushScaffold actions, matching the existing pantalla_ajustes_timer_sueno.dart "Add" action precedent for a real capability living in the header. One new ARB key (en/es only, per precedent): recordingsLibrarySettingsTooltip. Tests: 604 -> 605 (one scenario re-targeted in pantalla_ajustes_test.dart, one new scenario in pantalla_grabaciones_test.dart, which needed the same ListTile-ink-assertion suppression helper WU3a/WU3b established since it now pushes a ListTile-bearing settings screen). flutter analyze unchanged at 1 pre-existing info. Recorded in tasks.md as WU15b - not part of the original 18-unit plan, added here to close the gap WU15 flagged.
This commit is contained in:
@@ -231,6 +231,7 @@
|
||||
"recordingRenameEmptyError": "Enter a name",
|
||||
"recordingDeleteConfirmTitle": "Delete recording?",
|
||||
"recordingDeleteConfirmMessage": "This can't be undone.",
|
||||
"recordingsLibrarySettingsTooltip": "Recording settings",
|
||||
"stationOrderTitle": "Station order",
|
||||
"stationOrderByName": "By name",
|
||||
"stationOrderByQuality": "By quality",
|
||||
|
||||
@@ -231,6 +231,7 @@
|
||||
"recordingRenameEmptyError": "Ingresá un nombre",
|
||||
"recordingDeleteConfirmTitle": "¿Eliminar grabación?",
|
||||
"recordingDeleteConfirmMessage": "Esta acción no se puede deshacer.",
|
||||
"recordingsLibrarySettingsTooltip": "Ajustes de grabación",
|
||||
"stationOrderTitle": "Orden de emisoras",
|
||||
"stationOrderByName": "Por nombre",
|
||||
"stationOrderByQuality": "Por calidad",
|
||||
|
||||
@@ -868,6 +868,12 @@ abstract class AppLocalizations {
|
||||
/// **'Esta acción no se puede deshacer.'**
|
||||
String get recordingDeleteConfirmMessage;
|
||||
|
||||
/// No description provided for @recordingsLibrarySettingsTooltip.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Ajustes de grabación'**
|
||||
String get recordingsLibrarySettingsTooltip;
|
||||
|
||||
/// No description provided for @stationOrderTitle.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
|
||||
@@ -435,6 +435,9 @@ class AppLocalizationsAr extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'ترتيب المحطات';
|
||||
|
||||
|
||||
@@ -438,6 +438,9 @@ class AppLocalizationsBn extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'স্টেশনের ক্রম';
|
||||
|
||||
|
||||
@@ -441,6 +441,9 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'Senderreihenfolge';
|
||||
|
||||
|
||||
@@ -435,6 +435,9 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get recordingDeleteConfirmMessage => 'This can\'t be undone.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Recording settings';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'Station order';
|
||||
|
||||
|
||||
@@ -439,6 +439,9 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'Orden de emisoras';
|
||||
|
||||
|
||||
@@ -443,6 +443,9 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'Ordre des stations';
|
||||
|
||||
|
||||
@@ -436,6 +436,9 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'स्टेशन क्रम';
|
||||
|
||||
|
||||
@@ -437,6 +437,9 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'Urutan stasiun';
|
||||
|
||||
|
||||
@@ -439,6 +439,9 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'Ordine emittenti';
|
||||
|
||||
|
||||
@@ -426,6 +426,9 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => '局の並び順';
|
||||
|
||||
|
||||
@@ -438,6 +438,9 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'Ordem das estações';
|
||||
|
||||
|
||||
@@ -439,6 +439,9 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => 'Порядок станций';
|
||||
|
||||
|
||||
@@ -424,6 +424,9 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
String get recordingDeleteConfirmMessage =>
|
||||
'Esta acción no se puede deshacer.';
|
||||
|
||||
@override
|
||||
String get recordingsLibrarySettingsTooltip => 'Ajustes de grabación';
|
||||
|
||||
@override
|
||||
String get stationOrderTitle => '电台排序';
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import 'ajustes/pantalla_ajustes_backup.dart';
|
||||
import 'ajustes/pantalla_ajustes_ecualizador.dart';
|
||||
import 'ajustes/pantalla_ajustes_emisora_preferida.dart';
|
||||
import 'ajustes/pantalla_ajustes_emisoras_personalizadas.dart';
|
||||
import 'ajustes/pantalla_ajustes_grabaciones.dart';
|
||||
import 'ajustes/pantalla_ajustes_grupos_favoritos.dart';
|
||||
import 'ajustes/pantalla_ajustes_idioma.dart';
|
||||
import 'ajustes/pantalla_ajustes_info.dart';
|
||||
@@ -18,6 +17,7 @@ import 'ajustes/pantalla_ajustes_orden_listas.dart';
|
||||
import 'ajustes/pantalla_ajustes_salida_audio.dart';
|
||||
import 'ajustes/pantalla_ajustes_timer_sueno.dart';
|
||||
import 'ajustes/widgets/fila_ajuste.dart';
|
||||
import 'pantalla_grabaciones.dart';
|
||||
|
||||
class PantallaAjustes extends StatelessWidget {
|
||||
const PantallaAjustes({super.key});
|
||||
@@ -142,10 +142,15 @@ class _AjustesContent extends StatelessWidget {
|
||||
FilaAjuste(
|
||||
icon: Icons.radio_button_checked_rounded,
|
||||
titulo: l10n.recordingsSectionTitle,
|
||||
// WU15b: this row opens the recordings LIBRARY
|
||||
// (PantallaGrabaciones), matching the approved mockup's
|
||||
// "Ajustes > Grabaciones" screen. The folder/size settings
|
||||
// form (PantallaAjustesGrabaciones) is still reachable, but
|
||||
// now from within the library via its own settings action.
|
||||
onTap:
|
||||
() => PluriPushScaffold.push(
|
||||
context,
|
||||
(_) => const PantallaAjustesGrabaciones(),
|
||||
(_) => const PantallaGrabaciones(),
|
||||
),
|
||||
),
|
||||
FilaAjuste(
|
||||
|
||||
@@ -13,6 +13,7 @@ import '../widgets/pluri_icon.dart';
|
||||
import '../widgets/pluri_layout.dart';
|
||||
import '../widgets/pluri_premium_widgets.dart';
|
||||
import '../widgets/pluri_push_scaffold.dart';
|
||||
import 'ajustes/pantalla_ajustes_grabaciones.dart';
|
||||
|
||||
/// Inline-preview playback + duration lookup for a single recording file at
|
||||
/// a time (WU15, recordings-library spec — "Row playback starts and
|
||||
@@ -228,6 +229,17 @@ class _PantallaGrabacionesState extends State<PantallaGrabaciones> {
|
||||
|
||||
return PluriPushScaffold(
|
||||
title: l10n.recordingsLibraryTitle,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings_outlined),
|
||||
tooltip: l10n.recordingsLibrarySettingsTooltip,
|
||||
onPressed:
|
||||
() => PluriPushScaffold.push(
|
||||
context,
|
||||
(_) => const PantallaAjustesGrabaciones(),
|
||||
),
|
||||
),
|
||||
],
|
||||
body: FutureBuilder<List<ArchivoGrabacion>>(
|
||||
future: _grabaciones,
|
||||
builder: (context, snap) {
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
> needs. Tables and checklists are used throughout to keep it scannable despite the length.
|
||||
> **WU12 does not exist** — the native EQ band-count spike was resolved before planning closed (Engram id 2498, "keep
|
||||
> 5 bands"). Numbering skips 11→13 on purpose; this is not a gap.
|
||||
> **WU15b was added mid-apply, not planned upfront** — WU15 shipped `PantallaGrabaciones` (the recordings library)
|
||||
> fully tested but reachable from nowhere in the app. WU15b (below, after WU15's section) is the coordinator-ruled fix
|
||||
> that wires it into Settings navigation. It is small and does not change the 18-commit delivery model's shape.
|
||||
> Strict TDD is ON. Runner: `flutter test`. `flutter analyze` and a **scoped** `dart format` gate every commit.
|
||||
> **`flutter build` is never run.**
|
||||
>
|
||||
@@ -51,6 +54,7 @@
|
||||
| 13 | `feat(eq): restyle equalizer screen and add custom presets` | 3a | 400-550 | Medium-High | Monitor |
|
||||
| 14 | `feat(reproductor): restructure full player with tool-tray and EQ sheet` | 13 | 450-600 | Medium-High | Monitor |
|
||||
| 15 | `feat(grabaciones): add recordings library screen` | 3b | ~~300-400~~ → **REALIZED: 1,767** (1,767+ / 0-, 22 files) | Medium | Monitor§ |
|
||||
| 15b | `fix(grabaciones): wire the recordings library into Settings navigation` | 15 | 60-100 | Low | No |
|
||||
| 16 | `feat(connectivity): restyle offline and reconnect banners` | 1 | 150-250 | Low | No |
|
||||
| 17 | `feat(bienvenida): add monetization-free welcome screen` | 1 | 150-200 | Low | No |
|
||||
| 18 | `feat(i18n): add redesign strings and translate Escuchar rename to 11 locales` | all | ~0 eng. / 400-600 data | Medium (data volume, low logic risk) | No |
|
||||
@@ -640,7 +644,45 @@ and no task asks it to gain a "view library" link. WU15's own **Verify** command
|
||||
`pantalla_ajustes_grabaciones_test.dart` — confirming this screen is not meant to touch WU3b's file in this work
|
||||
unit. Not invented here; needs a design decision, not a guess.
|
||||
|
||||
## WU16 — Connectivity states
|
||||
## WU15b — Wire the recordings library into Settings navigation
|
||||
|
||||
**Not in the original plan — added to close the WU15 gap noted above.** WU15 built `PantallaGrabaciones` (the
|
||||
recordings library) fully tested and committed, but left it unreachable from the app: `rg "PantallaGrabaciones" lib/`
|
||||
found only its own declaration. This work unit exists solely to fix that.
|
||||
|
||||
**Commit**: `fix(grabaciones): wire the recordings library into Settings navigation`
|
||||
**Depends on**: WU15
|
||||
**Spec refs**: `recordings-library` — Browsable Recordings List (reachability); `app-navigation-shell` — Push-Chrome
|
||||
on Second-Level Screens (the settings form stays a pushed screen, now one level deeper)
|
||||
**Verify**: `flutter test test/pantallas/pantalla_ajustes_test.dart test/pantallas/pantalla_grabaciones_test.dart test/pantallas/ajustes/pantalla_ajustes_grabaciones_test.dart && flutter analyze && dart format --set-exit-if-changed $(git diff --cached --name-only --diff-filter=ACM HEAD -- '*.dart')`
|
||||
**Modified tests**: `pantalla_ajustes_test.dart` (one scenario re-targeted), `pantalla_grabaciones_test.dart` (one new
|
||||
scenario)
|
||||
|
||||
**Coordinator ruling applied.** The approved mockup's screen 12 ("Ajustes › Grabaciones") depicts the recordings
|
||||
**library**, not the settings form — so the `GRABACIONES Y MÚSICA` group's "Grabaciones" row now opens
|
||||
`PantallaGrabaciones` (the library) instead of `PantallaAjustesGrabaciones` (the folder/size-limit settings form).
|
||||
The settings form is not dropped: it stays reachable, but now from **within** the library, via a settings icon in its
|
||||
`PluriPushScaffold.actions` (matching the existing `pantalla_ajustes_timer_sueno.dart` "Add" action precedent for a
|
||||
real capability living in the header, not decorative chrome).
|
||||
|
||||
- [x] 15b.1 RED — re-targeted `pantalla_ajustes_test.dart`'s "tapping the Grabaciones row" scenario: it now asserts
|
||||
landing on `PantallaGrabaciones` (`find.text('My recordings')`), not the settings form (`find.text('Change
|
||||
path')` asserted absent).
|
||||
- [x] 15b.2 RED — added a new scenario to `pantalla_grabaciones_test.dart`: tapping the library's settings icon
|
||||
pushes a second `PluriPushScaffold` showing the settings form (`find.text('Change path')`).
|
||||
- [x] 15b.3 GREEN — `pantalla_ajustes.dart`: the "Grabaciones" `FilaAjuste.onTap` now pushes `PantallaGrabaciones`;
|
||||
removed the now-unused `PantallaAjustesGrabaciones` import from this file.
|
||||
- [x] 15b.4 GREEN — `pantalla_grabaciones.dart`: added `actions: [IconButton(Icons.settings_outlined, ...)]` to its
|
||||
`PluriPushScaffold`, pushing `PantallaAjustesGrabaciones`. One new ARB key (en/es only, matching precedent):
|
||||
`recordingsLibrarySettingsTooltip`.
|
||||
- [x] 15b.5 REFACTOR — confirmed the new test needed the same `_suppressListTileInkAssertion()` helper WU3a/WU3b
|
||||
established (WU3b Discovery 9: any pushed screen whose `PluriGlassSurface` body contains a tappable `ListTile`
|
||||
needs it) — added to `pantalla_grabaciones_test.dart`, scoped to only the one new scenario that mounts
|
||||
`PantallaAjustesGrabaciones`.
|
||||
- [x] 15b.6 Verify — scoped suite green: 605/605 (up from 604), 2 skipped (unchanged, both pre-existing WU15
|
||||
documented skips). `flutter analyze`: 1 issue, identical to baseline. Literal-encoding scan: zero hits.
|
||||
|
||||
|
||||
|
||||
**Commit**: `feat(connectivity): restyle offline and reconnect banners`
|
||||
**Depends on**: WU1
|
||||
|
||||
@@ -246,23 +246,30 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('tapping the Grabaciones row pushes its detail screen', (
|
||||
tester,
|
||||
) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
testWidgets(
|
||||
'tapping the Grabaciones row pushes the recordings LIBRARY, not the '
|
||||
'folder/size settings form (WU15b — the approved mockup screen '
|
||||
'"Ajustes > Grabaciones" depicts the library)',
|
||||
(tester) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstado();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildAjustes(estado));
|
||||
await pumpStable(tester);
|
||||
await tester.pumpWidget(buildAjustes(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
await tester.tap(find.text('Recordings'));
|
||||
await pumpStable(tester);
|
||||
await tester.tap(find.text('Recordings'));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
||||
expect(find.text('Change path'), findsOneWidget);
|
||||
});
|
||||
expect(find.byType(PluriPushScaffold), findsOneWidget);
|
||||
expect(find.text('My recordings'), findsOneWidget);
|
||||
// The folder/size settings form is reachable FROM the library now,
|
||||
// not directly from the Settings root row — see
|
||||
// pantalla_grabaciones_test.dart for that entry point.
|
||||
expect(find.text('Change path'), findsNothing);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('tapping the Info row pushes its detail screen', (
|
||||
tester,
|
||||
|
||||
@@ -24,6 +24,26 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
/// the real `share_plus` call, since this suite does not mock that channel
|
||||
/// either (see `pantalla_ajustes_backup_test.dart`'s note on the same
|
||||
/// constraint).
|
||||
///
|
||||
/// Pre-existing project constraint (see `pantalla_ajustes_test.dart`):
|
||||
/// PluriGlassSurface paints a background over ListTile's ink layer, which
|
||||
/// Flutter flags as a warning-level assertion, not a correctness bug. Only
|
||||
/// needed by the WU15b settings-affordance test below, which pushes
|
||||
/// PantallaAjustesGrabaciones (a ListTile-with-onTap screen) — this file's
|
||||
/// other scenarios never mount that screen.
|
||||
void _suppressListTileInkAssertion() {
|
||||
final original = FlutterError.onError;
|
||||
FlutterError.onError = (details) {
|
||||
if (details.exceptionAsString().contains(
|
||||
'ListTile background color or ink splashes may be invisible',
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
original?.call(details);
|
||||
};
|
||||
addTearDown(() => FlutterError.onError = original);
|
||||
}
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
@@ -95,6 +115,36 @@ void main() {
|
||||
expect(find.text('My recordings'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'WU15b: tapping the settings icon pushes the folder/size settings '
|
||||
'screen (PantallaAjustesGrabaciones stays reachable, now from within '
|
||||
'the library instead of directly from the Settings root row)',
|
||||
(tester) async {
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = EstadoGrabacion(
|
||||
servicio: _FakeServicioGrabacionConArchivos(
|
||||
const [],
|
||||
maxBytesFijo: 200 * 1024 * 1024,
|
||||
),
|
||||
);
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildScreen(
|
||||
estado: estado,
|
||||
reproductor: _ReproductorGrabacionesFake(const {}),
|
||||
),
|
||||
);
|
||||
await pumpStable(tester);
|
||||
|
||||
await tester.tap(find.byIcon(Icons.settings_outlined));
|
||||
await pumpStable(tester);
|
||||
|
||||
expect(find.byType(PluriPushScaffold), findsNWidgets(2));
|
||||
expect(find.text('Change path'), findsOneWidget);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('15.1: storage bar reflects 84 of 200 MB used', (tester) async {
|
||||
final estado = EstadoGrabacion(
|
||||
servicio: _FakeServicioGrabacionConArchivos([
|
||||
|
||||
Reference in New Issue
Block a user