Skip to content

인증샷 상세 화면 인터렉션 수정#109

Merged
chanho0908 merged 4 commits intodevelopfrom
feat/#104-photolog-interaction
Feb 26, 2026
Merged

인증샷 상세 화면 인터렉션 수정#109
chanho0908 merged 4 commits intodevelopfrom
feat/#104-photolog-interaction

Conversation

@chanho0908
Copy link
Member

@chanho0908 chanho0908 commented Feb 25, 2026

이슈 번호

#104

작업내용

  • 인증샷 상세 화면 카드 스와이프 인터렉션 수정
  • 인증 상세 화면 초기값에 대한 UI가 렌더링 되는 문제 수정

결과물

Before After

리뷰어에게 추가로 요구하는 사항 (선택)

현재 변경된 인터렉션을 구현하긴 했는데 가은이가 이야기하는거 보니까 아직 이것도 픽스는 아닌듯 ㅇㅅㅇ,,,
우선 데모데이를 위해 IOS랑 동일하게만 구현했어

  • 내 사진에서 좌, 우로 스와이프 여부에 관계 없이 상대 사진은 오른쪽에서 스와이프
  • 상대 사진에서 좌우 스와이프 여부에 관계 없이 내 사진은 왼쪽에서 스와이프
  1. Before영상에 보이는 것 처럼 UiState의 초기 상태에 대한 UI가 렌더링 되는 현상이 발생해서 임시로 isLoading 프로퍼티를 통해
    조건부로 UI를 렌더링하게 만들었어

Topbar 까지 조건부 렌더링에 추가하면 아에 빈 화면이 렌더링 되서 뒤로가기도 못하는지라 Topbar는 제외했어
임시로 구현한거라 추후에 로딩 프로그래스바 디자인 나오면 적용하면 될듯 !

@chanho0908 chanho0908 self-assigned this Feb 25, 2026
@chanho0908 chanho0908 added the Feature Extra attention is needed label Feb 25, 2026
@chanho0908 chanho0908 linked an issue Feb 25, 2026 that may be closed by this pull request
1 task
@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

Task 인증 기능의 로딩 상태 관리 및 UI 구조를 개선하는 변경입니다. ViewModel에서 로딩 상태의 라이프사이클을 재정의하여 onStart에서만 true로 설정되고 오류 시에만 false로 리셋되도록 변경했습니다. 상세 화면의 레이아웃을 Scaffold에서 Column으로 전환하고, 로딩 상태에 따라 콘텐츠 렌더링을 조건부로 제어합니다. SwipeableCard 컴포넌트에서 수직 드래그 로직을 제거하여 수평 드래그만 사용하도록 단순화하고, UI 상태에 isLoading 프로퍼티를 추가하며, 메서드명을 toSerializer()에서 toNavArgs()로 변경합니다.

주요 리뷰 포인트

🤔 로딩 상태 관리 일관성 검토 필요

현재 상황:

  • TaskCertificationViewModel.upload()에서는 onStart에서만 loading을 true로 설정하고, onError에서만 false로 리셋합니다
  • TaskCertificationDetailViewModel.fetchPhotolog()에서는 onFinally에서 loading을 true로 설정합니다

왜 문제인가:
두 ViewModel에서 로딩 상태를 다르게 관리하면 나중에 유지보수할 때 혼란이 생길 수 있습니다. 특히 upload() 함수는 성공 시 isLoading이 언제 false로 돌아가는지 명확하지 않습니다.

개선 제안:

  • 두 ViewModel의 로딩 상태 라이프사이클을 통일할 수 있는지 검토해주세요
  • upload()의 onSuccess 블록에서도 isLoading을 false로 명시적으로 리셋하는 것이 더 명확할 것 같습니다
// 제안: 명시적인 상태 관리
launchResult(
    flow = uploadFlow,
    onStart = { reduce { copy(isLoading = true) } },
    onSuccess = { reduce { copy(isLoading = false) } },  // 명시적 추가
    onError = { reduce { copy(isLoading = false) } }
)

✅ 조건부 렌더링 로직 확인

좋은 점:
TaskCertificationDetail.kt에서 uiState.isLoading에 따라 콘텐츠 렌더링을 제어하는 것은 초기 로딩 중 불완전한 UI 렌더링을 방지하는 좋은 패턴입니다. 이렇게 하면 사용자에게 안정적인 경험을 제공할 수 있습니다.

확인 사항:

  • isLoading이 false일 때 화면에 아무것도 표시되지 않는 상황은 없나요?
  • 로딩 중이 아닐 때는 반드시 content가 렌더링되는지 UX 흐름을 확인해주세요

