feat(android): Projects/Diff/Worktree/git-write parity with web+iOS (W5)
Brings the native Android client to parity with the web/iOS Projects + git surface, consuming the existing server endpoints — ZERO server change. (The "SDK-gated modules" premise was stale; they were already online.) - :api-client (pure Kotlin/JVM): PrStatus/GitLog/GitWrite models (tolerant decoders, safe-error), ProjectInfo + ahead/behind/lastCommitMs; 8 new Endpoints builders (projectPr/projectLog read-only; createWorktree/removeWorktree/prune/gitStage/ gitCommit/gitPush guarded w/ Origin) + ApiClient methods (PR/log degrade in body; writes 200→Ok / 429→RateLimited / 4xx-5xx→Rejected(safe msg)). - :app presenters (JVM-tested): DiffViewModel base-compare + git stage/commit/push; new WorktreeViewModel (create/remove/prune + client branch validation + isMain block); ProjectDetailViewModel failure-isolated PR-chip + recent-commits. - Compose screens wired: ProjectDetail (PR chip tappable only for https, recent commits, worktree create/remove-with-force/prune, view-diff), Diff (base input, per-file stage/unstage, commit/push bar), Projects (ahead/behind sync chip). Nav closes the pre-existing "no inbound link to the diff screen" gap. Verified independently: `./gradlew :app:assembleDebug test :api-client:koverVerify` BUILD SUCCESSFUL; 348 unit tests pass (api-client 102 / app 246); coverage gate held; git status android-only. Compose rendering/interaction deferred to on-device QA (android/DEVICE_QA_CHECKLIST.md), as prior Android waves did.
This commit is contained in:
@@ -66,6 +66,25 @@ Kover ≥80% on the pure modules); this checklist is what a human runs on real h
|
||||
- [ ] adaptive: compact = stack, expanded/tablet = list+detail (`NavigationSuiteScaffold` +
|
||||
`ListDetailPaneScaffold`); pointer secondary-click context menu on a tablet (sw≥600).
|
||||
|
||||
## Projects / git parity (W5 — presenters JVM-tested, Compose device-QA)
|
||||
- [ ] Project card **sync chip**: `↑ahead` / `↓behind` render only when non-zero; no chip when there is
|
||||
no upstream (fields absent).
|
||||
- [ ] Project detail **PR chip**: `availability=ok` → tappable chip opens the PR in the browser ONLY when
|
||||
the url is https (a non-https / junk url is inert, non-clickable); `no-pr` / `not-installed` /
|
||||
`unauthenticated` / `disabled` / `error` each render the degraded copy inertly; check-count colour
|
||||
(fail=red / pending=amber / pass=green).
|
||||
- [ ] Project detail **recent commits**: list renders short-hash + subject inertly; unavailable state on a
|
||||
log failure does NOT hide the rest of the detail (failure-isolated).
|
||||
- [ ] **New worktree** inline form: valid `branch` (+optional `base`) → create → list refreshes; an invalid
|
||||
branch name is rejected with NO network call; a disabled-403 shows the server's safe message.
|
||||
- [ ] Per-worktree **remove**: the button is absent on the `main` worktree; the confirm dialog offers a
|
||||
**Force** checkbox; a dirty-worktree 409 surfaces "force required" inertly; **prune** button works.
|
||||
- [ ] Diff **base-rev** input: entering a rev enters base mode (Working/Staged toggle hidden, `vs <rev>`
|
||||
shown, git-write controls hidden); Clear returns to working/staged; junk rev → server 400 surfaced.
|
||||
- [ ] Diff **stage/unstage**: per-file button (Working→"暂存", Staged→"取消暂存") posts the file and
|
||||
refreshes; **commit** field + button (empty message rejected client-side; Ok shows the short sha) ;
|
||||
**push** button (Ok shows branch→remote; 409 shows the inert server message; 429 shows rate-limited).
|
||||
|
||||
## Known minor gaps (tracked, non-blocking — see PROGRESS_ANDROID.md)
|
||||
- [ ] push body-tap opens the app (not yet the specific gate — the notification `openAppIntent` doesn't
|
||||
carry the sessionId; the gate is still visible in the terminal). MEDIUM.
|
||||
|
||||
Reference in New Issue
Block a user