No task waits on a Codex review; the redesign tasks are confirmed on the product owner's Z Fold7; TASK-016's leftovers are on hold; no biometric reauthentication. Agent: main (opus) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.1 KiB
6.1 KiB
TASK-037: Already-starred repositories show filled on the ALL list's first load
Status: implemented; all required checks pass; live-confirmed on the human product owner's device Owner: Claude Reviewer: Codex
Outcome
Closes a limitation accepted (not fixed) all the way back in TASK-018/TASK-030: a repository already starred before the current app session started an ALL-list session showing outlined (unstarred) until the human product owner directly tapped it. The ALL list now cross-references the account's actual starred set once per session and shows a filled star from the very first load for any repository that's genuinely already starred server-side.
Scope
- Included:
feature/repositories/RepositoryListViewModel.kt(loadStarredCoordinatesOnce,toUiModelgains analreadyStarredparameter,toggleFavoritekeeps the new cache in sync,refreshRepositories/logoutinvalidate/clear it), matching test additions inRepositoryListViewModelTest.kt. - Excluded: No change to the FAVORITES list (every row there is inherently already starred, and
FavoriteStar's rendering for that mode never consultedfavoriteActionin the first place — seeRepositoryListScreen.kt's existing doc comment). No per-row network call — this is one list fetch for the whole page, not N+1 (see Design notes).
Design notes
- One request for the whole list, not one per row: Gitea's plain repository-list response has
no "is this starred by me" field, but the account's full starred set is already reachable via the
same
/user/starredendpoint this app already calls for the FAVORITES tab (favoritesApi.listStarred).loadStarredCoordinatesOncefetches and caches that whole set once per session (mirroringloadOrganizationsOnce's exact shape: cache-check, try/catch with a non-fatal empty fallback on failure, cache the result either way) rather than calling Gitea's per-repositoryIsStarringendpoint once per row, which would turn a single list load into N+1 requests. - The cache stays self-consistent without extra network calls:
toggleFavorite's success paths now also updatestarredCoordinatesCachedirectly (+/-the toggled repository) instead of leaving it stale until the next full refetch — so switching between ALL/FAVORITES, paging, or reloading later in the same session keeps reflecting what this session itself just did, not just what the very first load saw. - Invalidated exactly where the equivalent
repositorySnapshotscache already is:refreshRepositories()(pull-to-refresh) clears it alongside the snapshot removal it already does, andlogout()clears it alongsidecachedOrganizations/repositorySnapshots. Deliberately not cleared inclearSessionOnAuthenticationExpired(), matching that those two existing caches aren't cleared there either — consistency with the established (if arguably imperfect) precedent was chosen over introducing a new asymmetry just for this one cache.
Acceptance criteria
- A repository the account has already starred (independent of this app session) shows a filled, primary-colored star on the ALL list's very first load, with no prior interaction.
- A repository not starred still shows outlined, as before.
- Toggling a star/unstar keeps working exactly as TASK-034 left it, and additionally keeps the new cache consistent for any later reload within the same session.
- Pull-to-refresh re-derives the starred set fresh rather than reusing a stale cache.
- Build, unit tests, and lint pass.
- Live-confirmed on the human product owner's device: a repository independently verified as
starred via the FAVORITES tab (
LGE-OC6-Inline-CylinderTypeWelder, ★ 1) showed filled on the ALL list immediately after a fresh app launch, screenshot-verified.
Required checks
./gradlew compileDebugKotlin compileDebugUnitTestKotlin
./gradlew testDebugUnitTest
./gradlew assembleDebug lintDebug
Evidence
RepositoryListViewModel.kt: newstarredCoordinatesCachefield; newloadStarredCoordinatesOnce();loadRepositoryPagecomputes analreadyStarredset for ALL-mode loads and passes it throughtoUiModel(alreadyStarred = ...);toggleFavorite's success paths update the cache;refreshRepositories()/logout()invalidate/clear it.RepositoryListViewModelTest.kt: two new tests — a repository already starred showsStarred(filled) on first load while an unstarred sibling showsIdle; un-starring an already-session- known-starred row and then reloading the ALL list (viashowAllRepositories()) showsIdle, proving the cache itself (not just the immediately-visible row) was updated. All 28 tests in this file pass, including every pre-existing one, unmodified../gradlew compileDebugKotlin compileDebugUnitTestKotlin: BUILD SUCCESSFUL../gradlew testDebugUnitTest: BUILD SUCCESSFUL, all unit tests passing../gradlew assembleDebug lintDebug: BUILD SUCCESSFUL, 0 lint errors.- Installed on the human product owner's device over wireless
adb. Live-verified: confirmedRobotAutomationDevelopment-Dept/LGE-OC6-Inline-CylinderTypeWelderwas genuinely starred server-side via the FAVORITES tab (★ 1 shown there), then located the same repository on the ALL list (page 2) after a fresh app launch and confirmed it showed a filled green ★ 1 immediately, with no tap needed — screenshot-verified, no crash inlogcat.
Known limitations
- Still session-scoped, not persisted across app restarts independent of this cache's own invalidation rules — this was always going to require re-fetching the starred set on a fresh session anyway, since Gitea (not this app) is the source of truth for who has starred what.
- If the account's starred set changes from somewhere else (the Gitea web UI, another device)
mid-session, the ALL list won't reflect that until the next
refreshRepositories()(pull-to-refresh) or a full logout/login — the same staleness window every other cached list in this app already has.
Review findings
None yet — no Codex review pending (Codex retired, 2026-09-29).