Ubisam-MobileGit/handoff/2026-09-28_restyle-existing-screens.md
choiminyeong d1a45abc16 TASK-050..054: tabs, Home, Work, code viewer, C++ pairs, fold layouts
Four tabs in an app shell that becomes a rail from 600dp, a brighter sky-blue palette with the real UbiSam logo, a Home tab built on Gitea's activity feeds, a Work tab that searches issues and pull requests across repositories, a full-screen code viewer with search, outline and Visual Studio colors, C/C++ header/source pairing, and two-pane layouts for wide windows.

The five tasks were built and checked together, so they are one commit: MainActivity, the routes and the shared components belong to all of them. 409 unit tests pass, lint reports 0 errors. Verified on an emulator up to the sign-in screen only; no physical device was available.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 14:35:00 +09:00

11 KiB

작업: 기존 화면 4개를 새 디자인으로 다시 꾸미기

목표

  • 저장소 목록, 알림, 저장소별 이슈/PR, 설정 화면의 모양만 새 디자인 컴포넌트로 바꾼다. 동작과 콜백은 그대로 둔다.

배경

  • 작업 위치: D:\Android\UBGitea\.claude\worktrees\awesome-pascal-3f2ada (git 워크트리). 이 폴더 안에서만 작업한다.
  • Android 앱(Kotlin, Jetpack Compose, Material 3). 앱은 하단 탭 4개(홈/저장소/작업/알림) 구조로 바뀌었다. 탭과 시스템 바 여백은 바깥의 AppShell 이 처리한다.
  • 이미 새 디자인으로 만든 화면이 있다. 이 두 파일을 본보기로 삼아 같은 모양을 만든다:
    • app/src/main/java/com/ubisam/ubgitea/feature/work/WorkScreen.kt (탭 화면: 상단 브랜드 바 + 제목 + 필터 칩 + 카드형 목록)
    • app/src/main/java/com/ubisam/ubgitea/feature/history/HistoryScreen.kt (하위 화면: 카드형 행, 상태 배지)
  • 공통 컴포넌트: app/src/main/java/com/ubisam/ubgitea/ui/components/UBGiteaTopBar.kt (BrandTopBar, ScreenTopBar, TabTitle, SectionTitle), .../ui/components/Elements.kt (UBIcon, Avatar, KindTile, StatusBadge, MonoChip, ListCard, CenteredLoading, CenteredMessage, CautionBanner, ActivityTone).
  • 아이콘: app/src/main/res/drawable/ic_ub_*.xml (home, repo, pull_request, merge, branch, commit, issue, bell, star, star_filled, search, back, chevron_right, chevron_down, chevron_up, folder, file, lock, comment, eye, list, copy, more, close, tag, settings, logout, refresh, check 등). UBIcon(R.drawable.ic_ub_xxx, contentDescription) 로 쓴다.
  • 상대 시간: com.ubisam.ubgitea.ui.formatRelative(epochMillis) (예: "12분 전").
  • Gradle 은 실행하지 않는다. 다른 작업자가 같은 폴더에서 테스트를 돌리고 있다. 컴파일 확인은 메인 세션이 한다. 대신 import 누락과 괄호 짝을 꼼꼼히 확인한다.

결정사항 (변경 금지)

  • 각 화면 최상위 Composable 의 함수 이름과 매개변수 목록은 그대로 둔다 (호출하는 MainActivity.kt 를 고치지 않기 위해). 예외: 아래에 명시한 매개변수 추가만 허용.
  • ViewModel, UiState, 도메인 모델은 고치지 않는다.
  • 색은 하드코딩하지 않는다. MaterialTheme.colorScheme.*, UBGiteaTheme.colors.* (com.ubisam.ubgitea.ui.theme.UBGiteaTheme), ActivityTone 만 쓴다.
  • 상태 색 규칙: 열림 = ActivityTone.SUCCESS, 병합됨 = ActivityTone.MERGED, 닫힘 = ActivityTone.DANGER, 알 수 없음/비공개 = ActivityTone.NEUTRAL, 공개 = ActivityTone.BRAND.
  • 사용자에게 보이는 글자는 전부 문자열 리소스. 새 문자열은 해당 화면의 기존 app/src/main/res/values/strings_<화면>.xml 파일 끝에 추가한다. 한국어.
  • 유니코드 기호(★ ☆ ☰)를 아이콘 대신 쓰던 곳은 UBIcon 으로 바꾼다.
  • 누를 수 있는 것은 높이 48dp 이상. 아이콘만 있는 버튼은 contentDescription 필수.
  • 화면 안에서 Scaffold 를 쓰지 않는다 (바깥 AppShell 이 이미 처리). 최상위는 Column(modifier.fillMaxSize()).
  • 목록 카드 모양: 페이지 배경(colorScheme.background) 위에 흰 카드(colorScheme.surface), 모서리 20dp, 좌우 여백 20dp, 행 사이 HorizontalDivider(color = colorScheme.outlineVariant). LazyColumn 에서는 WorkScreen.kt 의 WorkRow 처럼 행마다 isFirst/isLast 로 모서리를 둥글린다.
  • 코드 스타일: 읽기 쉬운 명시적인 코드. 주석은 "왜"만, 기존 주석 중 여전히 맞는 것은 남긴다.

