Skip to content

feat(app): split up to date progress filter into up to date and ended - #354

Merged
michaldrabik merged 3 commits into
trakt:mainfrom
matheus-souza:feat/progress-ended-filter
Sep 27, 2026
Merged

michaldrabik merged 3 commits into
trakt:mainfrom
matheus-souza:feat/progress-ended-filter

Conversation

@matheus-souza

@matheus-souza matheus-souza commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

Closes #353

Scoped to the split alone: no counts, no settings toggle, no API change. Mirrors trakt/trakt-web#3211, which does the same thing on the web.

Why

The Up to date filter in Profile > Progress answers two different questions at once. It returns every show whose aired episodes are all watched, so a series that wrapped up years ago sits next to a series that is still airing and simply has nothing unwatched right now. From the Progress section there is no way to tell which shows are actually finished without opening each one.

What changed

Up to date becomes two filters: Up to date (still airing, caught up) and Ended (status ended or canceled). In progress and Dropped are untouched.

  • No API change. up_next_nitro returns the full show object with status, so both filters read the same intent=completed bucket and partition it on that status. Verified against the live API: 16 completed, 10 ended, 6 returning series, 0 without a status.
  • Show.hasEnded sits in :common next to Show.isReleased, since it is a question about a show rather than about this screen. An unknown status counts as still airing, which preserves today's behaviour.
  • Paging keys off the raw page size. This is the one subtlety. Partitioning a page client-side shrinks it, and the pager previously decided hasMoreData from the size of what it received. Left alone, a page of 100 completed shows that filters down to 8 ended ones would have ended pagination early. ProgressPage carries the count the endpoint actually returned alongside the filtered items, and the pager reads that.
  • The chips need no UI change. ProgressFilters already iterates ProgressFilter.entries, so adding the enum entry is enough. ic_flag_checker already existed.
  • The ProgressFilter entry Completed became UpToDate, so a stored "Completed" no longer parses. GetProgressFilterUseCase already falls back to the default on an unknown value, and the default is UpToDate, which carries the same label users saw before. No migration needed.

Localisation

No new translatable keys. Up to date keeps button_text_progress_completed and Ended reuses translated_value_status_ended, both already translated in every locale, so this ships localised rather than waiting on a Crowdin round trip.

Notes

One consequence of partitioning client-side: the profile Progress row asks for PROGRESS_SECTION_LIMIT items and then splits them, so that preview row can show fewer than the limit. The all-progress screen is unaffected, since it pages. Calling it out rather than hiding it; the alternative would be over-fetching to fill the row, which is not worth it for a preview.

Testing

./gradlew :app:compileInternalDebugKotlin clean, ktlint 1.7.1 (the version CI runs) clean.

@michaldrabik

Copy link
Copy Markdown
Collaborator

Hi and thanks for the PR,
as the general rule we try to keep our apps in parity with what's happening on the website

If you wish to introduce something new into the app that's NOT already available in the website please open PR for the web first - or even better start with an Github issue or Featurebase request. This way tokens and time will not be wasted ;)

If you spot something thats already on web but not yet in the app then PR is welcome surely. If this one contains something like this partially please extract this in a new separate smaller one.

Thanks! This info should be available in the readme actually. I will be adding this now and also copy this message to other PRs.

@michaldrabik

Copy link
Copy Markdown
Collaborator

For this one specifically this is something we would need to introduce on the backend/api side first but I agree with the idea in general.

@matheus-souza

matheus-souza commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the steer - I took it to the web first.

trakt/trakt-web#3210 (feature request) and trakt/trakt-web#3211 (implementation) now cover the split there.

Checking the endpoint against my account while writing that up settled the API question: the split needs no API change. up_next_nitro returns the full show object today, status included, even though the contract documents status as only available with extended=full:

GET /sync/progress/up_next_nitro?intent=completed&limit=100
  16 shows -> 10 ended, 6 returning series, 0 without a status

The web already maps that value onto ProgressEntry.show.status via mapToShowEntry and simply does not use it, so the web PR is a pure client-side change sharing one query between both filters.

On this PR: I have reduced it to the split alone - the counts and the settings toggle are gone, since those were the part that would have needed server-side work. What is left is just the filter.

@matheus-souza

matheus-souza commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Reduced this to the split alone, as promised above.

26 files / +445 -204 → 10 files / +112 -24. Gone: the per-chip counts, the Show Progress Counts setting, ProgressCountsUseCase, ProgressOverview, LoadAllPages and the full-bucket paging it drove, plus the two new strings. The original per-page pagination from main is back.

What is left needs no API change: both filters read the same intent=completed bucket and split it on the status the endpoint already returns.

One thing worth flagging, since it was easy to get wrong. Partitioning a page client-side shrinks it, and the pager decided hasMoreData from the size of what it received. Left alone, a page of 100 completed shows filtering down to 8 ended ones would have stopped pagination early. ProgressPage now carries the count the endpoint actually returned alongside the filtered items, and the pager reads that instead.

Same change is up for the web in trakt/trakt-web#3211, following the parity guidance.

@matheus-souza

Copy link
Copy Markdown
Contributor Author

Update on the parity condition: the web counterpart has landed.

trakt/trakt-web#3211 was approved by @Marius-TV and merged into main on Sep 8. It ships the same split this PR does, with the same shape:

  • one shared intent=completed query, partitioned client-side on the show status
  • Up to date for still-airing shows you are caught up on, Ended for ended / canceled
  • a hasEnded() util next to the other media utils, so the classification is one place
  • no API change - which also answers the backend concern raised above: the status already comes back on the show object, so nothing new was needed server-side

So the web is now the reference implementation and this PR mirrors it rather than leading it. Naming matches (Up to date / Ended), the partition rule matches (ended or canceled, unknown status counts as still airing), and the scope matches - split only, no counts, no settings toggle.

Happy to rebase or adjust anything here if you would rather this track the merged web version more closely in any particular detail.

@matheus-souza
matheus-souza force-pushed the feat/progress-ended-filter branch from 79e94e3 to 91ec0ef Compare September 9, 2026 17:41
@matheus-souza

Copy link
Copy Markdown
Contributor Author

@michaldrabik the parity condition you raised is now cleared, and this PR is ready on the Android side.

Web: trakt/trakt-web#3211 was approved by @Marius-TV and merged into main on Sep 8. It also answers the backend point - the split needed no API change, because the show status already comes back on the up next response, so both filters read the same completed bucket and partition it client-side.

Android: this PR mirrors that implementation - same naming (Up to date / Ended), same partition rule (ended or canceled, unknown status counts as still airing), same scope (split only: no counts, no settings toggle, no API change).

Just rebased onto main (it was 34 commits behind). No conflicts, and no upstream commit has touched any of the 10 files this changes. Verified after the rebase:

./gradlew :app:compileInternalDebugKotlin    BUILD SUCCESSFUL
ktlint 1.7.1                                 clean on every changed file

Happy to adjust anything if you would rather it track the merged web version more closely in some detail.

matheus-souza and others added 3 commits September 27, 2026 22:28
The "Up to date" filter answers two questions at once. It lists every show whose
aired episodes are all watched, so a series that wrapped up years ago sits next
to one that is still airing and simply has nothing unwatched right now. From the
progress list there is no way to tell them apart without opening each show.

The up next endpoint returns the show status already, so the split needs no API
change. Both filters read the same completed bucket and partition it on that
status; an unknown status counts as still airing, which preserves today's
behaviour.

Paging keys off the number of items the endpoint returned rather than the number
left after the split, so a page that filters down to fewer than the page limit
does not end pagination early.
@michaldrabik
michaldrabik force-pushed the feat/progress-ended-filter branch from 91ec0ef to aaaba6f Compare September 27, 2026 20:54
@michaldrabik
michaldrabik merged commit 44402e4 into trakt:main Sep 27, 2026
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.

Split the "Up to date" progress filter into up to date and ended, with counts on each filter

2 participants