📐 제스처 로직 단순화 - 신중한 검토 권장

현재 상황:
SwipeableCard에서 수직 드래그(offsetY) 로직을 완전히 제거하고 수평 드래그(offsetX)만 사용하도록 변경했습니다.

확인할 사항:

  • 이전의 수직 드래그는 어떤 사용자 경험을 제공했나요?
  • 이번 변경이 의도한 제스처인지 확인이 필요합니다
  • 제거된 15줄의 로직이 다른 곳에서 처리되는지 확인해주세요
  • 스와이프 dismissal 애니메이션이 명확하고 부드러운지 테스트 결과를 공유해주시면 좋겠습니다

📋 메서드명 변경 - 명확하고 좋습니다

좋은 점:
toSerializer()toNavArgs() 변경은 메서드의 실제 목적(네비게이션 인자 구성)을 더 명확하게 전달합니다. 네이밍이 코드의 의도를 잘 반영하고 있습니다.

🔗 공개 API 변경 추적

확인 필요:

  • SwipeableCard에 새로운 파라미터 isDisplayingMyPhoto: Boolean이 추가되었습니다
  • 이 파라미터를 사용하는 모든 호출처에서 올바르게 전달되었는지 확인해주세요
  • 기존 호출처에서 default 값이 있는지 확인이 필요합니다

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly describes the main change: fixing card swipe interactions on the certification detail screen, which aligns with the primary objective and most significant code changes in the changeset.
Description check ✅ Passed The PR description is clearly related to the changeset, detailing the card swipe interaction fixes and UI rendering issues during initial loading state.

✏️ 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/#104-photolog-interaction

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt (1)

64-71: ⚠️ Potential issue | 🟠 Major

onFinally에서 isLoading = true 고정하면 fetch 실패 시에도 기본 UI가 노출됩니다

Line 70 로직은 성공/실패와 무관하게 본문 노출 조건을 true로 만듭니다. 초기 fetch 실패 시 기본값 기반 UI가 다시 보일 수 있어, 이번 PR의 깜빡임 방지 의도와 충돌합니다.
성공 시에만 본문 노출 플래그를 올리고, 실패 시에는 false 유지(또는 hasFetchedOnce 같은 별도 플래그 분리)로 정리하는 게 안전합니다.

🔧 제안 수정안
 private fun fetchPhotolog() {
     launchResult(
         block = { photologRepository.fetchPhotologs(argTargetDate) },
-        onSuccess = { reduce { it.toUiState(argGoalId, argBetweenUs, argTargetDate) } },
+        onSuccess = {
+            reduce {
+                it.toUiState(argGoalId, argBetweenUs, argTargetDate).copy(isLoading = true)
+            }
+        },
         onError = {
+            reduce { copy(isLoading = false) }
             showToast(R.string.task_certification_detail_fetch_photolog_fail, ToastType.ERROR)
         },
-        onFinally = { reduce { copy(isLoading = true) } },
     )
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt`
around lines 64 - 71, The current launchResult call sets isLoading = true in
onFinally which forces the UI body visible even on fetch failure; move the state
change so only successful fetch toggles visibility: remove or stop setting
isLoading in the onFinally handler and instead update the UI in onSuccess (e.g.,
in the onSuccess lambda call reduce { it.toUiState(...).copy(isLoading = true) }
or follow reduce { copy(isLoading = true) } inside onSuccess), leaving onError
to keep isLoading false (or use a separate hasFetchedOnce flag if needed) so
failures don't show the main content.
🧹 Nitpick comments (2)
feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailUiState.kt (1)

24-27: isLoading 네이밍이 실제 동작(로드 완료 후 표시)과 반대라 오해를 유발합니다

현재 플래그는 “로딩 중”이 아니라 “본문 렌더링 가능” 의미로 사용되고 있습니다. 이 상태로 유지되면 이후 조건문이 뒤집히거나 버그가 재발하기 쉬워집니다. isContentReady/isInitialized로 이름을 맞추거나, 진짜 로딩 의미를 유지하려면 화면 조건을 반전하는 방식으로 정리해보실까요?

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

In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailUiState.kt`
around lines 24 - 27, The flag isLoading in TaskCertificationDetailUiState is
misnamed (it actually represents "content ready" / "initialized"); rename the
property to a clear name like isContentReady or isInitialized and update all
usages (constructors, copies, condition checks) to match the new semantic;
alternatively keep the name but invert its logic where it's consumed (flip
boolean checks in UI rendering and any conditionals) so the variable truthfully
means "loading" — search for isLoading, the TaskCertificationDetailUiState data
class, and any places constructing or pattern-matching it to apply the rename or
invert consistently.
feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeableCard.kt (1)