수정/생성할 파일

  • app/src/main/java/com/ubisam/ubgitea/feature/repositories/RepositoryListScreen.kt : 수정
  • app/src/main/java/com/ubisam/ubgitea/feature/notifications/NotificationsScreen.kt : 수정 (매개변수 3개 추가)
  • app/src/main/java/com/ubisam/ubgitea/feature/workitems/WorkItemsScreen.kt : 수정
  • app/src/main/java/com/ubisam/ubgitea/feature/settings/SettingsScreen.kt : 수정 (매개변수 2개 추가)
  • app/src/main/res/values/strings_repositories.xml, strings_notifications.xml, strings_workitems.xml, strings_settings.xml : 문자열 추가
  • app/src/main/java/com/ubisam/ubgitea/MainActivity.kt : 아래 3, 5번에 적은 호출부 인자 추가만

구체 작업

  1. RepositoryListScreen.kt

    • 최상위의 Scaffold(topBar = BrandTopBar(...)) 를 Column 으로 바꾸고 BrandTopBar(state.accountNameOrNull(), instanceHost, onViewSettings, onLogout) 를 맨 위에 둔다.
    • 로그인 전 상태(SignedOut, WaitingForBrowser)에서는 BrandTopBar 를 보이지 않고, 화면 가운데에 로고(Image(painterResource(R.drawable.logo_ubisam), ...), 높이 36dp), 안내 문구, 버튼을 세로로 둔다.
    • Loading → CenteredLoading, 오류/빈 상태 → CenteredMessage (기존 문구와 버튼 동작 유지. debugDetail 표시도 유지).
    • RepositoryContent: 헤더를 TabTitle(stringResource(R.string.repositories_list_title)) 로, ModeTabs(전체 저장소/즐겨찾기)와 OrganizationFilterRow 는 유지하되 흰 배경 헤더 영역(Surface(color = surface, shape = RoundedCornerShape(bottomStart = 28.dp, bottomEnd = 28.dp))) 안에 넣는다. 좌우 스와이프로 탭을 바꾸는 modeSwipeGesture 는 그대로 둔다.
    • 저장소 행: owner / name (owner 는 흐린 색, name 은 굵게), 공개/비공개 StatusBadge (비공개면 앞에 ic_ub_lock 은 생략 가능), 설명(있으면, 최대 2줄), 언어 + MonoChip(마지막 push 브랜치, icon = ic_ub_branch) + formatRelative(updatedAtEpochMillis), 오른쪽에 즐겨찾기 버튼.
    • LastPushedBranchRow 의 Loading/Empty/Failed 상태와 재시도 동작은 유지한다.
    • FavoriteStar: ★/☆ 글자를 UBIcon(ic_ub_star_filled / ic_ub_star) 로. 채워진 별 색은 UBGiteaTheme.colors.brand, 실패는 colorScheme.error. 별 개수 숫자 유지. 48dp 터치 영역. contentDescription 은 기존 문자열 repositories_favorite / repositories_unfavorite.
    • OfflineCacheBanner → CautionBanner.
    • ReferencesContent(저장소를 고른 뒤 브랜치/태그 목록): 맨 위에 ScreenTopBar(title = 저장소 이름, subtitle = owner, onBack = onBackToRepositories) 를 두고 actions 에 PR/이슈 버튼(ic_ub_pull_request, onViewWorkItems). 로그아웃 버튼은 제거 (프로필 메뉴에 있음). 이 상태에서는 BrandTopBar 를 보이지 않는다. ReferencesContent 에 onBackToRepositories 를 넘기도록 내부 함수 시그니처를 바꿔도 된다.
    • ReferenceRow: 브랜치는 ic_ub_branch, 태그는 ic_ub_tag 아이콘, 이름은 모노스페이스, 커밋 id 는 MonoChip, 시각은 formatRelative, 보호된 브랜치는 StatusBadge(..., ActivityTone.WARNING).
  2. NotificationsScreen.kt

    • 매개변수 추가: accountName: String?, instanceHost: String, onViewSettings: () -> Unit, onLogout: () -> Unit (4개. modifier 앞에).
    • Scaffold 제거. BrandTopBar(accountName, instanceHost, onViewSettings, onLogout) + 흰 헤더 영역에 TabTitle(stringResource(R.string.notifications_title)) 와 필터(읽지 않음/전체, FilterChip 2개로).
    • 행: 읽지 않음이면 왼쪽에 지름 9dp 점(UBGiteaTheme.colors.brand, contentDescription 은 기존 notifications_unread_label 의 대괄호를 뺀 새 문자열), KindTile(이슈 ic_ub_issue, PR ic_ub_pull_request, 병합된 PR ic_ub_merge, 커밋 ic_ub_commit, 저장소 ic_ub_repo), 저장소 이름 + #번호 모노스페이스, 제목(읽지 않음이면 굵게), StatusBadge, 오른쪽 위에 formatRelative(updatedAtEpochMillis).
    • 누를 수 있는 조건(navigable)은 기존과 같다.
  3. MainActivity.kt 의 NotificationsScreen 호출부에 accountName = accountName, instanceHost = instanceHost, onViewSettings = { push(Route.Settings) }, onLogout = ::signOut, 를 추가한다. 다른 곳은 건드리지 않는다.

  4. WorkItemsScreen.kt (저장소 하나의 이슈/PR 목록과 상세)

    • Scaffold 제거. 맨 위에 ScreenTopBar. 제목은 저장소 이름(state 의 target.repository.name), subtitle 은 owner. 상세 화면에서는 제목을 #번호, subtitle 을 저장소 이름으로. onBack 은 목록 상태에서는 onBack, 상세 상태에서는 onBackToList. WorkItemsUiState.Idle 에서는 제목 없이 빈 화면.
    • 이슈/PR 탭(ModeTabs)은 유지.
    • 목록 행은 WorkScreen.kt 의 WorkRow 와 같은 모양 (KindTile + StatusBadge + 제목 + 작성자 · 상대 시간 + 댓글 수).
    • 상세: 제목과 상태 배지, 작성자(Avatar), 본문은 ListCard 안에. PR 은 MonoChip(headRef, ic_ub_branch) → MonoChip(baseRef, ic_ub_branch), 병합 가능 여부 배지. 리뷰와 댓글은 SectionTitle 아래 ListCard 목록, 각 항목에 Avatar + 작성자 + 상대 시간 + 본문.
    • NumberedPageFooter 와 페이지 이동 입력 로직은 그대로 둔다 (모양만 유지).
  5. SettingsScreen.kt

    • 매개변수 추가: accountName: String?, instanceHost: String, onSetCodeWordWrapEnabled: (Boolean) -> Unit, onSetCodeFontSizeSp: (Int) -> Unit, onLogout: () -> Unit (onBack 앞에).
    • Scaffold 제거. ScreenTopBar(title = stringResource(R.string.settings_title), onBack = onBack).
    • 섹션 3개를 SectionTitle + ListCard 로: "계정"(Avatar, 계정 이름, 접속 서버 주소 모노스페이스, 로그아웃 버튼), "코드 보기"(줄바꿈 스위치 = state.codeWordWrapEnabled, 글자 크기 = AppSettingsStore.CODE_FONT_SIZES_SP 를 FilterChip 으로 나열, 선택값 state.codeFontSizeSp), "동작"(기존 스위치 2개).
    • MainActivity.kt 의 SettingsScreen 호출부에 accountName = accountName, instanceHost = instanceHost, onSetCodeWordWrapEnabled = settingsViewModel::setCodeWordWrapEnabled, onSetCodeFontSizeSp = settingsViewModel::setCodeFontSizeSp, onLogout = ::signOut, 추가.
  6. 쓰지 않게 된 문자열 리소스와 import 는 지운다 (lint UnusedResources 경고 방지). 단 다른 파일에서 쓰는지 Grep 으로 확인 후 지운다.

건드리지 말 것

  • app/src/test/ 전체, feature/home/, feature/work/, feature/codebrowser/, feature/history/, feature/branchdetail/, ui/, data/, domain/, core/.
  • MainActivity.kt 는 3, 5번의 호출부 인자 추가 외에는 고치지 않는다.
  • Gradle 실행, git commit/push/stash 금지.

완료 기준

  • 4개 화면 모두 Scaffold 없이 새 컴포넌트를 사용.
  • 하드코딩된 색(Color(0x...))과 한글/영문 리터럴 문자열이 화면 코드에 없음.
  • 각 파일의 import 가 실제 사용과 일치.

보고 형식

  • 파일별로 무엇을 바꿨는지 3줄 이내
  • 추가/삭제한 문자열 리소스 이름
  • 확신이 없는 부분 (컴파일이 안 될 수 있다고 생각되는 곳)

막히면

  • 추측해서 진행하지 말고 멈춘 뒤, 무엇이 불명확한지 보고할 것