Skip to content

[feat/#278] 공통 여정 관련 UI#280

Open
znayeonzn wants to merge 18 commits intodevelopfrom
feat/#278-common-quest-ui
Open

[feat/#278] 공통 여정 관련 UI#280
znayeonzn wants to merge 18 commits intodevelopfrom
feat/#278-common-quest-ui

Conversation

@znayeonzn
Copy link
Collaborator

@znayeonzn znayeonzn commented Feb 26, 2026

Related issue 🛠

Work Description 📝

  • 공통 여정 퀘스트 돌아보기 UI (다른 사람, 내 답변)
  • SnackBar 컴포넌트 타입 추가
  • 네비 일부 연결

Screenshot 📸

_.mp4
_.mp4

Uncompleted Tasks 😅

  • 뒤로 가기 아이콘 네비 연결
  • 나의 공통 답변 세부 페이지 바텀시트 네비 연결
  • 모달 추가

PR Point 📌

  • 더미데이터 넣어서 UI만 확인할 수 있게끔 했습니다!
    모달 추가는 코리 반영하면서 넣어놓을게요!

  • 이번 PR 실수가 많을 거 같아요 열.코로 많은 지적해주세요 ㅎㅎ

  • 이번 스프린트에서 스낵바 아이콘이 하나 더 추가됐는데요!
    아이콘 타입 이넘 활용해서 넣고, 스낵바 사용하는 모든 화면 수정했습니다 !
    확인 부탁드려요! 🙇‍♀️

image

자잘한 수정 때문에 건든 파일이 많네요.. 여기 부분 위주로 봐주시면 돼요!

트러블 슈팅 💥

Summary by CodeRabbit

릴리즈 노트

  • 새 기능

    • 스낵바에 아이콘 타입(경고/성공) 적용 가능
    • 퀘스트: 공통 답변 보기, 내 답변 목록 및 상세 페이지 추가
    • 더보기 옵션 메뉴(차단·신고·수정·삭제) 추가
    • 내 답변 카드 및 공통답변 상세 화면 신규 컴포저블 추가
  • UI/UX 개선

    • 답변 항목의 확장/축약 토글과 클릭 처리 추가
    • 스낵바 시각 피드백 강화(아이콘·타입별 표시)
    • 여러 아이콘 리소스 추가 (차단·신고·성공·휴지통·오버플로우)

@znayeonzn znayeonzn self-assigned this Feb 26, 2026
@znayeonzn znayeonzn added 🍒 [FEAT] 새로운 기능 구현 🍥 [UI] UI 작업 🐰 아연 아연 labels Feb 26, 2026
@auto-assign auto-assign bot requested review from fredleeJH and sohee6989 February 26, 2026 10:16
@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Walkthrough

스낵바에 아이콘 타입 지원을 추가하고 퀘스트 관련 리뷰(공통/개인 답변) UI, 바텀시트 옵션 및 관련 네비게이션 경로들을 대규모로 추가/연결했습니다.

Changes

Cohort / File(s) Summary
디자인시스템 — 스낵바
app/src/main/java/com/byeboo/app/core/designsystem/type/CustomSnackBarType.kt, app/src/main/java/com/byeboo/app/core/designsystem/component/snackbar/CustomSnackBar.kt, app/src/main/java/com/byeboo/app/core/designsystem/component/snackbar/CustomSnackBarVisuals.kt, app/src/main/java/com/byeboo/app/core/designsystem/event/SnackbarTrigger.kt
새 enum CustomSnackBarType 추가, CustomSnackBar 시그니처에 iconType 파라미터 도입, LocalSnackBarTrigger 콜백 타입 변경, 아이콘 리소스 소스화.
글로벌 스낵바 호출자 반영
app/src/main/java/.../presentation/*/*ShowSnackBar*.kt, app/src/main/java/.../presentation/*/*ViewModel*.kt
앱 전반의 ShowSnackBar 사이드이펙트/호출들이 message → (message, iconType) 형태로 확장되어 호출부와 ViewModel들에서 iconType 전달로 업데이트됨 (다수 파일).
메인/내비게이션 확장
app/src/main/java/com/byeboo/app/presentation/main/MainNavHost.kt, app/src/main/java/com/byeboo/app/presentation/main/MainNavigator.kt, app/src/main/java/com/byeboo/app/presentation/quest/navigation/QuestNavigation.kt, app/src/main/java/com/byeboo/app/presentation/quest/navigation/QuestRoute.kt
퀘스트 관련 신규 라우트(QuestCommonAnswer, QuestMyAnswers, QuestMyAnswersDetail) 및 NavController 확장 함수와 questGraph 파라미터(새 네비게이션 콜백) 추가.

|퀘스트 리뷰 UI — 공통 답변
app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt, .../CommonAnswerState.kt, .../CommonAnswerViewModel.kt|공통 답변 화면/상태/뷰모델 추가, MoreOptionsBottomSheet로 차단·신고 옵션 처리 및 ShowSnackBar 사이드이펙트 사용.|
|퀘스트 리뷰 UI — 개인 답변(목록/상세)
app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerScreen.kt, .../MyAnswerDetailScreen.kt, .../MyAnswerViewModel.kt, .../MyAnswerState.kt|사용자 답변 목록·상세 화면 및 ViewModel 추가, EDIT/DELETE 옵션을 포함한 바텀시트 연동 및 상세 네비게이션 구현.|
|퀘스트 컴포넌트/모델/타입 추가
app/src/main/java/com/byeboo/app/presentation/quest/component/bottomsheet/MoreOptionsBottomSheet.kt, .../card/CommonAnswerItem.kt, .../card/MyAnswerItem.kt, .../component/type/OptionType.kt, .../model/QuestModel.kt|MoreOptionsBottomSheet 신규 컴포넌트 추가, CommonAnswerItem 확장(isExpanded/onClick), MyAnswerItem 추가, OptionType 열거와 MyAnswerModel 추가.|
|리소스 추가
app/src/main/res/drawable/ic_block.xml, app/src/main/res/drawable/ic_overflow_menu.xml, app/src/main/res/drawable/ic_report.xml, app/src/main/res/drawable/ic_success.xml, app/src/main/res/drawable/ic_trash.xml|차단·오버플로우·신고·성공·휴지통 벡터 드로어블 추가.|
|기타 화면 업데이트(패키지/사소 변경)
app/src/main/java/.../presentation/splash/Splash*.kt, app/src/main/java/.../presentation/home/*, app/src/main/java/.../presentation/mypage/*, 다수 퀘스트 관련 라우트/스크린 파일|패키지명 조정(일부), sideEffect 변수명 변경, ShowSnackBar 호출부 일괄 업데이트 등 광범위한 호출부 변경.|

Sequence Diagram(s)

(생략)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested reviewers

  • sohee6989

Poem

"나는 토끼, 코드 밭을 폴짝,
아이콘 달린 스낵바 반짝반짝,
퀘스트 답변이 줄지어 서고,
바텀시트로 차단·신고 골라,
훌쩍, 기능 완성 축하 껑충!" 🐇✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive 대부분의 변경사항이 공통 여정 UI 구현과 스낵바 타입 추가(이슈 범위 내)에 해당하나, MoreOptionsBottomSheet, OptionType, 네비게이션 확장 등 일부 변경사항이 이슈에서 명시되지 않은 추가 구현입니다. 이슈 #278의 바텀시트 및 네비게이션 요구사항이 암묵적으로 포함된 것으로 보이나, MoreOptionsBottomSheet와 OptionType enum 등의 일부 컴포넌트가 이슈 범위를 초과하는지 명확히 확인이 필요합니다.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 '[feat/#278] 공통 여정 관련 UI'로 주요 변경사항인 공통 여정 UI 구현을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명은 요구되는 템플릿의 주요 섹션(관련 이슈, 작업 내용, 스크린샷, 미완료 항목, PR 포인트, 트러블슈팅)을 포함하고 있으며, 구체적인 작업 내용과 변경사항을 명시하고 있습니다.
Linked Issues check ✅ Passed PR의 코드 변경사항들이 이슈 #278의 요구사항(공통 여정 답변 보기, 차단/신고 바텀시트, 나의 답변 확인)을 충족하며, 필요한 UI 컴포넌트와 네비게이션이 구현되어 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#278-common-quest-ui

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

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/byeboo/app/presentation/quest/screen/CommonJourneyScreen.kt (1)

186-193: ⚠️ Potential issue | 🔴 Critical

onAnswerClick에 하드코딩된 값 1이 전달되고 있습니다.

현재 onAnswerClick(1)로 하드코딩되어 있어, 어떤 답변 항목을 클릭해도 항상 answerId가 1로 전달됩니다. 실제 답변의 ID를 전달해야 합니다.

🐛 수정 제안
                 items(
                     items = state.answers,
                     key = { it.answerId },
                 ) { answer ->
                     CommonAnswerItem(
                         answer = answer,
-                        onClick = { onAnswerClick(1) },
+                        onClick = { onAnswerClick(answer.answerId) },
                         modifier =
                             Modifier
                                 .padding(horizontal = screenWidthDp(24.dp))
                                 .padding(bottom = screenHeightDp(24.dp)),
                     )
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/screen/CommonJourneyScreen.kt`
around lines 186 - 193, The onClick for CommonAnswerItem is passing a hardcoded
1 to onAnswerClick; change it to pass the clicked answer's ID (e.g., use
answer.id or answer.answerId depending on the model) so the real answer
identifier is forwarded to onAnswerClick; update the onClick lambda in
CommonAnswerItem to call onAnswerClick with the answer's id property (and add a
null/exists check if the model uses a nullable id).
🧹 Nitpick comments (13)
app/src/main/res/drawable/ic_report.xml (1)

10-26: 색상 하드코딩 대신 색상 리소스 참조로 바꾸는 것을 권장합니다.

#FF3B3E가 여러 path에 중복되어 있어 추후 테마 대응(다크모드/브랜드 컬러 변경) 시 유지보수 비용이 커집니다. @color/...로 추출해 공통 관리하는 편이 안전합니다.

♻️ 제안 diff
-        android:strokeColor="#FF3B3E"/>
+        android:strokeColor="@color/snackbar_alert_icon"/>

-        android:strokeColor="#FF3B3E"
+        android:strokeColor="@color/snackbar_alert_icon"

-        android:fillColor="#FF3B3E"/>
+        android:fillColor="@color/snackbar_alert_icon"/>

-        android:fillColor="#FF3B3E"/>
+        android:fillColor="@color/snackbar_alert_icon"/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/res/drawable/ic_report.xml` around lines 10 - 26, 해당 XML의 여러
path 요소(android:strokeColor 및 android:fillColor)에 하드코딩된 색상값 "#FF3B3E"가 중복되어 있으니
colors.xml에 예: <color name="ic_report_tint">#FF3B3E</color> 같은 리소스를 추가한 뒤
ic_report.xml의 모든 android:strokeColor 및 android:fillColor="#FF3B3E"를
`@color/ic_report_tint로` 교체하세요; 변경 대상 식별에는 각 <path> 요소의 android:pathData 값(예:
"M18,16H6V9.538...", "M4,20L20,20", "M12.005,6.5...", "M12,12.5m-1,0...")과 속성
이름(strokeColor, fillColor)을 참고하면 됩니다.
app/src/main/res/drawable/ic_overflow_menu.xml (1)

1-9: 전반적으로 LGTM, 색상 리소스 사용 고려 권장

벡터 드로어블 구조와 아이콘 형태는 적절합니다. 다만 fillColor#ffffff로 하드코딩되어 있어 다크/라이트 테마 지원이나 다른 배경에서의 재사용성이 제한될 수 있습니다.

스낵바 등 특정 UI에서만 사용될 경우 현재 구현도 무방하지만, 향후 유연성을 위해 색상 리소스나 테마 속성 사용을 고려해 보세요.

♻️ 색상 리소스 사용 제안
  <path
      android:pathData="M12,16.5C12.398,16.5 12.779,16.658 13.061,16.939C13.342,17.221 13.5,17.602 13.5,18C13.5,18.398 13.342,18.779 13.061,19.061C12.779,19.342 12.398,19.5 12,19.5C11.602,19.5 11.221,19.342 10.939,19.061C10.658,18.779 10.5,18.398 10.5,18C10.5,17.602 10.658,17.221 10.939,16.939C11.221,16.658 11.602,16.5 12,16.5ZM12,10.5C12.398,10.5 12.779,10.658 13.061,10.939C13.342,11.221 13.5,11.602 13.5,12C13.5,12.398 13.342,12.779 13.061,13.061C12.779,13.342 12.398,13.5 12,13.5C11.602,13.5 11.221,13.342 10.939,13.061C10.658,12.779 10.5,12.398 10.5,12C10.5,11.602 10.658,11.221 10.939,10.939C11.221,10.658 11.602,10.5 12,10.5ZM12,4.5C12.398,4.5 12.779,4.658 13.061,4.939C13.342,5.221 13.5,5.602 13.5,6C13.5,6.398 13.342,6.779 13.061,7.061C12.779,7.342 12.398,7.5 12,7.5C11.602,7.5 11.221,7.342 10.939,7.061C10.658,6.779 10.5,6.398 10.5,6C10.5,5.602 10.658,5.221 10.939,4.939C11.221,4.658 11.602,4.5 12,4.5Z"
-      android:fillColor="#ffffff"/>
+      android:fillColor="@color/white"/>
</vector>

또는 런타임에 ImageView.setColorFilter() 또는 app:tint를 사용하여 아이콘 색상을 동적으로 적용하는 방법도 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/res/drawable/ic_overflow_menu.xml` around lines 1 - 9, Replace
the hardcoded android:fillColor="#ffffff" in the <path> of the vector drawable
with a color resource or theme attribute so the icon follows dark/light themes
and can be reused; e.g. change android:fillColor to a reference like
`@color/overflow_icon` or a theme attribute like ?attr/colorOnSurface (or make the
vector tintable and remove the fillColor so callers can set app:tint or
ImageView.setColorFilter), update the color resource if needed, and ensure
usages apply the intended tint/theme attribute.
app/src/main/res/drawable/ic_block.xml (1)

6-8: clip-path는 제거해도 동일하게 동작합니다.

Line 7-8은 전체 캔버스를 그대로 클리핑해서 렌더링 결과 변화가 없습니다. 단순화를 위해 제거를 권장합니다.

Diff 제안
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
     android:viewportHeight="24">
-  <group>
-    <clip-path
-        android:pathData="M0,0h24v24h-24z"/>
     <path
         android:pathData="M12,12m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
         android:strokeWidth="2"
         android:fillColor="#00000000"
         android:strokeColor="#ffffff"/>
     <path
         android:pathData="M6.327,17.26L17.327,7.26"
         android:strokeWidth="2"
         android:fillColor="#00000000"
         android:strokeColor="#ffffff"/>
-  </group>
 </vector>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/res/drawable/ic_block.xml` around lines 6 - 8, SVG/XML contains
an unnecessary clip-path that clips the whole canvas (the <clip-path> element
with android:pathData="M0,0h24v24h-24z"); remove that <clip-path> element (and
its enclosing empty group if it becomes redundant) from ic_block.xml so the
drawable is simplified without changing rendering.
app/src/main/java/com/byeboo/app/presentation/quest/component/card/MyAnswerItem.kt (1)

60-60: Spacer에 padding 대신 height 사용 필요

Line 60에서 Spacerpadding을 사용하고 있습니다. padding은 모든 방향에 여백을 추가하므로 의도한 12dp가 아닌 24dp(위아래 합산)의 수직 공간이 생깁니다. Line 70처럼 height를 사용해야 합니다.

♻️ 수정 제안
-        Spacer(modifier = Modifier.padding(screenHeightDp(12.dp)))
+        Spacer(modifier = Modifier.height(screenHeightDp(12.dp)))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/component/card/MyAnswerItem.kt`
at line 60, In MyAnswerItem (file MyAnswerItem.kt) the Spacer uses
Modifier.padding(screenHeightDp(12.dp)) which adds padding both top and bottom
(totaling 24dp) instead of providing a 12dp vertical gap; change the Spacer's
modifier to Modifier.height(screenHeightDp(12.dp)) so it produces the intended
12dp vertical space (refer to the Spacer instance around the commented line and
mirror the approach used later on line 70).
app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerViewModel.kt (1)

84-90: 불필요한 viewModelScope.launch 블록

현재 when 블록 내에 suspend 함수 호출이 없어 viewModelScope.launch가 불필요합니다. TODO 구현 시 실제 비동기 작업이 추가되면 필요하겠지만, 현재로서는 제거하거나 실제 구현 시 추가하는 것이 더 명확합니다.

♻️ 선택적 수정 제안
     fun onOptionClick(option: OptionType) {
         onDismissBottomSheet()

-        viewModelScope.launch {
-            when (option) {
-                OptionType.EDIT -> { /* TODO 수정 화면 이동 */ }
-                OptionType.DELETE -> { /* TODO 삭제 모달 띄우기 */ }
-                else -> {}
-            }
+        when (option) {
+            OptionType.EDIT -> { /* TODO 수정 화면 이동 */ }
+            OptionType.DELETE -> { /* TODO 삭제 모달 띄우기 */ }
+            else -> {}
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerViewModel.kt`
around lines 84 - 90, The current viewModelScope.launch wrapper around the
when(option) block is unnecessary because there are no suspend calls; remove the
viewModelScope.launch call and execute the when(option) directly (retaining the
OptionType.EDIT and OptionType.DELETE branches) and only reintroduce
viewModelScope.launch around the when when you implement actual asynchronous
work for those branches (or make the called functions suspend and keep the
launch). Refer to the viewModelScope.launch and the when(option) handling
OptionType.EDIT / OptionType.DELETE in MyAnswerViewModel to locate and update
the code.
app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingquestcompleted/OffboardingQuestCompletedViewModel.kt (1)

60-68: onFailure 내부의 중첩 launch는 제거하는 편이 좋습니다.

이미 viewModelScope.launch 안에서 실행 중이라 _sideEffect.emit(...)을 직접 호출해도 됩니다. 중첩 코루틴을 줄이면 흐름이 단순해집니다.

제안 diff
-                    }.onFailure {
-                        viewModelScope.launch {
-                            _sideEffect.emit(
-                                QuestCompletedSideEffect.ShowSnackBar(
-                                    message = "서버에 연결할 수 없습니다. 잠시 후 시도해 주세요.",
-                                    iconType = CustomSnackBarType.ALERT,
-                                ),
-                            )
-                        }
-                    }
+                    }.onFailure {
+                        _sideEffect.emit(
+                            QuestCompletedSideEffect.ShowSnackBar(
+                                message = "서버에 연결할 수 없습니다. 잠시 후 시도해 주세요.",
+                                iconType = CustomSnackBarType.ALERT,
+                            ),
+                        )
+                    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingquestcompleted/OffboardingQuestCompletedViewModel.kt`
around lines 60 - 68, In OffboardingQuestCompletedViewModel inside the onFailure
block, remove the nested viewModelScope.launch and directly call
_sideEffect.emit(...) from the surrounding coroutine context (you are already
inside viewModelScope.launch), so replace the inner launch-wrapped emit with a
direct _sideEffect.emit call (keep the same
QuestCompletedSideEffect.ShowSnackBar parameters and CustomSnackBarType.ALERT).
app/src/main/java/com/byeboo/app/presentation/quest/start/QuestStartViewModel.kt (1)

92-95: 동일한 에러 스낵바 emit 로직은 헬퍼로 합치는 것을 권장합니다.

현재 두 실패 분기에서 동일한 블록이 반복되어 유지보수 시 메시지/타입 변경 누락 위험이 있습니다.

♻️ 제안 diff
@@
-                        _sideEffect.emit(
-                            QuestStartSideEffect.ShowSnackBar(
-                                message = "서버에 연결할 수 없습니다. 잠시 후 시도해 주세요.",
-                                iconType = CustomSnackBarType.ALERT,
-                            ),
-                        )
+                        emitServerConnectionErrorSnackBar()
@@
-                        _sideEffect.emit(
-                            QuestStartSideEffect.ShowSnackBar(
-                                message = "서버에 연결할 수 없습니다. 잠시 후 시도해 주세요.",
-                                iconType = CustomSnackBarType.ALERT,
-                            ),
-                        )
+                        emitServerConnectionErrorSnackBar()
@@
+        private suspend fun emitServerConnectionErrorSnackBar() {
+            _sideEffect.emit(
+                QuestStartSideEffect.ShowSnackBar(
+                    message = "서버에 연결할 수 없습니다. 잠시 후 시도해 주세요.",
+                    iconType = CustomSnackBarType.ALERT,
+                ),
+            )
+        }

Also applies to: 132-135

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/start/QuestStartViewModel.kt`
around lines 92 - 95, Duplicate ShowSnackBar emission with the same message/type
is repeated in QuestStartViewModel (the QuestStartSideEffect.ShowSnackBar blocks
at the two failure branches around the existing emits), so extract a single
helper method (e.g., emitServerConnectionSnackBar or emitErrorSnackBar) inside
QuestStartViewModel that constructs/emits QuestStartSideEffect.ShowSnackBar with
the shared message and CustomSnackBarType.ALERT, then replace both duplicated
blocks (the ones around lines 92-95 and 132-135) with calls to that helper to
centralize the message/type and avoid drift.
app/src/main/java/com/byeboo/app/core/designsystem/component/snackbar/CustomSnackBarVisuals.kt (1)

7-13: 구현이 올바르며, data class 사용을 고려해 볼 수 있습니다.

SnackbarVisuals 인터페이스 구현이 적절합니다. 선택적으로 data class로 변경하면 equals, hashCode, copy 메서드를 자동으로 얻을 수 있어 테스트나 비교 시 유용할 수 있습니다.

♻️ data class로 변경 제안
-class CustomSnackBarVisuals(
+data class CustomSnackBarVisuals(
     override val message: String,
     val type: CustomSnackBarType,
     override val actionLabel: String? = null,
     override val withDismissAction: Boolean = false,
     override val duration: SnackbarDuration = SnackbarDuration.Short,
 ) : SnackbarVisuals
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/core/designsystem/component/snackbar/CustomSnackBarVisuals.kt`
around lines 7 - 13, Convert the CustomSnackBarVisuals class into a data class
to automatically get equals/hashCode/copy/toString for easier testing and
comparisons; update the declaration of CustomSnackBarVisuals (which implements
SnackbarVisuals and references CustomSnackBarType, message, actionLabel,
withDismissAction, duration) so it is declared as a data class while keeping the
same primary constructor parameters and the implemented interface
SnackbarVisuals.
app/src/main/java/com/byeboo/app/presentation/quest/component/type/OptionType.kt (1)

6-29: 하드코딩된 문자열을 string resource로 이동하는 것을 고려해 주세요.

optionTitle 값들이 직접 하드코딩되어 있습니다. 향후 다국어 지원(i18n)을 위해 string resource로 관리하는 것이 좋습니다.

♻️ String resource 사용 제안
 enum class OptionType(
     `@DrawableRes` val optionIcon: Int,
-    val optionTitle: String,
+    `@StringRes` val optionTitleRes: Int,
 ) {
     BLOCK(
         optionIcon = R.drawable.ic_block,
-        optionTitle = "사용자 차단하기",
+        optionTitleRes = R.string.option_block_user,
     ),
     // ... 나머지 항목도 동일하게 적용
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/component/type/OptionType.kt`
around lines 6 - 29, The enum OptionType currently hardcodes Korean titles in
the optionTitle property for entries BLOCK, REPORT, EDIT, DELETE; change
optionTitle to reference string resources instead (e.g., R.string.option_block,
R.string.option_report, R.string.option_edit, R.string.option_delete) and update
callers to resolve the string via Context or a string provider when displaying
(keep the enum values as resource IDs or store `@StringRes` ints rather than raw
Strings). Ensure you add the corresponding entries to strings.xml and mark
optionTitle as an Int annotated with `@StringRes` in the OptionType class.
app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerDetailScreen.kt (1)

122-126: 뒤로 가기 버튼이 비활성화 상태입니다.

PR 목표에서 "뒤로 가기 아이콘 네비 연결"이 미완료 항목으로 언급되어 있습니다. navigateUp 콜백을 MyAnswerDetailRoute에 추가하고 연결해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerDetailScreen.kt`
around lines 122 - 126, Add a navigateUp callback to the MyAnswerDetailRoute and
wire it to the back icon's click handler: update the MyAnswerDetailRoute
signature to accept a navigateUp: () -> Unit parameter, pass that down to
MyAnswerDetailScreen, and replace the TODO in the Modifier.noRippleClickable
used for the back icon so it calls navigateUp() when clicked; ensure any callers
of MyAnswerDetailRoute provide the appropriate NavController::navigateUp or
equivalent lambda.
app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt (1)

129-133: 뒤로 가기 버튼 클릭 핸들러가 누락되었습니다.

PR 목표에서 "뒤로 가기 아이콘 네비 연결"이 미완료 항목으로 언급되어 있습니다. 이후 작업 시 navigateUp 또는 navigateToQuest 콜백을 연결해야 합니다.

이 기능을 구현하기 위한 코드를 생성해 드릴까요, 아니면 추적을 위한 이슈를 생성할까요?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt`
around lines 129 - 133, The back icon currently lacks a click handler; wrap the
Icon in a clickable surface (e.g., IconButton or Modifier.clickable) and invoke
the appropriate navigation callback passed into CommonAnswerScreen (use the
existing navigateUp or navigateToQuest parameter/name) to perform navigation
when tapped; ensure the click target uses the same tint and accessibility by
keeping contentDescription non-null (e.g., "Back") so TalkBack users get the
action.
app/src/main/java/com/byeboo/app/presentation/quest/component/bottomsheet/MoreOptionsBottomSheet.kt (1)

85-92: 일관성을 위해 screenHeightDp 사용을 고려해 주세요.

다른 부분에서는 screenHeightDp/screenWidthDp를 사용하여 반응형 레이아웃을 구현하고 있는데, HorizontalDividerpadding(vertical = 20.dp)에서는 하드코딩된 값을 사용하고 있습니다.

♻️ 수정 제안
                 HorizontalDivider(
                     modifier =
                         Modifier
                             .fillMaxWidth()
-                            .padding(vertical = 20.dp),
+                            .padding(vertical = screenHeightDp(20.dp)),
                     thickness = 1.dp,
                     color = ByeBooTheme.colors.gray800,
                 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/component/bottomsheet/MoreOptionsBottomSheet.kt`
around lines 85 - 92, Replace the hardcoded Vertical padding on
HorizontalDivider in MoreOptionsBottomSheet (the Modifier.padding(vertical =
20.dp) call) with a responsive value derived from the existing
screenHeightDp/screenWidthDp approach used in this file; compute the vertical
padding from screenHeightDp (using the same scale factor pattern used elsewhere)
and apply that computed value to Modifier.padding so the divider spacing scales
with screen size.
app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerScreen.kt (1)

88-92: 뒤로 가기 버튼이 비활성화 상태입니다.

noRippleClickable에 클릭 핸들러가 없어 뒤로 가기 아이콘이 동작하지 않습니다. TODO 주석이 있으니 이후 네비게이션 연결이 필요합니다.

이 기능을 구현하는 코드를 생성해 드릴까요?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerScreen.kt`
around lines 88 - 92, The back button is non-functional because
Modifier.noRippleClickable has no onClick handler; in MyAnswerScreen replace the
TODO with a lambda that performs navigation (e.g., Modifier.noRippleClickable {
navController.popBackStack() } ) or, if NavController isn't available in this
composable, add an onBackPressed/onNavigateUp: ()->Unit parameter to
MyAnswerScreen and call that inside the noRippleClickable lambda
(Modifier.noRippleClickable { onBackPressed() }) so the icon actually navigates
back.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/byeboo/app/presentation/quest/component/bottomsheet/MoreOptionsBottomSheet.kt`:
- Line 76: The Spacer uses Modifier.padding(end = screenWidthDp(12.dp)) which
doesn't create horizontal space; replace the padding modifier with a width
modifier so the Spacer actually occupies horizontal gap (change Spacer(...
Modifier.padding(end = screenWidthDp(12.dp))) to use
Modifier.width(screenWidthDp(12.dp))) in MoreOptionsBottomSheet (also update the
second occurrence around the other Spacer noted in the comment).

In
`@app/src/main/java/com/byeboo/app/presentation/quest/navigation/QuestNavigation.kt`:
- Around line 133-137: MyAnswerDetailRoute is not receiving the answerId from
the QuestMyAnswersDetail nav route, so the UI always shows the first answer;
update the navigation composable to pass the route args into the route and
change MyAnswerViewModel to read the answerId from SavedStateHandle (use
savedStateHandle.toRoute<QuestMyAnswersDetail>().answerId) and use that id to
load the specific answer; target symbols: composable<QuestMyAnswersDetail>,
MyAnswerDetailRoute, MyAnswerViewModel, SavedStateHandle, toRoute, answerId.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt`:
- Around line 147-148: In CommonAnswerScreen.kt the Spacer inside the Row uses
Modifier.padding(bottom = screenHeightDp(16.dp)) which has no effect because Row
lays out children horizontally; either remove the unused Spacer or replace its
modifier to produce the intended spacing: if you wanted horizontal space use
Modifier.width(...), if you wanted vertical space move the Spacer out of the Row
into a Column/parent and use Modifier.height(screenHeightDp(16.dp)) or apply the
bottom padding to the Row or parent container; update the Spacer or parent
accordingly to reflect the intended vertical/horizontal spacing.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerDetailScreen.kt`:
- Line 139: The Spacer inside the Row in MyAnswerDetailScreen.kt is using
Modifier.padding(bottom = screenHeightDp(16.dp)), which has no effect in a
horizontal Row; replace that Spacer with a vertical spacing approach by using a
Spacer with Modifier.height(screenHeightDp(16.dp)) or move the bottom padding to
a parent Column/Box that controls vertical spacing (refer to the Spacer instance
and the surrounding Row in MyAnswerDetailScreen.kt and mirror the fix used in
CommonAnswerScreen.kt).
- Line 64: The current code uses uiState.answers.first() which throws
NoSuchElementException for empty lists; change that to a safe access (e.g., use
firstOrNull() or getOrNull(0)) and handle the null case rather than letting it
crash — update the val answerState assignment in MyAnswerDetailScreen to use
uiState.answers.firstOrNull() (or uiState.answers.getOrNull(0)) and then either
early-return, show an empty/error UI, or provide a fallback value so downstream
code using answerState is not executed with a missing item.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerScreen.kt`:
- Around line 96-106: The Text composable in MyAnswerScreen currently hardcodes
the nickname string ("하츠핑하츠님의") inside the buildAnnotatedString; replace that
hardcoded text by injecting the real user nickname from the screen state (e.g.,
use uiState.nickname or a value exposed by the ViewModel) when building the
string in the Text composable (the buildAnnotatedString block used for the
header), ensuring you handle null/empty nickname with a sensible fallback.

---

Outside diff comments:
In
`@app/src/main/java/com/byeboo/app/presentation/quest/screen/CommonJourneyScreen.kt`:
- Around line 186-193: The onClick for CommonAnswerItem is passing a hardcoded 1
to onAnswerClick; change it to pass the clicked answer's ID (e.g., use answer.id
or answer.answerId depending on the model) so the real answer identifier is
forwarded to onAnswerClick; update the onClick lambda in CommonAnswerItem to
call onAnswerClick with the answer's id property (and add a null/exists check if
the model uses a nullable id).

---

Nitpick comments:
In
`@app/src/main/java/com/byeboo/app/core/designsystem/component/snackbar/CustomSnackBarVisuals.kt`:
- Around line 7-13: Convert the CustomSnackBarVisuals class into a data class to
automatically get equals/hashCode/copy/toString for easier testing and
comparisons; update the declaration of CustomSnackBarVisuals (which implements
SnackbarVisuals and references CustomSnackBarType, message, actionLabel,
withDismissAction, duration) so it is declared as a data class while keeping the
same primary constructor parameters and the implemented interface
SnackbarVisuals.

In
`@app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingquestcompleted/OffboardingQuestCompletedViewModel.kt`:
- Around line 60-68: In OffboardingQuestCompletedViewModel inside the onFailure
block, remove the nested viewModelScope.launch and directly call
_sideEffect.emit(...) from the surrounding coroutine context (you are already
inside viewModelScope.launch), so replace the inner launch-wrapped emit with a
direct _sideEffect.emit call (keep the same
QuestCompletedSideEffect.ShowSnackBar parameters and CustomSnackBarType.ALERT).

In
`@app/src/main/java/com/byeboo/app/presentation/quest/component/bottomsheet/MoreOptionsBottomSheet.kt`:
- Around line 85-92: Replace the hardcoded Vertical padding on HorizontalDivider
in MoreOptionsBottomSheet (the Modifier.padding(vertical = 20.dp) call) with a
responsive value derived from the existing screenHeightDp/screenWidthDp approach
used in this file; compute the vertical padding from screenHeightDp (using the
same scale factor pattern used elsewhere) and apply that computed value to
Modifier.padding so the divider spacing scales with screen size.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/component/card/MyAnswerItem.kt`:
- Line 60: In MyAnswerItem (file MyAnswerItem.kt) the Spacer uses
Modifier.padding(screenHeightDp(12.dp)) which adds padding both top and bottom
(totaling 24dp) instead of providing a 12dp vertical gap; change the Spacer's
modifier to Modifier.height(screenHeightDp(12.dp)) so it produces the intended
12dp vertical space (refer to the Spacer instance around the commented line and
mirror the approach used later on line 70).

In
`@app/src/main/java/com/byeboo/app/presentation/quest/component/type/OptionType.kt`:
- Around line 6-29: The enum OptionType currently hardcodes Korean titles in the
optionTitle property for entries BLOCK, REPORT, EDIT, DELETE; change optionTitle
to reference string resources instead (e.g., R.string.option_block,
R.string.option_report, R.string.option_edit, R.string.option_delete) and update
callers to resolve the string via Context or a string provider when displaying
(keep the enum values as resource IDs or store `@StringRes` ints rather than raw
Strings). Ensure you add the corresponding entries to strings.xml and mark
optionTitle as an Int annotated with `@StringRes` in the OptionType class.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt`:
- Around line 129-133: The back icon currently lacks a click handler; wrap the
Icon in a clickable surface (e.g., IconButton or Modifier.clickable) and invoke
the appropriate navigation callback passed into CommonAnswerScreen (use the
existing navigateUp or navigateToQuest parameter/name) to perform navigation
when tapped; ensure the click target uses the same tint and accessibility by
keeping contentDescription non-null (e.g., "Back") so TalkBack users get the
action.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerDetailScreen.kt`:
- Around line 122-126: Add a navigateUp callback to the MyAnswerDetailRoute and
wire it to the back icon's click handler: update the MyAnswerDetailRoute
signature to accept a navigateUp: () -> Unit parameter, pass that down to
MyAnswerDetailScreen, and replace the TODO in the Modifier.noRippleClickable
used for the back icon so it calls navigateUp() when clicked; ensure any callers
of MyAnswerDetailRoute provide the appropriate NavController::navigateUp or
equivalent lambda.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerScreen.kt`:
- Around line 88-92: The back button is non-functional because
Modifier.noRippleClickable has no onClick handler; in MyAnswerScreen replace the
TODO with a lambda that performs navigation (e.g., Modifier.noRippleClickable {
navController.popBackStack() } ) or, if NavController isn't available in this
composable, add an onBackPressed/onNavigateUp: ()->Unit parameter to
MyAnswerScreen and call that inside the noRippleClickable lambda
(Modifier.noRippleClickable { onBackPressed() }) so the icon actually navigates
back.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerViewModel.kt`:
- Around line 84-90: The current viewModelScope.launch wrapper around the
when(option) block is unnecessary because there are no suspend calls; remove the
viewModelScope.launch call and execute the when(option) directly (retaining the
OptionType.EDIT and OptionType.DELETE branches) and only reintroduce
viewModelScope.launch around the when when you implement actual asynchronous
work for those branches (or make the called functions suspend and keep the
launch). Refer to the viewModelScope.launch and the when(option) handling
OptionType.EDIT / OptionType.DELETE in MyAnswerViewModel to locate and update
the code.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/start/QuestStartViewModel.kt`:
- Around line 92-95: Duplicate ShowSnackBar emission with the same message/type
is repeated in QuestStartViewModel (the QuestStartSideEffect.ShowSnackBar blocks
at the two failure branches around the existing emits), so extract a single
helper method (e.g., emitServerConnectionSnackBar or emitErrorSnackBar) inside
QuestStartViewModel that constructs/emits QuestStartSideEffect.ShowSnackBar with
the shared message and CustomSnackBarType.ALERT, then replace both duplicated
blocks (the ones around lines 92-95 and 132-135) with calls to that helper to
centralize the message/type and avoid drift.

In `@app/src/main/res/drawable/ic_block.xml`:
- Around line 6-8: SVG/XML contains an unnecessary clip-path that clips the
whole canvas (the <clip-path> element with android:pathData="M0,0h24v24h-24z");
remove that <clip-path> element (and its enclosing empty group if it becomes
redundant) from ic_block.xml so the drawable is simplified without changing
rendering.

In `@app/src/main/res/drawable/ic_overflow_menu.xml`:
- Around line 1-9: Replace the hardcoded android:fillColor="#ffffff" in the
<path> of the vector drawable with a color resource or theme attribute so the
icon follows dark/light themes and can be reused; e.g. change android:fillColor
to a reference like `@color/overflow_icon` or a theme attribute like
?attr/colorOnSurface (or make the vector tintable and remove the fillColor so
callers can set app:tint or ImageView.setColorFilter), update the color resource
if needed, and ensure usages apply the intended tint/theme attribute.

In `@app/src/main/res/drawable/ic_report.xml`:
- Around line 10-26: 해당 XML의 여러 path 요소(android:strokeColor 및
android:fillColor)에 하드코딩된 색상값 "#FF3B3E"가 중복되어 있으니 colors.xml에 예: <color
name="ic_report_tint">#FF3B3E</color> 같은 리소스를 추가한 뒤 ic_report.xml의 모든
android:strokeColor 및 android:fillColor="#FF3B3E"를 `@color/ic_report_tint로` 교체하세요;
변경 대상 식별에는 각 <path> 요소의 android:pathData 값(예: "M18,16H6V9.538...",
"M4,20L20,20", "M12.005,6.5...", "M12,12.5m-1,0...")과 속성 이름(strokeColor,
fillColor)을 참고하면 됩니다.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da5b372 and 2ac162e.

📒 Files selected for processing (82)
  • app/src/main/java/com/byeboo/app/core/designsystem/component/snackbar/CustomSnackBar.kt
  • app/src/main/java/com/byeboo/app/core/designsystem/component/snackbar/CustomSnackBarVisuals.kt
  • app/src/main/java/com/byeboo/app/core/designsystem/event/SnackbarTrigger.kt
  • app/src/main/java/com/byeboo/app/core/designsystem/type/CustomSnackBarType.kt
  • app/src/main/java/com/byeboo/app/presentation/auth/userinfo/UserInfoScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/auth/userinfo/UserInfoState.kt
  • app/src/main/java/com/byeboo/app/presentation/auth/userinfo/UserInfoViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/home/HomeScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/home/HomeUiState.kt
  • app/src/main/java/com/byeboo/app/presentation/home/HomeViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/home/homeamulet/HomeAmuletScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/home/homeamulet/HomeAmuletState.kt
  • app/src/main/java/com/byeboo/app/presentation/home/homeamulet/HomeAmuletViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/main/MainNavHost.kt
  • app/src/main/java/com/byeboo/app/presentation/main/MainNavigator.kt
  • app/src/main/java/com/byeboo/app/presentation/main/MainScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/mypage/MyPageScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/mypage/MyPageState.kt
  • app/src/main/java/com/byeboo/app/presentation/mypage/MypageViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/mypage/blockedusers/BlockedUsersScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/mypage/blockedusers/BlockedUsersState.kt
  • app/src/main/java/com/byeboo/app/presentation/mypage/editprofile/EditProfileScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/mypage/editprofile/EditProfileState.kt
  • app/src/main/java/com/byeboo/app/presentation/mypage/editprofile/EditProfileViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/OffboardingJourneyState.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/OffboardingJourneyViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingcompletedguide/OffboardingCompletedGuideScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingcompletedguide/OffboardingCompletedGuideState.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingcompletedguide/OffboardingCompletedGuideViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingcompletedjourney/OffboardingCompletedJourneyScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingquestcompleted/OffboardingQuestCompletedScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingquestcompleted/OffboardingQuestCompletedState.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingquestcompleted/OffboardingQuestCompletedViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingquestreview/OffboardingQuestReviewScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/offboarding/offboardingquestreview/OffboardingQuestReviewState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/QuestScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/QuestViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/behavior/QuestBehaviorCompleteScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/behavior/QuestBehaviorCompleteState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/behavior/QuestBehaviorCompleteViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/behavior/QuestBehaviorState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/behavior/QuestBehaviorViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/behavior/QuestBehaviorWritingScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/component/bottomsheet/MoreOptionsBottomSheet.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/component/card/CommonAnswerItem.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/component/card/MyAnswerItem.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/component/type/OptionType.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/model/QuestModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/model/QuestState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/navigation/QuestNavigation.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/navigation/QuestRoute.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/record/QuestRecordingCompleteScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/record/QuestRecordingCompleteState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/record/QuestRecordingCompleteViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/record/QuestRecordingScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/record/QuestRecordingState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/record/QuestRecordingViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerDetailScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/my/QuestReviewScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/my/QuestReviewState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/my/QuestReviewViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/screen/CommonJourneyScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/start/QuestStartScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/start/QuestStartState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/start/QuestStartViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/tip/QuestTipScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/tip/QuestTipState.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/tip/QuestTipViewModel.kt
  • app/src/main/java/com/byeboo/app/presentation/splash/SplashScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/splash/SplashState.kt
  • app/src/main/java/com/byeboo/app/presentation/splash/SplashViewModel.kt
  • app/src/main/res/drawable/ic_block.xml
  • app/src/main/res/drawable/ic_overflow_menu.xml
  • app/src/main/res/drawable/ic_report.xml
  • app/src/main/res/drawable/ic_success.xml
  • app/src/main/res/drawable/ic_trash.xml

Comment on lines +133 to +137
composable<QuestMyAnswersDetail> {
MyAnswerDetailRoute(
paddingValues = paddingValues,
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find MyAnswerDetailRoute implementation
fd -t f "MyAnswerDetailRoute" --type f

Repository: 36-APPJAM-HEARTZ/ByeBoo-ANDROID

Length of output: 57


🏁 Script executed:

# Search for MyAnswerDetailRoute function/composable definition
rg -n "fun MyAnswerDetailRoute|@Composable.*MyAnswerDetailRoute" --type=kotlin

Repository: 36-APPJAM-HEARTZ/ByeBoo-ANDROID

Length of output: 201


🏁 Script executed:

# Find MyAnswerViewModel file
fd -t f "*MyAnswerViewModel*" --type f

Repository: 36-APPJAM-HEARTZ/ByeBoo-ANDROID

Length of output: 373


🏁 Script executed:

# Check MyAnswerViewModel constructor and SavedStateHandle usage
rg -n "class MyAnswerViewModel|SavedStateHandle|answerId" --type=kotlin -B 2 -A 5 | grep -A 20 "MyAnswerViewModel"

Repository: 36-APPJAM-HEARTZ/ByeBoo-ANDROID

Length of output: 8822


🏁 Script executed:

# Read MyAnswerDetailRoute implementation
cat -n app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerDetailScreen.kt | head -80

Repository: 36-APPJAM-HEARTZ/ByeBoo-ANDROID

Length of output: 3779


🏁 Script executed:

# Read the full MyAnswerViewModel to see all details
cat -n app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerViewModel.kt

Repository: 36-APPJAM-HEARTZ/ByeBoo-ANDROID

Length of output: 4635


🏁 Script executed:

# Search for QuestMyAnswersDetail route definition in QuestNavigation
rg -n "QuestMyAnswersDetail" app/src/main/java/com/byeboo/app/presentation/quest/navigation/QuestNavigation.kt -B 2 -A 2

Repository: 36-APPJAM-HEARTZ/ByeBoo-ANDROID

Length of output: 346


MyAnswerDetailRouteQuestMyAnswersDetail 라우트로부터 answerId를 수신하지 않습니다.

네비게이션에서 QuestMyAnswersDetail(answerId)로 이동하지만, MyAnswerDetailRoute는 이 파라미터를 받지 않습니다. 현재 구현에서는 uiState.answers.first()를 사용하므로 항상 첫 번째 답변만 표시됩니다.

MyAnswerViewModel에서 SavedStateHandle을 주입받아 라우트 파라미터로부터 answerId를 추출하도록 수정해야 합니다:

class MyAnswerViewModel
    `@Inject`
    constructor(
        private val savedStateHandle: SavedStateHandle,
    ) : ViewModel() {
        private val answerId: Long = savedStateHandle.toRoute<QuestMyAnswersDetail>().answerId
        // ... answerId를 사용하여 해당 답변 조회
    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/navigation/QuestNavigation.kt`
around lines 133 - 137, MyAnswerDetailRoute is not receiving the answerId from
the QuestMyAnswersDetail nav route, so the UI always shows the first answer;
update the navigation composable to pass the route args into the route and
change MyAnswerViewModel to read the answerId from SavedStateHandle (use
savedStateHandle.toRoute<QuestMyAnswersDetail>().answerId) and use that id to
load the specific answer; target symbols: composable<QuestMyAnswersDetail>,
MyAnswerDetailRoute, MyAnswerViewModel, SavedStateHandle, toRoute, answerId.

modifier: Modifier = Modifier,
) {
val scrollState = rememberScrollState()
val answerState = uiState.answers.first()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

uiState.answers.first()는 빈 리스트에서 NoSuchElementException을 발생시킵니다.

answers 리스트가 비어있을 경우 앱이 크래시됩니다. 안전한 접근 방식을 사용하거나 빈 상태를 처리해야 합니다.

🐛 수정 제안
 `@Composable`
 private fun MyAnswerDetailScreen(
     uiState: MyAnswerState,
     paddingValues: PaddingValues,
     onClickMoreOptions: () -> Unit,
     onDismissBottomSheet: () -> Unit,
     onOptionClick: (OptionType) -> Unit,
     modifier: Modifier = Modifier,
 ) {
     val scrollState = rememberScrollState()
-    val answerState = uiState.answers.first()
+    val answerState = uiState.answers.firstOrNull() ?: return
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val answerState = uiState.answers.first()
`@Composable`
private fun MyAnswerDetailScreen(
uiState: MyAnswerState,
paddingValues: PaddingValues,
onClickMoreOptions: () -> Unit,
onDismissBottomSheet: () -> Unit,
onOptionClick: (OptionType) -> Unit,
modifier: Modifier = Modifier,
) {
val scrollState = rememberScrollState()
val answerState = uiState.answers.firstOrNull() ?: return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerDetailScreen.kt`
at line 64, The current code uses uiState.answers.first() which throws
NoSuchElementException for empty lists; change that to a safe access (e.g., use
firstOrNull() or getOrNull(0)) and handle the null case rather than letting it
crash — update the val answerState assignment in MyAnswerDetailScreen to use
uiState.answers.firstOrNull() (or uiState.answers.getOrNull(0)) and then either
early-return, show an empty/error UI, or provide a fallback value so downstream
code using answerState is not executed with a missing item.

Comment on lines +96 to +106
Text(
text =
buildAnnotatedString {
append("하츠핑하츠님의")
append("\n")
append("공통퀘스트 답변이에요")
},
color = ByeBooTheme.colors.gray50,
style = ByeBooTheme.typography.head2,
modifier = Modifier.padding(vertical = screenHeightDp(10.dp)),
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

닉네임이 하드코딩되어 있습니다.

"하츠핑하츠님의"가 하드코딩되어 있습니다. 실제 사용자 닉네임은 uiState에서 가져오거나 ViewModel을 통해 주입받아야 합니다.

🔧 수정 제안
                 Text(
                     text =
                         buildAnnotatedString {
-                            append("하츠핑하츠님의")
+                            append("${uiState.nickname}님의")
                             append("\n")
                             append("공통퀘스트 답변이에요")
                         },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerScreen.kt`
around lines 96 - 106, The Text composable in MyAnswerScreen currently hardcodes
the nickname string ("하츠핑하츠님의") inside the buildAnnotatedString; replace that
hardcoded text by injecting the real user nickname from the screen state (e.g.,
use uiState.nickname or a value exposed by the ViewModel) when building the
string in the Text composable (the buildAnnotatedString block used for the
header), ensuring you handle null/empty nickname with a sensible fallback.

Copy link
Collaborator

@fredleeJH fredleeJH left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생 많으셨습니다!


@Composable
private fun MyAnswerDetailTopBar(
onClickMoreOptions: () -> Unit,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 탑 바를 공통 컴포넌트로 빼는건 별로일까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영하겠습니다!!

modifier = modifier,
)

QuestTitle(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

공통퀘스트는 피그마상 타이틀이 조금 다른데 같은데 확인 부탁 드립니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분 아직 이전 pr 병합을 못해서 일단 이렇게 썼습니다!
추후에 풀 받아온 후 수정하겠습니다!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
app/src/main/java/com/byeboo/app/presentation/quest/review/common/component/AnswerDetailTopBar.kt (1)

19-23: 뒤로가기 콜백 파라미터 누락

현재 onClickMoreOptions만 파라미터로 받고 있지만, 뒤로가기 아이콘도 클릭 가능한 상태입니다(Line 36-38). 재사용성과 완성도를 위해 onClickBack 콜백도 추가하는 것이 좋습니다.

♻️ 제안: onClickBack 파라미터 추가
 `@Composable`
 fun AnswerDetailTopBar(
+    onClickBack: () -> Unit,
     onClickMoreOptions: () -> Unit,
     modifier: Modifier = Modifier,
 ) {

그리고 Line 36-38의 noRippleClickable에 적용:

             modifier =
                 Modifier.noRippleClickable(
-                    // Todo: 뒤로가기
+                    onClick = onClickBack,
                 ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/component/AnswerDetailTopBar.kt`
around lines 19 - 23, Add a missing back-click callback to the
AnswerDetailTopBar composable by introducing an onClickBack: () -> Unit
parameter (with a sensible default if desired) alongside the existing
onClickMoreOptions; then update the back icon's noRippleClickable invocation
(the click handler around the Back icon in AnswerDetailTopBar) to call
onClickBack instead of being hardcoded/absent so back presses are handled by the
caller.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt`:
- Around line 82-85: CommonAnswerScreen currently forwards its incoming modifier
to AnswerDetailTopBar even though that modifier is already applied to the parent
Column (see Column in CommonAnswerScreen); change the call to AnswerDetailTopBar
to pass a fresh/empty Modifier (or omit the modifier argument if optional)
instead of the external modifier to avoid double-applying the same modifier;
update the invocation of AnswerDetailTopBar in CommonAnswerScreen and ensure any
local styling needed by AnswerDetailTopBar is provided via a dedicated internal
modifier rather than the external parameter.
- Line 94: Spacer에 Modifier.padding(bottom = ...)를 사용하면 의도한 수직 간격이 적용되지 않으므로, 해당
Spacer 호출(함수/파일의 Spacer(...) 사용 부분)에서 Modifier.padding을 제거하고 대신
Modifier.height(screenHeightDp(10.dp))로 크기를 지정해 수직 여백을 명시적으로 적용하도록 변경하세요; 즉 현재의
Spacer(modifier = Modifier.padding(bottom = screenHeightDp(10.dp)))를
Spacer(modifier = Modifier.height(screenHeightDp(10.dp)))로 바꾸면 됩니다.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/component/AnswerDetailTopBar.kt`:
- Around line 31-38: Two Icon composables currently set contentDescription =
null (e.g., the Icon using ImageVector.vectorResource(id = R.drawable.ic_left)
with Modifier.noRippleClickable) which prevents screen readers from conveying
the button purpose; update both Icon usages (the left/back Icon and the other
Icon in the 43-52 range) to provide meaningful, localized contentDescription
strings (use string resources like R.string.back or R.string.edit/action_name)
instead of null, and only keep null if the icon is purely decorative; ensure the
descriptions match the button action invoked by the surrounding clickable
(Modifier.noRippleClickable) so accessibility tools read the correct intent.

---

Nitpick comments:
In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/component/AnswerDetailTopBar.kt`:
- Around line 19-23: Add a missing back-click callback to the AnswerDetailTopBar
composable by introducing an onClickBack: () -> Unit parameter (with a sensible
default if desired) alongside the existing onClickMoreOptions; then update the
back icon's noRippleClickable invocation (the click handler around the Back icon
in AnswerDetailTopBar) to call onClickBack instead of being hardcoded/absent so
back presses are handled by the caller.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ac162e and eebf0ca.

📒 Files selected for processing (4)
  • app/src/main/java/com/byeboo/app/presentation/quest/component/bottomsheet/MoreOptionsBottomSheet.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/component/AnswerDetailTopBar.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerDetailScreen.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/com/byeboo/app/presentation/quest/review/common/personal/MyAnswerDetailScreen.kt
  • app/src/main/java/com/byeboo/app/presentation/quest/component/bottomsheet/MoreOptionsBottomSheet.kt

Comment on lines +82 to +85
AnswerDetailTopBar(
onClickMoreOptions = onClickMoreOptions,
modifier = modifier,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

외부 modifier를 내부 컴포넌트에 전달

CommonAnswerScreenmodifier 파라미터를 AnswerDetailTopBar에 다시 전달하고 있습니다. 이 modifier는 이미 Line 72의 Column에 적용되어 있으므로, AnswerDetailTopBar에는 빈 Modifier 또는 별도의 modifier를 사용해야 합니다.

🐛 수정 제안
         AnswerDetailTopBar(
             onClickMoreOptions = onClickMoreOptions,
-            modifier = modifier,
+            modifier = Modifier,
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt`
around lines 82 - 85, CommonAnswerScreen currently forwards its incoming
modifier to AnswerDetailTopBar even though that modifier is already applied to
the parent Column (see Column in CommonAnswerScreen); change the call to
AnswerDetailTopBar to pass a fresh/empty Modifier (or omit the modifier argument
if optional) instead of the external modifier to avoid double-applying the same
modifier; update the invocation of AnswerDetailTopBar in CommonAnswerScreen and
ensure any local styling needed by AnswerDetailTopBar is provided via a
dedicated internal modifier rather than the external parameter.

questQuestion = "그 사람이 싫어하기에 내가 포기해야만 했던 일은 무엇일까?",
)

Spacer(modifier = Modifier.padding(bottom = screenHeightDp(10.dp)))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Spacer에 padding 대신 height 사용 권장

Spacer의 크기를 지정할 때 Modifier.padding(bottom = ...)은 의도한 대로 동작하지 않습니다. 수직 간격을 주려면 Modifier.height()를 사용하세요.

🐛 수정 제안
-        Spacer(modifier = Modifier.padding(bottom = screenHeightDp(10.dp)))
+        Spacer(modifier = Modifier.height(screenHeightDp(10.dp)))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Spacer(modifier = Modifier.padding(bottom = screenHeightDp(10.dp)))
Spacer(modifier = Modifier.height(screenHeightDp(10.dp)))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/all/CommonAnswerScreen.kt`
at line 94, Spacer에 Modifier.padding(bottom = ...)를 사용하면 의도한 수직 간격이 적용되지 않으므로,
해당 Spacer 호출(함수/파일의 Spacer(...) 사용 부분)에서 Modifier.padding을 제거하고 대신
Modifier.height(screenHeightDp(10.dp))로 크기를 지정해 수직 여백을 명시적으로 적용하도록 변경하세요; 즉 현재의
Spacer(modifier = Modifier.padding(bottom = screenHeightDp(10.dp)))를
Spacer(modifier = Modifier.height(screenHeightDp(10.dp)))로 바꾸면 됩니다.

Comment on lines +31 to +38
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_left),
contentDescription = null,
tint = ByeBooTheme.colors.gray50,
modifier =
Modifier.noRippleClickable(
// Todo: 뒤로가기
),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

접근성: contentDescription 누락

두 아이콘 모두 contentDescription = null로 설정되어 있어 스크린 리더 사용자가 버튼의 용도를 알 수 없습니다.

♿ 제안: contentDescription 추가
         Icon(
             imageVector = ImageVector.vectorResource(id = R.drawable.ic_left),
-            contentDescription = null,
+            contentDescription = "뒤로 가기",
             tint = ByeBooTheme.colors.gray50,
         Icon(
             imageVector = ImageVector.vectorResource(id = R.drawable.ic_overflow_menu),
-            contentDescription = null,
+            contentDescription = "더보기 옵션",
             tint = ByeBooTheme.colors.white,

Also applies to: 43-52

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/byeboo/app/presentation/quest/review/common/component/AnswerDetailTopBar.kt`
around lines 31 - 38, Two Icon composables currently set contentDescription =
null (e.g., the Icon using ImageVector.vectorResource(id = R.drawable.ic_left)
with Modifier.noRippleClickable) which prevents screen readers from conveying
the button purpose; update both Icon usages (the left/back Icon and the other
Icon in the 43-52 range) to provide meaningful, localized contentDescription
strings (use string resources like R.string.back or R.string.edit/action_name)
instead of null, and only keep null if the icon is purely decorative; ensure the
descriptions match the button action invoked by the surrounding clickable
(Modifier.noRippleClickable) so accessibility tools read the correct intent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍒 [FEAT] 새로운 기능 구현 🍥 [UI] UI 작업 🐰 아연 아연

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[feat] 공통 여정 퀘스트 돌아보기

2 participants