126-131: 재등장 시작 방향을 카드 타입이 아니라 실제 스와이프 방향 기준으로 맞추는 것을 권장합니다

현재는 카드 종류(isDisplayingMyPhoto) 기준으로 재등장 위치가 고정됩니다. 그래서 일부 케이스에서 사용자가 민 방향과 애니메이션 방향 체감이 어긋날 수 있습니다. 의도적으로 “카드별 고정 방향”을 원하신 게 아니라면, targetX 부호를 재사용하면 더 일관됩니다.

🔧 제안 수정안
- val reappearStartX =
-     if (isDisplayingMyPhoto) {
-         spec.dismissDistancePx * spec.reappearOffsetRatio
-     } else {
-         -spec.dismissDistancePx * spec.reappearOffsetRatio
-     }
+ val reappearStartX = -targetX * spec.reappearOffsetRatio
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeableCard.kt`
around lines 126 - 131, The reappearStartX calculation currently uses
isDisplayingMyPhoto to choose direction, which can desync the visual swipe
motion; change it to derive sign from the actual swipe target (reuse targetX's
sign) so the reappear offset follows the swipe direction. Replace the
conditional that sets reappearStartX (using isDisplayingMyPhoto,
spec.dismissDistancePx and spec.reappearOffsetRatio) with logic that multiplies
spec.dismissDistancePx * spec.reappearOffsetRatio by sign(targetX) (or
Math.sign(targetX) equivalent) so reappearStartX always aligns with the swipe
target direction.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt`:
- Around line 146-149: 업로드의 2단계 실패 시에도 isLoading 플래그를 해제해야 합니다:
TaskCertificationViewModel 내 uploadPhotolog()와 modifyPhotolog()의 onError 핸들러에 현재
2차 API 호출 실패 시 isLoading=true가 유지되는 문제가 있으니 각 onError 콜백에 reduce {
copy(isLoading = false) }를 추가하여 로딩 상태를 해제하고 기존 showToast(...) 호출을 유지하도록 수정하세요.

---

Outside diff comments:
In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt`:
- Around line 64-71: The current launchResult call sets isLoading = true in
onFinally which forces the UI body visible even on fetch failure; move the state
change so only successful fetch toggles visibility: remove or stop setting
isLoading in the onFinally handler and instead update the UI in onSuccess (e.g.,
in the onSuccess lambda call reduce { it.toUiState(...).copy(isLoading = true) }
or follow reduce { copy(isLoading = true) } inside onSuccess), leaving onError
to keep isLoading false (or use a separate hasFetchedOnce flag if needed) so
failures don't show the main content.

---

Nitpick comments:
In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeableCard.kt`:
- Around line 126-131: The reappearStartX calculation currently uses
isDisplayingMyPhoto to choose direction, which can desync the visual swipe
motion; change it to derive sign from the actual swipe target (reuse targetX's
sign) so the reappear offset follows the swipe direction. Replace the
conditional that sets reappearStartX (using isDisplayingMyPhoto,
spec.dismissDistancePx and spec.reappearOffsetRatio) with logic that multiplies
spec.dismissDistancePx * spec.reappearOffsetRatio by sign(targetX) (or
Math.sign(targetX) equivalent) so reappearStartX always aligns with the swipe
target direction.

In
`@feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailUiState.kt`:
- Around line 24-27: The flag isLoading in TaskCertificationDetailUiState is
misnamed (it actually represents "content ready" / "initialized"); rename the
property to a clear name like isContentReady or isInitialized and update all
usages (constructors, copies, condition checks) to match the new semantic;
alternatively keep the name but invert its logic where it's consumed (flip
boolean checks in UI rendering and any conditionals) so the variable truthfully
means "loading" — search for isLoading, the TaskCertificationDetailUiState data
class, and any places constructing or pattern-matching it to apply the rename or
invert consistently.

ℹ️ Review info

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3af2e62 and 00e0bd9.

📒 Files selected for processing (7)
  • feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt
  • feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt
  • feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt
  • feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt
  • feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeableCard.kt
  • feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailUiState.kt
  • feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt

@chanho0908 chanho0908 requested a review from dogmania February 25, 2026 19:13
Copy link
Member

@dogmania dogmania left a comment

Choose a reason for hiding this comment

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

고생하셨슴다~!

@chanho0908 chanho0908 merged commit 0d9e50a into develop Feb 26, 2026
14 checks passed
@chanho0908 chanho0908 deleted the feat/#104-photolog-interaction branch February 26, 2026 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature Extra attention is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

인증샷 상세 화면 인터렉션 수정

2 participants