Files
pluriwave/openspec/changes/rediseno-funcional/specs/station-discovery-browse/spec.md
T

5.9 KiB

Station Discovery & Browse Specification

Purpose

Discovery content as the Buscar tab's landing state, active-filter pills, results counter, clear-all-filters, the Países browser over the verified /json/countries contract, and a client-side search-sort control. Traceable to WU5 (relocation), WU6 (Buscar), WU7 (Países + _get transport extraction).

Requirements

Requirement: Buscar Landing State Shows Discovery Content

Buscar MUST render the existing discovery content (near-you, genres, trending, countries entry point) as its landing state whenever no search query is entered. Entering a query MUST replace that landing state with search results.

Scenario: Empty query shows discovery content

  • GIVEN the user opens the Buscar tab with no prior query
  • WHEN the screen renders
  • THEN near-you, genre, trending, and a Países entry point are visible

Scenario: Entering a query replaces the landing state

  • GIVEN the Buscar tab is showing discovery content
  • WHEN the user types a non-empty query
  • THEN discovery content is replaced by the search-results view

Requirement: Active-Filter Pills and Results Counter

The search-results view MUST show each active filter (country, language, minimum quality) as a removable pill, and MUST show a results counter ("N RESULTADOS").

Scenario: Applying a filter shows a removable pill

  • GIVEN the user has an active search
  • WHEN they apply a country filter
  • THEN a pill labeled with that country and a close (x) affordance appears
  • AND the results counter reflects the filtered count

Scenario: Removing a pill re-runs the search without that filter

  • GIVEN a country filter pill is active
  • WHEN the user taps its close affordance
  • THEN the filter is cleared and results/count update accordingly

Requirement: One-Tap Clear-All-Filters on Empty Results

When 1 or more filters are active and the resulting search has zero results, the system MUST offer a single action that clears every active filter at once ("Quitar los N filtros").

Scenario: Two active filters, zero results

  • GIVEN country and quality filters are both active and yield 0 results
  • WHEN the empty state renders
  • THEN a "Quitar los 2 filtros" action is shown
  • AND tapping it clears both filters in one action, not one at a time

Requirement: Países Browser Over the Verified Countries Contract

The Países screen (new second-level screen) MUST fetch from /json/countries (optional substring filter /json/countries/{filter}) and MUST NOT use /json/countrycodes, which is deprecated. It MUST parse the stationcount field as a string via int.tryParse, never as a direct int cast. Its request MUST NOT include lastcheckok, since that parameter is meaningful only for station-listing calls. Source: Engram reference/radio-browser-countries-endpoint (id 2500), verified against https://api.radio-browser.info/.

Scenario: Countries list renders with counts

  • GIVEN /json/countries returns a list of country objects
  • WHEN the Países screen loads
  • THEN "Tus idiomas" and the full alphabetical list render with each country's parsed station count

Scenario: stationcount arrives as a JSON string (edge case, critical)

  • GIVEN a country entry has "stationcount": "482" (a JSON string, not a number)
  • WHEN the screen parses and renders that entry
  • THEN it displays 482 without throwing a type-cast error

Scenario: Countries request omits the station-only filter

  • GIVEN the Países screen issues its /json/countries request
  • WHEN the outgoing request is inspected
  • THEN it does NOT include lastcheckok

Requirement: Client-Side Search Sort Only

Buscar MUST expose an "Ordenar" control backed exclusively by client-side ordering (OrdenEmisoras). The system MUST NOT adopt the Radio Browser server-side order/reverse parameters for this control, because server-side ordering sorts the full result set before limit is applied while the app's client-side sort only reorders the page already fetched — adopting it would silently change which stations users see, not just their order. Every sort option rendered in the UI MUST map to a real OrdenEmisoras case; the system MUST NOT render an option that does not actually sort. Source: Engram reference/radio-browser-sort-order (id 2505), verified against https://api.radio-browser.info/.

Scenario: Selecting a sort option reorders the current page only

  • GIVEN a set of search results is already fetched
  • WHEN the user selects "Ordenar: calidad"
  • THEN the currently-displayed page is reordered via OrdenEmisoras.calidad
  • AND no new network request with an order parameter is issued

Scenario: Every rendered sort option is backed by a real case (regression guard)

  • GIVEN the "Ordenar" control's rendered option list
  • WHEN each option is checked against OrdenEmisoras
  • THEN every option maps to an existing enum case with test coverage; no decorative option that does not sort is rendered

Requirement: Existing Station Calls Unchanged by Transport Extraction

Extracting _get's transport loop (server discovery, host rotation, bounded retries, User-Agent, timeout, status check, json.decode) into a shared helper for the Países call MUST NOT change the observable behavior of the 8 existing ServicioRadio station methods: obtenerPopulares, obtenerTendencias, buscarPorNombre, buscarPorPais, buscarPorIdioma, buscarPorTag, buscar, registrarClick.

Scenario: Characterization tests pass unchanged before and after extraction

  • GIVEN characterization tests exist for all 8 station methods
  • WHEN the transport loop is extracted from _get
  • THEN all 8 tests pass identically before and after the extraction

Scenario: Station calls still send lastcheckok

  • GIVEN any of the 8 station methods issues a request
  • WHEN the outgoing request is inspected
  • THEN it still includes lastcheckok=1, unchanged from before the extraction