Live, iterative round of changes driven by the human product owner testing on their own device over wireless adb: - Tapping a branch/tag opens commit history instead of the file browser - Selected reference row is color-coded instead of a text label - Branch/tag rows and commit rows show commit date/time with time zone - Tapping a changed file shows a parsed before/after diff, client-side (new UnifiedDiffParser, no extra network call) - The two diff panels scroll vertically in sync and share one horizontal scroll position - Diff lines are colored by text only (red/green) - On a >=600dp-wide screen (a Z Fold's inner screen unfolded) the file list and selected diff show side by side - Removed the branch/tag compare feature entirely, per direct request Also fixes a real bug found via live device debugging: getCommitDetail requested the wrong URL path (missing "git/"), causing every commit tap to 404. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
11 KiB
TASK-024: History screen redesign (branch tap, colored selection, commit time, file diff, Z-Fold split view, compare removal)
Status: implemented; all required checks pass; device verification in progress (live, iterative) Owner: Claude Reviewer: Codex
Outcome
A single connected round of changes to the branch/tag reference screen and the history screen, driven by the human product owner testing the app live on their own device (a Galaxy Z Fold, SM_F966N) over wireless adb and requesting adjustments in real time:
- Tapping a branch or tag now opens that ref's commit history directly, not the file browser.
- The selected branch/tag row is distinguished by color (background + text tint), not a "선택됨" text label.
- Branch/tag rows and commit rows show the commit's date/time (with time zone) via a new shared
formatInstanthelper. - Tapping a changed file in a commit's file list shows a side-by-side (기존/변경, stacked vertically) diff for just that file, parsed client-side from the already-fetched combined patch — no new network call.
- The diff panels' vertical scroll is synchronized between the two stacked panels, and all lines (both panels) share one horizontal scroll position so long lines scroll together rather than one line at a time.
- Removed/added lines are colored (red/green text), not background-highlighted.
- On a wide screen (≥600dp — Android's own "medium" breakpoint for list-detail layouts, which covers a Z Fold's inner screen unfolded), the commit detail's file list and the selected file's diff show side by side in one screen instead of the diff replacing the list.
- Fixed a real bug found via live server-log inspection:
getCommitDetailrequested/repos/{owner}/{repo}/commits/{sha}(missing thegit/prefix Gitea's raw-diff endpoints use), causing every commit tap to 404 with "커밋이나 기준을 찾을 수 없습니다." - Fixed a second real bug found via a temporary debug-logging pass on-device: Gitea returns the literal JSON
null(not[]) for some empty list responses;parseListtreated that as a parse failure. (This one shipped earlier as TASK-023; noted here because the same live-debugging session led to it.) - Removed the branch/tag comparison feature entirely (UI, ViewModel state, and the underlying
HistoryRepository.compare/getComparePatchmethods) at the human product owner's explicit request ("이 기능은 안 쓸 것 같으니까 그냥 지워줘도 될 것 같아").
Scope
- Included:
feature/repositories/RepositoryListScreen.kt(colored selection, commit time),feature/repositories/RepositoryListViewModel.kt/RepositoryReferencePresenter.kt(ReferenceRowUiModel.commitAtEpochMillis),domain/model/RepositoryModels.kt(TagSummary.commitAtEpochMillis),data/repositories/GiteaRepositoryBrowserApi.kt(tagcreatedtimestamp mapping),feature/history/HistoryViewModel.kt/HistoryScreen.kt(file diff selection folded intoCommitDetailContent, compare removed),data/history/GiteaHistoryApi.kt(commit-detail URL fix, compare removed),domain/history/HistoryRepository.kt(compare removed),domain/history/UnifiedDiffParser.kt(new),ui/DateTimeFormatting.kt(new, shared),MainActivity.kt(routing + wiring). - Excluded: File browsing (
Route.CodeBrowser) remains unreachable from the UI — a known, disclosed side effect of item 1, not addressed here (see "Known side effect" below; a follow-up task will restore it as a tab).
Known side effect (disclosed, not silently absorbed)
The repository reference screen was the only entry point that reached Route.CodeBrowser (file/source browsing). After item 1, file browsing is no longer reachable from the app's UI at all, even though the feature (TASK-006) still exists and works. This was an explicit, direct instruction from the human product owner. A follow-up task (requested live, not yet started as of this commit) will reintroduce it as a default "Files" tab alongside a "History" tab, swipeable, on the branch/tag detail screen.
Acceptance criteria
- Tapping a branch or tag row pushes
Route.History(owner, name, ref)instead ofRoute.CodeBrowser(...). - The selected reference row is distinguished by color; no "선택됨"/text-only indicator remains.
- Branch/tag rows and commit rows display a formatted local date/time with explicit time zone.
- Tapping a changed file shows a parsed, stacked before/after diff without an extra network call.
- The two diff panels' vertical scroll positions stay in sync; all diff lines share one horizontal scroll position.
- Removed/added diff lines are colored by text color only.
- At ≥600dp width, the commit file list and selected file diff render side by side in one screen; below it, selecting a file replaces the list as its own back-stack level (
BackHandlerclears the selection rather than leaving the screen). getCommitDetailrequests/repos/{owner}/{repo}/git/commits/{sha}(matches the already-working.diffendpoint's path family).- The compare feature (UI, ViewModel states/methods,
HistoryRepository/GiteaHistoryApimethods, associated strings and tests) is fully removed, not just hidden. - Build, unit tests, and lint pass after every step (verified repeatedly through this session, not just at the end).
- Full, final device confirmation of every item above in one pass — verification has been continuous and iterative live on-device throughout this work, with several intermediate bugs already caught and fixed (see Evidence), but a single closing confirmation pass has not yet been explicitly requested and received from the human product owner as of this commit.
Required checks
./gradlew assembleDebug
./gradlew testDebugUnitTest
./gradlew lintDebug
Evidence
domain/history/UnifiedDiffParser.kt(new): splits one file's section out of a combined unified-diff patch (bydiff --gitboundaries, matched via+++/---path lines) and lays it out asDiffRow(left, right)pairs — consecutive removed/added runs pair up by index, a loneDiffRow.Gapmarks the boundary between two hunks. 6 unit tests inUnifiedDiffParserTest.kt(context pairing, unequal removed/added run padding, hunk gaps, multi-file section extraction, unknown-path returns empty rather than throwing, a deleted file matched by its old path since the new side is/dev/null).ui/DateTimeFormatting.kt(new): single sharedformatInstant(epochMillis)usingZoneId.systemDefault()and an explicit zone abbreviation in the formatted string, reused by both the repository reference screen and the history screen (previouslyRepositoryListScreen.kthad a private, near-identical copy — consolidated rather than duplicated further).feature/history/HistoryViewModel.kt:HistoryUiState.CommitDetailContentgainedselectedFile: SelectedFileDiff?and awithSelectedFile(...)reconstruction method; the standaloneFileDiffContentstate was removed entirely — selecting/clearing a file is now an in-place update of the current screen's state, not a navigation to a new one, which is what makes the wide-layout side-by-side rendering possible without duplicating fetch logic.feature/history/HistoryScreen.kt: newCommitDetailScreencomposable wraps the existingCommitDetailView(file list) and the newFileDiffViewin aBoxWithConstraints, branching onmaxWidth >= 600.dp(WideLayoutBreakpoint) to either show both side by side (Row+VerticalDivider) or show one full-screen with its own back handler, matching Android's own list-detail adaptive-layout breakpoint guidance.- Synced scroll: two
LazyListStates, cross-observed viasnapshotFlow { firstVisibleItemIndex to firstVisibleItemScrollOffset }.collect { ... scrollToItem(...) }with an equality guard to prevent feedback loops (relies on both panels always having exactly the same row count, so index+offset mirroring keeps them showing the same rows without per-line height math). One sharedScrollStatepassed into every line in both panels for horizontal scroll, replacing the original per-linerememberScrollState()(which only scrolled one line at a time — a bug caught live by the human product owner: "글자 한줄만 스크롤 되게 하지 말고"). - Commit-detail URL bug: found by reproducing live (tap a commit → "커밋이나 기준을 찾을 수 없습니다"), confirming via the Gitea container's own request log (Container Manager → 로그, searched for the repository name) that some requests were genuine
200 OKs misclassified as errors and others were real 404s, then bisecting with temporaryBuildConfig.DEBUG-gated logging (removed before commit) that printed the caught exception's class/message. Root cause:getCommitDetailusedrepoUrl(repository, "commits", sha)instead ofrepoUrl(repository, "git", "commits", sha). Fixed by aligning to the samegit/commits/family the already-working.diffendpoint uses.GiteaHistoryApiTest's path-segment assertion updated to match. - Compare removal:
domain/history/HistoryRepository.kt(compare/getComparePatchmethods removed),data/history/GiteaHistoryApi.kt(compare/getComparePatch/compareUrl/CompareDtoremoved,validatePage/safeNextPageremoved as they had no remaining caller),feature/history/HistoryViewModel.kt(CompareLoading/CompareResult/CompareErrorstates and allcompare*/loadCompare/replaceComparePatchmethods removed),feature/history/HistoryScreen.kt(CompareInput/CompareCommitRow/ComparePatchSectioncomposables and theirwhenbranches removed),MainActivity.kt(compare callback wiring removed),strings_history.xml(compare-specific strings removed), matching tests removed fromGiteaHistoryApiTest.ktandHistoryViewModelTest.kt. ./gradlew assembleDebug testDebugUnitTest lintDebug --rerun-tasks: BUILD SUCCESSFUL at every checkpoint through this work, most recently after the compare removal — all unit tests passing, 0 lint errors.- Live device verification (wireless
adbto the human product owner's own Z Fold, paired earlier this session): confirmed color-coded selection and commit time render; confirmed the previously-404ing commit-detail screen now opens; confirmed the file diff view renders and colors correctly; confirmed the shared-horizontal-scroll fix; confirmed the Z-Fold split view request was received and implemented (not yet re-confirmed live after the compare-removal rebuild — that is the remaining item above). One rendering glitch was reported live (two screens appearing to overlap) during this iteration; it has not been independently reproduced or root-caused as of this commit and should be watched for in the next round of device testing.
Review findings
None yet — pending Codex/human review.