Skip to content

feat(catalog): sortable results, sub-category filter, recently-added heading#25

Merged
fabiodalez-dev merged 4 commits into
mainfrom
feat/catalog-sort-subcategory-heading
Jul 9, 2026
Merged

feat(catalog): sortable results, sub-category filter, recently-added heading#25
fabiodalez-dev merged 4 commits into
mainfrom
feat/catalog-sort-subcategory-heading

Conversation

@fabiodalez-dev

@fabiodalez-dev fabiodalez-dev commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Addresses Uwe's in-app remarks (a real lending-library user): the opening list had no heading, results were only sortable by cataloguing date, and there was no sub-category filtering.

What's new

  • Sort picker — Newest / Oldest / Title A–Z / Title Z–A, sent as sort= to GET /api/v1/catalog/search (backend support: Pinakes PR #242). Changing sort resets pagination. The active order is shown as "Sort: " in the results header.
  • Sub-category filter — drill down into a category to filter by a specific sub-category (sent as the genre id, which the API matches at any level). Each level is labelled "Within ".
  • Recently-added heading — the home shelf now carries a "what am I looking at" heading; for lending libraries the subtitle conveys recency ("newest titles ready to borrow") so the reporter actually sees it, and the shelf query is aligned with the label.

Reviewed + hardened

Ran an adversarial multi-lens review on the diff; all findings fixed in the same branch:

  • Race guard: a sort change during an in-flight loadMore/search could append a stale-sort page + restore a mismatched cursor → a generation counter now drops superseded results.
  • Home heading reaches loan-mode users (was gated to catalogue-mode only) and its query matches the label.
  • Sort discoverability + TalkBack stateDescription/selected semantics.
  • Sub-category breadcrumb (parent name per level).
  • genrePath() depth cap (StackOverflow guard).
  • i18n complete + translated across en/it/fr/de.

./gradlew :app:compileDebugKotlin builds clean. Not yet built to an APK / run in the emulator.

Summary by CodeRabbit

  • New Features

    • Aggiunto l’ordinamento nelle ricerche con nuove opzioni e selezione da menu.
    • La schermata Home mostra contenuti e testi più coerenti con la modalità catalogo.
    • I filtri di genere ora supportano una navigazione più profonda tra categorie e sottocategorie.
  • Bug Fixes

    • Migliorata la stabilità dei risultati di ricerca durante cambio filtro, ordinamento o caricamento di altre pagine.
    • Aggiornate le traduzioni per le nuove etichette e descrizioni nell’interfaccia.

…loan mode, sort/pagination race guard, sort discoverability + a11y, sub-category breadcrumb, genrePath depth cap
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@fabiodalez-dev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6c0120b0-b037-48ae-a49d-b58ebf410a85

📥 Commits

Reviewing files that changed from the base of the PR and between c0e601d and 58a4569.

📒 Files selected for processing (1)
  • app/src/main/java/com/pinakes/app/ui/screens/home/HomeViewModel.kt

Walkthrough

Introdotto un parametro di ordinamento (sort) end-to-end dal contratto OpenAPI fino all'UI di ricerca, con enum BookSort e protezione da risultati stale. Aggiunta modalità catalogo-only nella Home tramite FeatureStore. Migliorata la navigazione a cascata dei generi nei filtri. Aggiornate traduzioni i18n in quattro lingue.

Changes

Ordinamento catalogo, modalità Home e filtri genere

Layer / File(s) Summary
Contratto API: parametro sort
_contract/openapi.json, _contract/MOBILE_API_SPEC.md
Aggiunto il parametro query sort (enum newest/oldest/title_asc/title_desc, default newest) all'endpoint /catalog/search e sincronizzata la documentazione del manifest API.
Propagazione sort su API/repository
.../data/network/PinakesApi.kt, .../data/repository/CatalogRepository.kt
Aggiunto il parametro opzionale sort a PinakesApi.search e CatalogRepository.search, inviato al backend solo se non vuoto.
BookSort e stato di ricerca anti-stale
.../ui/screens/search/SearchViewModel.kt
Introdotta l'enum BookSort, il campo sort in SearchUiState, il metodo setSort, e searchGeneration per scartare risultati obsoleti in loadMore/runSearch.
UI ordinamento nella ricerca
.../ui/screens/search/SearchScreen.kt
Aggiunto il composable SortMenu con dropdown su BookSort.entries, wiring a state.sort/vm.setSort ed etichetta di ordinamento nell'header risultati.
Modalità catalogo-only nella Home
.../ui/screens/home/HomeViewModel.kt, .../ui/screens/home/HomeScreen.kt
Iniettato FeatureStore in HomeViewModel; observeCache/refresh adattano filtri e cache in base a catalogueMode; SectionHeader mostra titolo/sottotitolo condizionali.
Navigazione a cascata dei generi
.../ui/screens/search/SearchFilterSheet.kt
Aggiunta la funzione genrePath (DFS con limite profondità e protezione cicli) e riscritto il rendering dei chip per evidenziare il percorso genere selezionato su più livelli.
Traduzioni sort/Home/filtri
i18n/de.json, i18n/en.json, i18n/fr.json, i18n/it.json
Aggiunte le chiavi cd_sort, sort_*, home_section_recent*, filters_section_subgenre/within e aggiornato home_section_available_subtitle in tutte le lingue.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SearchScreen
  participant SearchViewModel
  participant CatalogRepository
  participant PinakesApi

  SearchScreen->>SearchViewModel: setSort(BookSort)
  SearchViewModel->>SearchViewModel: incrementa searchGeneration, aggiorna stato
  SearchViewModel->>CatalogRepository: search(filters, sort=apiValue)
  CatalogRepository->>PinakesApi: search(sort=sort?.takeIf notBlank)
  PinakesApi-->>CatalogRepository: risultati catalog/search?sort=...
  CatalogRepository-->>SearchViewModel: ApiResult<SearchPage>
  SearchViewModel->>SearchViewModel: verifica generazione, scarta se stale
  SearchViewModel-->>SearchScreen: nuovo stato con risultati/sort attivo
Loading

Possibly related PRs

  • fabiodalez-dev/Pinakes-Android#9: entrambe le PR modificano HomeViewModel/CatalogRepository sulla logica di lettura da cache e refresh del catalogo (observeCache/refresh).

Poem

Un coniglio ordina i libri in fila,
newest, oldest, dalla A alla Z tranquilla 🐰
Nella tana catalogo-only si scopre,
generi a cascata, radici e foglie che si aprono,
con salti tra le lingue, quattro voci in coro —
*click* sul menu, e tutto è in ordine d'oro! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Il titolo riassume bene i tre cambi principali: risultati ordinabili, filtro per sottocategorie e intestazione dei contenuti recenti.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/catalog-sort-subcategory-heading

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

… contract

- observeCache() offline fallback filtered to available copies unconditionally, so
  in catalogue-only mode the "Recently added" shelf still hid unavailable titles
  from the cached snapshot — the same label/data mismatch the refresh() path was
  fixed for. Branch it on catalogueMode too (unfiltered in catalogue mode).
- Sync the app's API contract (_contract/openapi.json + MOBILE_API_SPEC.md) with
  the new catalog-search `sort` param (newest/oldest/title_asc/title_desc).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/main/java/com/pinakes/app/ui/screens/home/HomeViewModel.kt (1)

94-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Aggiornare il KDoc di refresh() e hasFreshShelf per il comportamento condizionale.

Il KDoc di refresh() (righe 85-91) descrive ancora lo shelf come "the server-side available=true query", ma in modalità catalogo-only la query è senza filtro di disponibilità. Anche il KDoc di hasFreshShelf (righe 43-48) menziona "server-side available=true page" che non è più sempre vero.

📝 KDoc aggiornato per refresh()
 /**
  * Refresh the shelf and the offline cache concurrently. The shelf is the server-side
- * `available=true` query (the catalog can hold hundreds of titles whose newest page is
- * fully on loan — only the server knows what's loanable now); the unfiltered first
- * page keeps feeding the Room cache for offline starts. A shelf failure only surfaces
- * an error when there is nothing cached to fall back on.
+ * query filtered by availability in loan mode, or unfiltered in catalogue-only mode
+ * (the catalog can hold hundreds of titles whose newest page is fully on loan — only
+ * the server knows what's loanable now); the unfiltered first page keeps feeding the
+ * Room cache for offline starts. A shelf failure only surfaces an error when there
+ * is nothing cached to fall back on.
  */
📝 KDoc aggiornato per hasFreshShelf
 /**
- * True once a server-side `available=true` page has been rendered this session. From
+ * True once a server-side shelf page has been rendered this session. From
  * that point the shelf is authoritative and cache emissions must not overwrite it —
  * the cache only holds the first unfiltered page, a subset of the real answer.
  */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/pinakes/app/ui/screens/home/HomeViewModel.kt` around
lines 94 - 101, The KDoc for refresh() and hasFreshShelf in HomeViewModel is now
inaccurate because the shelf query is conditional: catalogue mode uses an
unfiltered newest-titles search, while loan mode still uses availableOnly=true.
Update the documentation on refresh() and hasFreshShelf to describe this
branching behavior and remove the claim that the shelf is always a server-side
available=true query, keeping the wording aligned with the SearchFilters logic
and shelf search in refresh().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/pinakes/app/ui/screens/home/HomeViewModel.kt`:
- Around line 94-101: HomeViewModel is reading features.features.value only once
in refresh(), so the shelf can be built with the old catalogueMode before
FeatureStore updates. Update HomeViewModel to observe features.features and
trigger refresh() when catalogueMode changes, or delay initial home loading
until the feature refresh completes. Use the existing refresh(), observeCache(),
and hasFreshShelf flow to ensure the shelf is rebuilt with the current mode
instead of being kept stale.

---

Nitpick comments:
In `@app/src/main/java/com/pinakes/app/ui/screens/home/HomeViewModel.kt`:
- Around line 94-101: The KDoc for refresh() and hasFreshShelf in HomeViewModel
is now inaccurate because the shelf query is conditional: catalogue mode uses an
unfiltered newest-titles search, while loan mode still uses availableOnly=true.
Update the documentation on refresh() and hasFreshShelf to describe this
branching behavior and remove the claim that the shelf is always a server-side
available=true query, keeping the wording aligned with the SearchFilters logic
and shelf search in refresh().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b35a5acd-8bc4-41a1-bdea-ad4f2d5aed8b

📥 Commits

Reviewing files that changed from the base of the PR and between cfdbf4f and c0e601d.

📒 Files selected for processing (13)
  • _contract/MOBILE_API_SPEC.md
  • _contract/openapi.json
  • app/src/main/java/com/pinakes/app/data/network/PinakesApi.kt
  • app/src/main/java/com/pinakes/app/data/repository/CatalogRepository.kt
  • app/src/main/java/com/pinakes/app/ui/screens/home/HomeScreen.kt
  • app/src/main/java/com/pinakes/app/ui/screens/home/HomeViewModel.kt
  • app/src/main/java/com/pinakes/app/ui/screens/search/SearchFilterSheet.kt
  • app/src/main/java/com/pinakes/app/ui/screens/search/SearchScreen.kt
  • app/src/main/java/com/pinakes/app/ui/screens/search/SearchViewModel.kt
  • i18n/de.json
  • i18n/en.json
  • i18n/fr.json
  • i18n/it.json

Comment thread app/src/main/java/com/pinakes/app/ui/screens/home/HomeViewModel.kt
CodeRabbit (#25): HomeViewModel read features.features.value.catalogueMode once, so
a /health that resolves after the first shelf load — or a runtime lending<->catalogue
switch — didn't re-drive the shelf, leaving it filtered/labelled for the wrong mode
until a manual refresh. Now refresh() is driven by
features.features.map { catalogueMode }.distinctUntilChanged() (collectLatest, so a
flip mid-flight abandons the stale re-drive and distinctUntilChanged avoids a network
storm), and observeCache() combines the cached-catalog flow with the same flag flow
so the offline fallback re-filters too. compileDebugKotlin clean.
@fabiodalez-dev fabiodalez-dev merged commit 4c8522f into main Jul 9, 2026
1 of 2 checks passed
@fabiodalez-dev fabiodalez-dev deleted the feat/catalog-sort-subcategory-heading branch July 9, 2026 17:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant