fix(trakt): first connect survives network drops and shows the full history at once - #759
Merged
ProdigyV21 merged 24 commits intoSep 27, 2026
Merged
Conversation
Home marked its cards only when a Trakt token was present, while Search, Discover and Details already mark from the same watched cache for every profile (local, Cloud, MDBList, SIMKL). Drop the Trakt gate, index the episode history once into started show ids instead of scanning it per show, and mark the latest UI state instead of writing back a snapshot taken before the reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvQSEcH38SjW7hjUY8TvSs
The tick pass replaced the hero with the plain row card, dropping the runtime, ratings, budget and network logo that are hydrated into the hero only; it now takes over just the tick. A profile switch reset every runtime state except the tick throttle, so the next profile could wait up to 90 s for its ticks. The history index and the state update run on Dispatchers.Default, as in Search, instead of the network pool. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvQSEcH38SjW7hjUY8TvSs
On a device no tick ever showed: the startup cache rows were marked and started the 90 s throttle, then the catalog load published fresh, unmarked rows and its tick pass was throttled away; nothing asked again. The throttle now only skips a pass when the rows are the very ones the last pass marked. Every new set of rows (startup cache, catalog load, next page, cloud reload) triggers a debounced pass, and Home resuming (back from Details) forces one, as Search does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvQSEcH38SjW7hjUY8TvSs
The pass Home asks for on resume waited the full startup pause (3 s on most phones, memoryClass <= 256) although the cache is loaded and the pass takes milliseconds. Once a pass has run, a resume pass now only debounces for 300 ms; the first pass after launch or a profile switch keeps the startup pause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvQSEcH38SjW7hjUY8TvSs
On a phone the ticks showed after the startup pass, vanished when the catalog load re-published the rows (unmarked) and came back 3 s later. New rows now take over what the last pass found at once; a full pass still runs when no pass has run yet or on resume. The marking of rows and hero moved into HomeUiState.withWatchedBadges, shared by both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RvQSEcH38SjW7hjUY8TvSs
… history at once - Device-code polling treated any error without an HTTP code as fatal, so a DNS or timeout blip while the user approves the code in a browser ended the sign-in with a raw exception text. Such IO failures of the poll itself are now retried until the code expires. - The silent full sync after connecting is started before the Continue Watching fetch, so the sync summary no longer waits ~10 s for it. - Without a cloud account the sync service only holds the last full sync in memory, which expands just the 15 most recent shows when a show was rewatched. The watched cache took that list instead of Trakt, so Home lost most series ticks until the app restarted. Trakt is now read unless the history comes from the cloud account. - The watched cache reports a reload after an invalidation and Home marks its rows then: COMPLETED goes out before the reload, so Home read the old cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WAwV5pRZCPBFLm8WQ7FULi
…nected, guard stale cache loads - Polls that fail without reaching Trakt are remembered: if the code runs out that way, the network error is reported instead of a plain expiry, and a 409 right after such a failure (token issued, answer lost) no longer claims the code was reused. Certificate failures are not retried. - The watched cache reads Trakt whenever it is connected. The Supabase reads are empty with USE_NETLIFY_CLOUD_SYNC, and without an account the sync service only holds the last (possibly partial) full sync, so the extra account check was not needed. - A cache load overtaken by an invalidation or a profile switch no longer marks the cache initialized with its old data; a caller that waited for it loads the current state instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WAwV5pRZCPBFLm8WQ7FULi
Cards, the hero (TV and phone), the featured trailer card, the Home backdrop preload and the details backdrop rewrite image.tmdb.org URLs to the smallest official TMDB width that covers the slot instead of 'original' (1-2 MB per card). Stored URLs stay unchanged, other hosts are untouched, and a URL is never rewritten to a larger size. A portrait card without a poster is sized for the backdrop it shows. The preload now uses the row's card width (TV 210 dp, phone 200 dp) and Compose's rounding, so preload and card share one cache entry. The TV details backdrop is sized like the Home hero, so both share one file. Once startup has settled, the focused item's hero logo moves from BACKGROUND to DEFERRED so it no longer waits behind the single-slot card-logo fan-out; during startup the initial rows keep DEFERRED. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qy4WX7LN7PngMAhsMQCJC8
…8974y feat(home): show watched ticks without a Trakt sign-in
…eopened Sources - A request Telegram did not answer in time now makes the whole lookup incomplete, so it is never cached as "no results" (the per-request 20s timeout used to look like an empty answer). - When a phrasing's unfiltered first page has more results behind it, the phrasing is repeated with the video and document filters, so newer text or photo posts cannot push the files off the only page read. Pages with nothing behind them still cost one request. - Telegram results found by a click-search are merged into every saved source list that Sources returns (fresh, stale, disk and addon-less paths), so reopening Sources keeps them, including with Telegram as the only provider. - The phrase loop and the result cache moved into TelegramPhraseSearch / TelegramResultCache (no Android dependencies) with regression tests for a per-request timeout, a cut-off first page, and reopening after a click-search (including Telegram-only).
Subtitle auto-sync: whole-file reference (Matroska + MP4)
fix(telegram): search only on click, stop search floods and lost Telegram sources
…jan-5roog1 perf(home): load TMDB artwork at the size each slot draws
Owner
|
Thanks for the Trakt reliability work. I kept the reconnect/history improvements and added serialized cache loading, stale profile/generation checks, independent provider fallback, and regression coverage. The combined batch passes 128 targeted unit tests and both Play/Sideload Kotlin compilation. Merging with your original commits and authorship preserved; live third-party account flows were not exercised. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Connecting Trakt for the first time went wrong in three places (reproduced on a phone with logcat, profile without a cloud account):
Unable to resolve host "api.trakt.tv"toast. While the user approves the code in a browser, ARVIO sits in the background and the phone briefly loses DNS. The polling loop treated any error without an HTTP code as fatal and ended the sign-in.TraktSyncService.getWatchedMovies/Episodes(), which without a Supabase account return what the last full sync kept in memory. When a show was rewatched, that sync expands only the 15 most recent shows (useProgressExpansion), so the cache shrank to 1170 episode keys and Trakt was skipped because the list was not empty: Home went from 151 started shows to 15. Home was not told about the reload either, becauseCOMPLETEDgoes out before it.Changes
isTransientTraktPollFailure: anIOExceptionof the poll itself (DNS, timeout, dropped connection) no longer ends the activation. Only the poll is retried, not the work after the token is stored. Certificate failures are excluded because they do not heal by waiting. If the code runs out while the network stays down, the network error is reported instead of a plain expiry. A 409 right after such a failure (token issued, answer lost) reports that error instead of "code already used".performFullSyncruns in its own job anyway, so the summary no longer waits for Continue Watching. On the device the summary was filled ~5 s after the token instead of ~14 s.USE_NETLIFY_CLOUD_SYNCthe Supabase reads are empty anyway, and without an account the in-memory sync result cannot stand in for the Trakt history.TraktRepository.watchedCacheReloaded, a smallWatchedCacheReloadshelper) and runsrefreshWatchedBadges(immediate = true). The first load after start or a profile switch is not reported, because Home runs its own pass for it.invalidateWatchedCache()or a profile switch used to mark the cache initialized with its old data. Now a generation counter keeps it from doing that, and a caller that waited for it loads the current state. The parallel sync made this more likely, and the reload event would have carried it to Home.Notes, not changed here
TraktSyncWorkerrefreshes the in-memory list without invalidating the cache. This is unchanged.HomeViewModelnext to feat(home): show watched ticks without a Trakt sign-in #758, and the two merge without conflicts.Testing
TraktWatchedCacheLoadTest(Robolectric): Trakt is read even when the sync left a partial list in memory. A load overtaken by an invalidation does not keep its old data. Both fail onmain.WatchedCacheReloadsTest: only a reload after an invalidation is reported, and a profile reset drops a pending one.TraktDeviceActivationTest: DNS, timeout and connect failures are transient; HTTP 404/410, missing credentials and certificate failures are not.testSideloadDebugUnitTest: 1823 tests, 0 failures.created by Claude (Anthropic) on behalf of @ReichiMD