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>
68 lines
11 KiB
Markdown
68 lines
11 KiB
Markdown
# 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:
|
|
|
|
1. Tapping a branch or tag now opens that ref's commit history directly, not the file browser.
|
|
2. The selected branch/tag row is distinguished by color (background + text tint), not a "선택됨" text label.
|
|
3. Branch/tag rows and commit rows show the commit's date/time (with time zone) via a new shared `formatInstant` helper.
|
|
4. 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.
|
|
5. 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.
|
|
6. Removed/added lines are colored (red/green text), not background-highlighted.
|
|
7. 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.
|
|
8. Fixed a real bug found via live server-log inspection: `getCommitDetail` requested `/repos/{owner}/{repo}/commits/{sha}` (missing the `git/` prefix Gitea's raw-diff endpoints use), causing every commit tap to 404 with "커밋이나 기준을 찾을 수 없습니다."
|
|
9. 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; `parseList` treated that as a parse failure. (This one shipped earlier as TASK-023; noted here because the same live-debugging session led to it.)
|
|
10. Removed the branch/tag comparison feature entirely (UI, ViewModel state, and the underlying `HistoryRepository.compare`/`getComparePatch` methods) 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` (tag `created` timestamp mapping), `feature/history/HistoryViewModel.kt` / `HistoryScreen.kt` (file diff selection folded into `CommitDetailContent`, 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
|
|
|
|
- [x] Tapping a branch or tag row pushes `Route.History(owner, name, ref)` instead of `Route.CodeBrowser(...)`.
|
|
- [x] The selected reference row is distinguished by color; no "선택됨"/text-only indicator remains.
|
|
- [x] Branch/tag rows and commit rows display a formatted local date/time with explicit time zone.
|
|
- [x] Tapping a changed file shows a parsed, stacked before/after diff without an extra network call.
|
|
- [x] The two diff panels' vertical scroll positions stay in sync; all diff lines share one horizontal scroll position.
|
|
- [x] Removed/added diff lines are colored by text color only.
|
|
- [x] 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 (`BackHandler` clears the selection rather than leaving the screen).
|
|
- [x] `getCommitDetail` requests `/repos/{owner}/{repo}/git/commits/{sha}` (matches the already-working `.diff` endpoint's path family).
|
|
- [x] The compare feature (UI, ViewModel states/methods, `HistoryRepository`/`GiteaHistoryApi` methods, associated strings and tests) is fully removed, not just hidden.
|
|
- [x] 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
|
|
|
|
```text
|
|
./gradlew assembleDebug
|
|
./gradlew testDebugUnitTest
|
|
./gradlew lintDebug
|
|
```
|
|
|
|
## Evidence
|
|
|
|
- `domain/history/UnifiedDiffParser.kt` (new): splits one file's section out of a combined unified-diff patch (by `diff --git` boundaries, matched via `+++`/`---` path lines) and lays it out as `DiffRow(left, right)` pairs — consecutive removed/added runs pair up by index, a lone `DiffRow.Gap` marks the boundary between two hunks. 6 unit tests in `UnifiedDiffParserTest.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 shared `formatInstant(epochMillis)` using `ZoneId.systemDefault()` and an explicit zone abbreviation in the formatted string, reused by both the repository reference screen and the history screen (previously `RepositoryListScreen.kt` had a private, near-identical copy — consolidated rather than duplicated further).
|
|
- `feature/history/HistoryViewModel.kt`: `HistoryUiState.CommitDetailContent` gained `selectedFile: SelectedFileDiff?` and a `withSelectedFile(...)` reconstruction method; the standalone `FileDiffContent` state 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`: new `CommitDetailScreen` composable wraps the existing `CommitDetailView` (file list) and the new `FileDiffView` in a `BoxWithConstraints`, branching on `maxWidth >= 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 `LazyListState`s, cross-observed via `snapshotFlow { 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 shared `ScrollState` passed into every line in both panels for horizontal scroll, replacing the original per-line `rememberScrollState()` (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 OK`s misclassified as errors and others were real 404s, then bisecting with temporary `BuildConfig.DEBUG`-gated logging (removed before commit) that printed the caught exception's class/message. Root cause: `getCommitDetail` used `repoUrl(repository, "commits", sha)` instead of `repoUrl(repository, "git", "commits", sha)`. Fixed by aligning to the same `git/commits/` family the already-working `.diff` endpoint uses. `GiteaHistoryApiTest`'s path-segment assertion updated to match.
|
|
- Compare removal: `domain/history/HistoryRepository.kt` (`compare`/`getComparePatch` methods removed), `data/history/GiteaHistoryApi.kt` (`compare`/`getComparePatch`/`compareUrl`/`CompareDto` removed, `validatePage`/`safeNextPage` removed as they had no remaining caller), `feature/history/HistoryViewModel.kt` (`CompareLoading`/`CompareResult`/`CompareError` states and all `compare*`/`loadCompare`/`replaceComparePatch` methods removed), `feature/history/HistoryScreen.kt` (`CompareInput`/`CompareCommitRow`/`ComparePatchSection` composables and their `when` branches removed), `MainActivity.kt` (compare callback wiring removed), `strings_history.xml` (compare-specific strings removed), matching tests removed from `GiteaHistoryApiTest.kt` and `HistoryViewModelTest.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 `adb` to 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.
|