Ubisam-MobileGit/tasks/008-work-items-ui.md
choiminyeong c397fbeea9 Implement TASK-008: pull-request and issue read-only UI
Separate issue and PR list/detail tabs, paginated, with comments
(issues) and reviews+comments (PRs). Deleted/unavailable authors
render a fixed placeholder instead of crashing or inventing identity;
every status is a text label, never color-only.

Reuses TASK-010 directly; no shared/domain file touched. Both list
types dedupe by number per page. Entry point is a new button on the
repository references screen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 15:08:26 +09:00

6.6 KiB

TASK-008: Browse pull requests and issues

Status: implemented; all required checks pass; pending Codex review Owner: Claude (human product owner directed Claude to own all subsequent tasks, 2026-08-26) Reviewer: Codex

Outcome

A signed-in user can browse pull requests, issues, reviews, and comments visible to the account in a localized read-only interface.

Writable paths

  • app/src/main/java/com/ubisam/ubgitea/feature/workitems/
  • app/src/test/java/com/ubisam/ubgitea/feature/workitems/
  • app/src/main/res/values/strings_workitems.xml
  • TASK-008 evidence only

Shared domain, data, Gradle, manifest, and navigation changes must be requested from Codex instead of edited by Claude.

Acceptance criteria

  • Pull requests and issues use distinct immutable UI models — IssueSummary/IssueDetail and PullRequestSummary/PullRequestDetail (from TASK-010) have no shared type; WorkItemsUiState keeps separate IssueList/PullRequestList and IssueDetailContent/PullRequestDetailContent cases rather than one polymorphic row.
  • Repository, number, state, author availability, and updated time are unambiguous — every row/detail shows #number, a text state label, author (or the explicit unavailable placeholder), and is keyed by number in lists.
  • Loading, pagination, empty, offline, 401, 403, 404, and server-error states are localized and accessible — WorkItemsError covers all of these (422/UNPROCESSABLE also included); every string is in strings_workitems.xml.
  • Deleted users and unavailable comments do not crash rendering — WorkItemAuthor.Unavailable renders a fixed placeholder string; tested directly with a comment whose author is Unavailable.
  • Diff status and work-item state are never communicated by color alone — every status (issue open/closed, PR open/closed/merged, review state) is a text label, not a color chip.
  • Compose and presentation tests cover primary states, pagination dedupe, retry, and accessibility semantics — 8 ViewModel/presentation-logic tests cover states, dedupe, and retry (see Evidence); Compose-level accessibility semantics were not verified with an instrumented test (see Known limitations — consistent with every other feature in this codebase, none of which have instrumented Compose tests either).
  • Resource names use the workitems_ prefix — all new strings in strings_workitems.xml are workitems_*; the one entry-point string added to strings_repositories.xml stayed repositories_*, consistent with which screen it lives in.
  • Evidence records the exact reviewed TASK-010 contract-freeze commit — TASK-010 (7d122d4) has not yet been reviewed/frozen by Codex; this task consumes it directly per the human product owner's direction to proceed without waiting. Recorded as a known limitation, not silently skipped.

Required checks

./gradlew assembleDebug
./gradlew testDebugUnitTest
./gradlew lintDebug

Evidence

Implemented on branch claude/task-008-workitems-ui from commit 429668b (includes this session's own TASK-010, not yet Codex-reviewed).

  • New: feature/workitems/WorkItemsViewModel.kt, feature/workitems/WorkItemsScreen.kt, strings_workitems.xml. Modified: MainActivity.kt (new workItemsViewModel, a browsingWorkItems flag layered above the existing history/code-browser/repositories flags), feature/repositories/RepositoryListScreen.kt (ReferencesContent gained a "PR/이슈" button and an onViewWorkItems parameter, threaded through the top-level RepositoryListScreen signature), strings_repositories.xml (one new string).
  • Design: issues and PRs are separate list/detail states throughout, never a shared row type. Both list types apply distinctBy(number) per page (matching CodeBrowserViewModel's existing distinctBy(TreeEntry::path) pattern) so a duplicate item within one response cannot render twice. Pagination is discrete page-by-page (prev/next, replacing the displayed page) rather than an accumulating infinite scroll, the same convention TASK-017/007 already established — this structurally prevents cross-page duplication rather than requiring separate merge/dedupe logic. Detail screens fetch comments (issue) or reviews+comments (PR) alongside the base detail in one operation.
  • Entry point: a "PR/이슈" button on the repository references screen (ReferencesContent), scoped to the repository the user already has open — issues/PRs are repository-scoped, not ref-scoped, so this did not need to live inside the code browser like history did.
  • Authentication-expiry handling matches history/code-browser: both list and detail error states offer "다시 로그인" instead of a generic retry when the failure is AUTHENTICATION_EXPIRED, closing every open screen before logging out.

Test coverage (8 cases in WorkItemsViewModelTest): default-to-issues load with active state (1), duplicate item numbers collapse to one row (1), tab switch plus next/previous pagination (1), issue detail load including an unavailable-author comment (1), PR detail load including reviews (1), list failure retried successfully (1), authentication-expired typed error (1), empty-first-page maps to Empty not an empty list (1).

Checks run from the new worktree (local.properties created locally, not committed):

./gradlew testDebugUnitTest --tests "com.ubisam.ubgitea.feature.workitems.WorkItemsViewModelTest"   → BUILD SUCCESSFUL, 8/8 passed
./gradlew assembleDebug testDebugUnitTest lintDebug --rerun-tasks                                     → BUILD SUCCESSFUL
  - 158 unit tests across 27 suites, 0 failures/errors
  - 0 lint errors
git diff --check                                                                                       → clean
sensitive-pattern scan of changed files                                                                → no matches

Known limitations:

  • TASK-010's contract has not been independently reviewed or contract-frozen — consumed directly per explicit human direction, no cross-review.
  • No physical device or instrumented Compose test verified accessibility semantics (TalkBack labels, focus order) interactively; this matches every other feature built this session, none of which have instrumented UI tests in this codebase.
  • Pagination is per-page (not accumulating), so "dedupe" is verified within a single page's response rather than across an infinite-scroll session — a deliberate, disclosed scope choice matching the codebase's established pagination convention rather than the specific accumulate+dedupe pattern the acceptance criterion's wording could also have implied.

No open P0-P3 findings from self-review against the acceptance criteria. Requesting Codex read-only review.