docs(sdd): add delta specs for the functional redesign
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user