[FEAT/#95] 챌린지 미션프로그래스 뷰 컴포넌트 조립#101
Conversation
Walkthrough챌린지 미션 진행 화면을 추가하고 ViewModel과 UI 상태(progressPercentage, hasCompletedAny)를 도입했으며, Compose 화면(라우트·스크린) 및 투두 섹션 레이아웃을 추가·조정했습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User as 사용자
participant Route as ChallengeMissionprogressRoute
participant VM as ChallengeMissionProgressViewModel
participant State as UiState
participant Screen as ChallengeMissionprogressScreen
User->>Route: 화면 진입
Route->>VM: ViewModel 구독 (uiState)
VM->>State: UiState.Success 발행 (routines, progressPercentage 등)
State->>Screen: 상태 전달
Screen->>Screen: UI 렌더링 (제목, 진행률, 투두)
User->>Screen: 투두 아이템 클릭
Screen->>VM: onTodoClick(id)
VM->>State: routines 내 isCompleted 토글로 상태 업데이트
State->>Screen: 변경된 상태 반영
User->>Screen: 완료하기 클릭
Screen->>VM: onCompletedTodayClick()
VM->>State: 완료 조건 확인 및 처리
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/cherrish/android/core/designsystem/component/button/CherrishButton.kt (1)
66-71: 가로 패딩 제거로 기본 터치 타깃/가독성 저하 위험가로 패딩이 0이면 wrap-content로 쓰일 때 버튼 폭이 매우 좁아져 48dp 최소 터치 타깃 기준을 쉽게 못 맞춥니다. 의도된 디자인이 아니라면 가로 패딩(또는 최소 크기 보장)을 유지하는 편이 안전합니다.
🛠️ 수정 제안
modifier = modifier .scale(scale) .clip(RoundedCornerShape(12.dp)) .background(backgroundColor) - .padding(vertical = 10.dp), + .padding(horizontal = 10.dp, vertical = 10.dp),
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionprogressScreen.kt`:
- Around line 82-83: ChallengeMissionSelectedTitle is rendering hardcoded text;
update it to accept the actual challenge data (or specific fields like
title/plan) from uiState.challenge and bind those values instead of static
strings. Locate where ChallengeMissionSelectedTitle() is invoked and pass
uiState.challenge (or uiState.challenge.title and uiState.challenge.plan) as
props/parameters, and modify the ChallengeMissionSelectedTitle composable to
read and display those fields (or fallback safely if uiState.challenge is null).
Also apply the same binding fix for the other occurrences mentioned (the blocks
rendering the title/plan around the 110-139 and 144-145 regions) so all
instances use uiState.challenge data.
- Around line 69-75: The LazyColumn is not applying the Scaffold's paddingValues
and may overlap system insets; update the LazyColumn's modifier (used in
ChallengeMissionprogressScreen) to apply .padding(paddingValues) at the start of
the chain so scaffold insets are honored, then verify whether
navigationBarsPadding() is still needed (remove if redundant) to avoid double
insets; ensure you modify the LazyColumn modifier that currently calls
.fillMaxSize().background(...).navigationBarsPadding() to apply
padding(paddingValues) first.
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressViewModel.kt`:
- Around line 64-68: onCompletedTodayClick currently returns early and does
nothing; implement the completion flow by, after verifying state is
UiState.Success and !state.data.hasCompletedAny, calling the domain/use-case or
repository method (e.g., challengeRepository.completeToday()/completeMission())
to perform the server call, handle success/failure (update _uiState with new
data or emit an error state) and emit any UI events (e.g., via existing
single-event channel) to inform the UI; ensure you reference and update _uiState
and use UiState.Success branch in ChallengeMissionProgressViewModel to reflect
the new completion status and side-effects.
- Around line 50-60: The onTodoClick handler currently only toggles the
routine's isCompleted in _uiState.updateSuccess but doesn't recalc
progressPercentage, so update state.copy to also compute the new
progressPercentage after mapping routines: derive the updated routines list (the
mapped result used in state.copy), count completed items (e.g.,
updatedRoutines.count { it.isCompleted }) and set progressPercentage =
(completedCount.toFloat() / updatedRoutines.size) * 100 (or your app's expected
scale), then return state.copy(routines = updatedRoutines.toPersistentList(),
progressPercentage = calculatedValue) so UI reflects the new completion
percentage; adjust types/rounding as needed.
🧹 Nitpick comments (1)
app/src/main/java/com/cherrish/android/presentation/challenge/component/ChallengeMissionTodoSection.kt (1)
79-87: 리스트 항목에 안정 키를 부여해 주세요
Column + forEach로 바뀌면서 항목별 키가 사라졌습니다. 내부에remember상태가 있는 컴포저블이라면 아이템 재정렬/교체 시 상태가 섞일 수 있으니key(item.id)로 감싸는 게 안전합니다.♻️ 개선 제안
+import androidx.compose.runtime.key ... Column( verticalArrangement = Arrangement.spacedBy(8.dp) ) { routines.forEach { item -> - ChallengeChecklist( - isChecked = item.isCompleted, - onChecklistClick = { onRoutineClick(item.id) }, - checklistContent = item.routine - ) + key(item.id) { + ChallengeChecklist( + isChecked = item.isCompleted, + onChecklistClick = { onRoutineClick(item.id) }, + checklistContent = item.routine + ) + } } }As per coding guidelines, ...
| fun onTodoClick(id: Long) { | ||
| _uiState.updateSuccess { state -> | ||
| state.copy( | ||
| routines = state.routines.map { | ||
| if (it.id == id) { | ||
| it.copy(isCompleted = !it.isCompleted) | ||
| } else { | ||
| it | ||
| } | ||
| }.toPersistentList() | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find the ViewModel file
find . -name "ChallengeMissionProgressViewModel.kt" -type fRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 190
🏁 Script executed:
# Once found, let's examine the full file to understand the structure
git ls-files | grep -i "challengemissionprogress"Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 525
🏁 Script executed:
# Search for UiState definition related to challenge mission progress
rg "ChallengeMissionProgress.*UiState" -A 20 -t ktRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 100
🏁 Script executed:
# Look for progressPercentage usage in the codebase
rg "progressPercentage" -t kt -B 2 -A 2Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 100
🏁 Script executed:
# Check the file structure around the challenge/missionprogress directory
fd "." -t f "presentation/challenge/missionprogress" | head -20Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 199
🏁 Script executed:
# Read the UiState file
cat -n app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressUiState.ktRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 911
🏁 Script executed:
# Read the ViewModel file
cat -n app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressViewModel.ktRepository: TEAM-Cherrish/Cherrish-Android
Length of output: 3025
🏁 Script executed:
# Search for progressPercentage without file type restriction
rg "progressPercentage" -B 2 -A 2Repository: TEAM-Cherrish/Cherrish-Android
Length of output: 3197
onTodoClick() 시 progressPercentage를 함께 갱신해야 합니다
현재 코드는 체크 상태를 업데이트할 때 routines만 변경하고 progressPercentage는 그대로 유지됩니다. 초기 상태에서는 완료된 항목 1개 / 전체 4개 = 25%로 설정되었으나, 사용자가 체크박스를 토글해도 진행률이 갱신되지 않아 UI와 실제 완료 상태가 맞지 않을 수 있습니다.
수정 예시
fun onTodoClick(id: Long) {
_uiState.updateSuccess { state ->
- state.copy(
- routines = state.routines.map {
- if (it.id == id) {
- it.copy(isCompleted = !it.isCompleted)
- } else {
- it
- }
- }.toPersistentList()
- )
+ val updatedRoutines = state.routines.map {
+ if (it.id == id) {
+ it.copy(isCompleted = !it.isCompleted)
+ } else {
+ it
+ }
+ }.toPersistentList()
+ val completedCount = updatedRoutines.count { it.isCompleted }
+ val progress = if (updatedRoutines.isNotEmpty()) {
+ (completedCount * 100) / updatedRoutines.size
+ } else 0
+ state.copy(
+ routines = updatedRoutines,
+ progressPercentage = progress
+ )
}
}🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressViewModel.kt`
around lines 50 - 60, The onTodoClick handler currently only toggles the
routine's isCompleted in _uiState.updateSuccess but doesn't recalc
progressPercentage, so update state.copy to also compute the new
progressPercentage after mapping routines: derive the updated routines list (the
mapped result used in state.copy), count completed items (e.g.,
updatedRoutines.count { it.isCompleted }) and set progressPercentage =
(completedCount.toFloat() / updatedRoutines.size) * 100 (or your app's expected
scale), then return state.copy(routines = updatedRoutines.toPersistentList(),
progressPercentage = calculatedValue) so UI reflects the new completion
percentage; adjust types/rounding as needed.
| fun onCompletedTodayClick() { | ||
| val state = _uiState.value | ||
| if (state !is UiState.Success) return | ||
| if (!state.data.hasCompletedAny) return | ||
| } |
There was a problem hiding this comment.
완료 버튼 클릭이 성공 상태에서도 무동작입니다
onCompletedTodayClick()가 성공 상태에서도 아무 동작을 하지 않아 버튼이 무의미해집니다. 서버 호출/이벤트 발행 등 실제 완료 처리 로직을 연결해야 합니다.
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/cherrish/android/presentation/challenge/missionprogress/ChallengeMissionProgressViewModel.kt`
around lines 64 - 68, onCompletedTodayClick currently returns early and does
nothing; implement the completion flow by, after verifying state is
UiState.Success and !state.data.hasCompletedAny, calling the domain/use-case or
repository method (e.g., challengeRepository.completeToday()/completeMission())
to perform the server call, handle success/failure (update _uiState with new
data or emit an error state) and emit any UI events (e.g., via existing
single-event channel) to inform the UI; ensure you reference and update _uiState
and use UiState.Success branch in ChallengeMissionProgressViewModel to reflect
the new completion status and side-effects.
sohee6989
left a comment
There was a problem hiding this comment.
일단 여기까지!!
수고하셨습니다!
이거 같이 한 번더 확인해야될듯요!!!ㅎㅎ
| verticalArrangement = Arrangement.spacedBy(14.dp) | ||
| ) { | ||
| item { | ||
| Spacer(modifier = Modifier.height(38.dp)) |
There was a problem hiding this comment.
p1: 이 값도 contentPadding으로 관리해줘도 되지 않나여?!
| } | ||
|
|
||
| @Composable | ||
| private fun ChallengeMissionSelectedTitle(modifier: Modifier = Modifier) { |
| } | ||
|
|
||
| item { | ||
| Spacer(modifier = Modifier.height(24.dp)) |
| verticalAlignment = Alignment.CenterVertically | ||
| ) { | ||
| Text( | ||
| "피부 컨디션 챌린지", |
| color = CherrishTheme.colors.gray1000 | ||
| ) | ||
|
|
||
| Surface( |
There was a problem hiding this comment.
p1: 이거 굳이 surface 안 감싸도 되지 않나여??
| cherryType = uiState.cherryType, | ||
| remainingRoutines = uiState.remainingCount, | ||
| challengeProgress = uiState.progressPercentage, | ||
| modifier = Modifier.padding(bottom = 16.dp) |
There was a problem hiding this comment.
p1: 10.dp인듯?!
더블체크부탁드립니다
Related issue 🛠
Work Description ✏️
Screenshot 📸
Screen_Recording_20260120_005123_Cherrish.mp4
Uncompleted Tasks 😅
To Reviewers 📢
사랑합니다 나.소.수 언니들
Summary by CodeRabbit
New Features
UI 개선
✏️ Tip: You can customize this high-level summary in your review settings.