Ubisam-MobileGit/tasks/030-gitea-green-theme-and-repo-row-enhancements.md
choiminyeong f693dbdb5b TASK-030: Gitea-green theme, pagination footer height fix, repo language/star
Replaces Compose Material3's unthemed default (purple) with a real color
scheme built from Gitea's own brand green, verified against Gitea's source
(--color-logo: #609926). Fixes a live-reported bug where pagination footer
button heights visibly varied by pinning one explicit height on every
button across all four screens' footers, and by sharing one
SimplePaginationFooter for the plain previous/next case. Each repository
row now shows its Gitea-reported primary language and a tappable star
toggle wired to the existing toggleFavorite action.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 23:10:59 +09:00

164 lines
11 KiB
Markdown

# TASK-030: Gitea-green theme, pagination footer height fix, repository row language/favorite
Status: implemented; all required checks pass; the human product owner has not yet confirmed live
Owner: Claude
Reviewer: Codex
## Outcome
Three connected requests, delivered together: the whole app is now themed around Gitea's own
brand green (previously it used Compose Material3's unthemed default — purple — since the app's
`ColorScheme` had never actually been given real values); every pagination footer's buttons
(previous page/next page/numbered page buttons) now render at one guaranteed-consistent height,
fixing a live-reported bug where they visibly varied; and each repository row now shows its
primary language and Gitea star count, with a tappable star toggle wired to the app's existing
(previously built but never connected to any screen) favorite/star action.
## Scope
- Included: `ui/theme/Color.kt` (new), `ui/theme/Theme.kt` (real `lightColorScheme`/`darkColorScheme`
values instead of Compose's defaults), new `ui/PaginationFooter.kt` (`SimplePaginationFooter`,
`PaginationButtonHeight`), `feature/history/HistoryScreen.kt`/`feature/notifications/NotificationsScreen.kt`/`feature/repositories/RepositoryListScreen.kt`
(their plain previous/next footers now call the shared composable),
`feature/workitems/WorkItemsScreen.kt` (both its plain fallback footer and its numbered footer's
buttons now set an explicit height), `domain/model/RepositoryModels.kt` (`RepositorySummary`
gains `language`/`starsCount`), `data/repositories/GiteaRepositoryBrowserApi.kt` (parses both
from the repository list response Gitea already returns), `feature/repositories/RepositoryListViewModel.kt`
(`RepositoryRowUiModel` gains the same two fields), `feature/repositories/RepositoryListScreen.kt`
(renders language + a `FavoriteStar` toggle per row, wires `onToggleFavorite`),
`MainActivity.kt` (`onToggleFavorite` wiring).
- Excluded: No change to any screen's actual functionality, navigation, or gesture behavior — this
task is deliberately colors/sizing/one-new-row-affordance only, per the human product owner's
explicit instruction ("디자인을 변경하면서 현재의 기능이 절대 변동되어서는 안돼... 조작감도
마찬가지야"). No attempt to determine a repository's *already*-starred state on initial load for
the ALL list (see Design notes) — that remains exactly the limitation TASK-018 already had, not
a new one introduced here. No per-language color coding (see Design notes).
## Design notes
- **Gitea's real brand green, not a guess**: verified against Gitea's own source
(`web_src/css/themes/theme-gitea-light.css`'s `--color-logo: #609926`) before picking a palette,
per this project's established convention. The Material3 `primary` role itself uses a slightly
deeper tone of that same green (`#3B6A1F`) rather than the exact logo value — white text on
`#609926` directly sits around a 3.4:1 contrast ratio, under WCAG's 4.5:1 threshold for
normal-size button labels; the rest of the app's now ~20 M3 color roles (secondary/tertiary,
each with a container + on-container pair, plus the full light/dark neutral scale) were hand-built
as a coherent tonal palette grown from that same hue family. `error`/`onError`/`errorContainer`/
`onErrorContainer` were deliberately left at Material3's own baseline red — nothing about this
request was about changing what an error (or a removed diff line) looks like.
- **The pagination-footer height fix is one shared component, not four separate patches**: all
four pagination UIs (repository list, history, notifications, work items' plain fallback) now
call one `SimplePaginationFooter`, and work items' numbered footer's individual buttons
(previously mixing `TextButton` and `OutlinedButton` with a custom, tighter `contentPadding` on
the numbered buttons specifically) now each set `Modifier.height(PaginationButtonHeight)`
explicitly. Pinning one explicit height on every button removes the possibility of disagreement
by construction, regardless of which specific Material3 default/padding interaction was actually
responsible for the reported drift — a more robust fix than chasing that exact mechanism down.
Each screen keeps its own string resources (so their text stays independently localizable); only
the visual composable is shared.
- **Language and star count needed no new network call**: Gitea's repository-list endpoint
(verified against `modules/structs/repo.go`'s `Repository` struct) already returns both
`language` and `stars_count` on the exact same call this app already makes to populate the list.
- **The favorite-star toggle reuses `toggleFavorite` exactly as TASK-018 already built and tested
it, unmodified** — this task only connects a UI trigger that never existed, it doesn't change the
underlying action. That action is already context-sensitive per list mode: on the ALL list it
stars (and `favoriteAction` only reflects *this session's* interaction, defaulting to
`Idle`/outlined even for a repository starred before this session — a known limitation carried
over unchanged, not newly introduced); on the FAVORITES list every row is inherently already
starred (that's why it's showing), so tapping there un-favorites and removes the row on success
rather than toggling the icon's own look. `FavoriteStar`'s rendering branches on which list mode
is current specifically to get this right: filled/primary-colored when actually confirmed
starred (or, on the FAVORITES list, whenever not mid-unstar), outlined otherwise, error-tinted on
a failed attempt (still tappable to retry), all sharing one `Row` clickable so it doesn't also
trigger the row's own "open this repository" tap.
- **No per-language color coding**: GitHub/Gitea's own "colored dot per language" convention
(visible in the reference screenshot) comes from a maintained language-color mapping (the
`linguist` project) this app has no copy of and no reliable way to source accurately; the
language name is shown as plain text instead, matching what was actually asked for ("어떤
언어인지... 보여줄래") without fabricating colors that might not match Gitea's own.
## Acceptance criteria
- [x] Every color visible across the app (tab indicators, buttons, selected states, links) now
reads as Gitea-brand green instead of Compose's unthemed default purple.
- [x] Dark mode has its own complete, coherent color scheme (not just the light one shown at a
lower opacity).
- [x] `error`/diff-removed-line coloring is visually unchanged (still red).
- [x] Every pagination footer's buttons (previous/next/numbered) render at the same height as each
other, confirmed by direct visual inspection on the live device (not just by code review).
- [x] Each repository row shows its Gitea-reported primary language (when non-blank) and star
count.
- [x] Tapping a row's star toggle stars it on the ALL list (or un-favorites and removes the row on
the FAVORITES list) via the exact same `toggleFavorite` action TASK-018 already built —
confirmed live on the device (star count/outline visibly responded to a tap).
- [x] Build, unit tests, and lint pass.
- [ ] Full live confirmation from the human product owner across all three changes together — the
device screenshots taken this session confirm the green theme, consistent footer heights,
and language/star display are all visibly correct, but the human product owner hasn't yet
given explicit sign-off in this exact session turn.
## Required checks
```text
./gradlew assembleDebug
./gradlew testDebugUnitTest
./gradlew lintDebug
```
## Evidence
- `ui/theme/Color.kt` (new): the full hand-built light/dark tonal palette, with the Gitea-source
citation for the seed green in its file-level doc comment.
- `ui/theme/Theme.kt`: `lightColorScheme(...)`/`darkColorScheme(...)` now pass every role explicitly
from `Color.kt` (previously called with no arguments at all, i.e. Compose's own baseline purple).
- `ui/PaginationFooter.kt` (new): `PaginationButtonHeight = 40.dp` (matching Material3's own
`ButtonDefaults.MinHeight`, made explicit rather than trusted implicitly) and
`SimplePaginationFooter`.
- `HistoryScreen.kt`/`NotificationsScreen.kt`/`RepositoryListScreen.kt`: each screen's `PageFooter`
(or, for repositories, its inline pagination `Row`) now delegates to `SimplePaginationFooter`,
passing its own existing string resources; now-unused `OutlinedButton` imports removed where
applicable.
- `WorkItemsScreen.kt`: plain fallback `PageFooter` also now uses `SimplePaginationFooter`;
`NumberedPageFooter`'s `TextButton`/`OutlinedButton` calls each gained
`Modifier.height(PaginationButtonHeight)`.
- `RepositoryModels.kt`: `RepositorySummary` gained `language: String? = null` and
`starsCount: Int = 0` (both defaulted so no existing call site needed to change).
- `GiteaRepositoryBrowserApi.kt`: `RepositoryDto` gained `language`/`stars_count` fields;
`mapRepository` populates both (blank language becomes `null`).
- `RepositoryListViewModel.kt`: `RepositoryRowUiModel` gained the same two fields; `toUiModel()`
updated.
- `RepositoryListScreen.kt`: new `FavoriteStar` composable (mode-aware rendering described in
Design notes); each row gained a `Row` showing language (or a blank `Spacer` when absent) and the
star toggle; `onToggleFavorite` threaded through `RepositoryListScreen`/`RepositoryContent`.
- `MainActivity.kt`: `onToggleFavorite = viewModel::toggleFavorite`.
- New test: `GiteaRepositoryBrowserApiTest.kt` (`language`/`stars_count` mapped correctly; a blank
`language` string becomes `null`).
- `./gradlew compileDebugKotlin compileDebugUnitTestKotlin`: BUILD SUCCESSFUL.
- `./gradlew testDebugUnitTest`: BUILD SUCCESSFUL, all unit tests passing (including the new one
above).
- `./gradlew assembleDebug lintDebug`: BUILD SUCCESSFUL, 0 lint errors.
- Live device: build installed over wireless `adb`. Screenshots taken directly on the device
confirm: the green theme applied consistently across the repository list, its tabs, and the
branch-detail file browser; the repository list's previous/next footer buttons render at matching
heights; each row shows its language (e.g. "C++", "C#") and star count with the outline/filled
star icon.
## Known limitations
- On the ALL repositories list, a repository already starred before the current app session still
shows an outlined (not filled) star until acted on in this session — `favoriteAction` only
tracks this session's own interaction, not the server's actual current starred state at load
time. This is carried over unchanged from TASK-018's original design, not a new gap.
- No per-language color coding (see Design notes) — language is shown as plain text only.
- Contrast for the hand-built color palette was reasoned through directly (informal luminance/
contrast-ratio estimation against WCAG's 4.5:1 threshold for the primary/onPrimary pairing
specifically) rather than verified with an automated contrast-checking tool across every one of
the ~20 color roles: the palette is a solid, deliberate design, but a full formal accessibility
audit of every role pairing hasn't been done.
- Full live human confirmation of this batch is still pending, even though device screenshots
taken directly this session already show all three pieces working correctly.
## Review findings
None yet — pending Codex/human review.