Skip to content

[Feat] 탐색 화면 셀프 QA#168

Merged
nahy-512 merged 6 commits into
developfrom
feat/#167-explore-self-qa
Jan 22, 2026
Merged

[Feat] 탐색 화면 셀프 QA#168
nahy-512 merged 6 commits into
developfrom
feat/#167-explore-self-qa

Conversation

@giovannijunseokim

@giovannijunseokim giovannijunseokim commented Jan 22, 2026

Copy link
Copy Markdown
Contributor

📮 관련 이슈

📌 작업 내용

📸 스크린샷

자동 스크롤
auto-scroll.mp4

Summary by CodeRabbit

릴리스 노트

  • 새 기능

    • 컬렉션 상세 페이지에서 특정 이미지로 자동 스크롤하는 기능 추가
    • 컬렉션 상세 이동 시 특정 이미지 정보 전달 가능
  • 개선사항

    • 탐색(Explore) 항목의 제목 및 설명에 텍스트 오버플로우 처리 적용

✏️ Tip: You can customize this high-level summary in your review settings.

- `MainNavigator` 및 `navigateToCollectionDetail`에 `targetImageUrl` 파라미터 추가
- `CollectionDetailRoute`에 `targetImageUrl` 전달 로직 구현
- `ExploreScreen`에서 컬렉션 상세 이동 시 이미지 URL을 함께 전달하도록 수정
- `CollectionDetailScreen`에 `targetImageUrl` 파라미터 추가
- `onGloballyPositioned`를 사용하여 각 컨텐츠의 위치(y좌표) 저장
- `LaunchedEffect`를 통해 화면 진입 시 `targetImageUrl`에 해당하는 위치로 애니메이션 스크롤 동작 구현
@giovannijunseokim giovannijunseokim self-assigned this Jan 22, 2026
@giovannijunseokim giovannijunseokim requested a review from a team as a code owner January 22, 2026 13:07
@giovannijunseokim giovannijunseokim added Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경 📱 UI feat - 컴포넌트 조립, 화면 구현 labels Jan 22, 2026
@coderabbitai

coderabbitai Bot commented Jan 22, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

컬렉션 상세로 이동 시 대상 이미지 URL을 네비게이션 경로로 전파하고, 상세 화면에서 각 콘텐츠의 Y 좌표를 기록해 주어진 targetImageUrl로 자동 스크롤하는 로직을 추가했습니다. 관련 시그니처와 Content 컴포저블에 modifier 파라미터가 도입되었습니다.

Changes

Cohort / File(s) 변경 요약
Route 데이터 클래스
app/src/main/java/com/flint/core/navigation/Route.kt
CollectionDetail 데이터 클래스에 val targetImageUrl: String? = null 프로퍼티 추가.
CollectionDetail 화면 및 로직
app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt
CollectionDetailRoute/CollectionDetailScreentargetImageUrl 파라미터 추가; contentPositions: MutableMap<String, Int> 도입 및 각 아이템의 y 좌표를 onGloballyPositioned로 기록; LaunchedEffect로 targetImageUrl이 있으면 animateScrollTo로 자동 스크롤; Contentmodifier: Modifier = Modifier 파라미터 추가 및 호출부에 전달.
CollectionDetail 네비게이션
app/src/main/java/com/flint/presentation/collectiondetail/navigation/CollectionDetailNavigation.kt
NavController.navigateToCollectionDetail(collectionId: String, targetImageUrl: String? = null) 시그니처 추가 및 네비게이션 그래프에서 backStackEntry를 통해 route.targetImageUrl 을 UI에 전달하도록 변경.
Explore 화면 및 네비게이션
app/src/main/java/com/flint/presentation/explore/ExploreScreen.kt
app/src/main/java/com/flint/presentation/explore/navigation/ExploreNavigation.kt
Explore의 컬렉션 상세 네비게이션 콜백 시그니처를 (collectionId: String, imageUrl: String) -> Unit으로 변경하고, 버튼 클릭 시 imageUrl을 함께 전달하도록 수정; ExplorePageItem 텍스트에 overflow/maxLines 처리 추가.
MainNavigator
app/src/main/java/com/flint/presentation/main/MainNavigator.kt
navigateToCollectionDetail(collectionId: String, targetImageUrl: String? = null) 파라미터 추가 및 내부 네비게이션 호출에 전달.

Sequence Diagram(s)

sequenceDiagram
    participant Explore as ExploreScreen
    participant Nav as Navigation
    participant Detail as CollectionDetailScreen
    participant UI as LazyColumn
    participant Tracker as PositionTracker

    Explore->>Nav: navigateToCollectionDetail(collectionId, imageUrl)
    Nav->>Detail: CollectionDetailRoute(targetImageUrl=imageUrl)
    Detail->>Tracker: 초기화 contentPositions Map
    Detail->>UI: 렌더링 시작 (Content 항목들)
    UI->>Tracker: onGloballyPositioned -> y 좌표 전송
    Tracker-->>Detail: contentPositions 업데이트
    Detail->>Detail: LaunchedEffect(targetImageUrl, contentPositions)
    Detail->>UI: animateScrollToItem(target y position)
    UI-->>Detail: 스크롤 완료 이벤트
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • nahy-512

탐색에서 출발한 토끼가 뛰네, 🐰
한 줄 이미지로 길을 찾고,
좌표를 기록해 살짝 밀면,
스르륵 내게 도착하네 ✨
짧은 점프, 정확한 도착! 🎯

🚥 Pre-merge checks | ✅ 2 | ❌ 3
❌ Failed checks (1 warning, 2 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.
Title check ❓ Inconclusive The title '[Feat] 탐색 화면 셀프 QA' is vague and does not clearly convey the main changes; it refers to a feature category but lacks specificity about the actual implementation details. Clarify the title to highlight key changes, such as: '[Feat] 컬렉션 상세 페이지 자동 스크롤 및 텍스트 오버플로우 처리' to better communicate the primary objectives.
Linked Issues check ❓ Inconclusive The PR implements auto-scroll functionality to target images in collection detail pages and text overflow handling as reflected in code changes, but the linked issue #167 contains no explicit objectives or requirements. Fill in the linked issue #167 with explicit requirements and objectives, or document implementation details in the PR description to validate alignment with project goals.
✅ Passed checks (2 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The code changes include auto-scroll mechanism, text overflow handling, and navigation parameter extensions that are all related to the described PR objectives.
Description check ✅ Passed PR 설명이 필수 템플릿 구조를 따르고 있으나, 스크린샷 섹션에서 이미지/비디오 태그 형식이 올바르지 않으며, '미구현' 및 '리뷰어' 섹션이 누락되어 있습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt`:
- Line 46: contentPositions is a plain mutableMapOf so updates in
onGloballyPositioned aren't observed by Compose; replace its declaration with a
state-backed map using mutableStateMapOf (from androidx.compose.runtime) so
changes are tracked, keep onGloballyPositioned updating contentPositions as
before, and ensure the LaunchedEffect that depends on contentPositions.size now
observes changes; also add the necessary import for mutableStateMapOf.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt`:
- Around line 327-330: The code stores scroll positions using content.imageUrl
as the map key (in the Modifier.onGloballyPositioned block assigning
contentPositions[content.imageUrl] = coordinates.positionInParent().y.toInt()),
which can collide when multiple ContentModelNew items share the same imageUrl;
change the key to the unique identifier content.id (e.g.,
contentPositions[content.id] = ...) and update any related navigation/scroll
logic to pass and consume targetContentId instead of targetImageUrl so lookups
and scroll targets use the unique content id.
♻️ Duplicate comments (1)
app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt (1)

239-255: 자동 스크롤이 동작하지 않는 타이밍 버그

두 가지 문제가 있습니다:

  1. contentPositionsmutableMapOf()로 선언되어 Compose 상태로 추적되지 않습니다.
  2. LaunchedEffect(Unit)은 초기 컴포지션 시 한 번만 실행되는데, 이 시점에는 onGloballyPositioned 콜백이 아직 호출되지 않아 contentPositions가 비어있습니다. 따라서 contentPositions[targetImageUrl]은 항상 null을 반환하고 스크롤이 실행되지 않습니다.

mutableStateMapOf를 사용하고 LaunchedEffect의 key를 변경하여 위치 정보가 기록된 후 스크롤이 실행되도록 수정해야 합니다.

🐛 수정 제안
+import androidx.compose.runtime.mutableStateMapOf
+import androidx.compose.runtime.snapshotFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
...
-        val contentPositions: MutableMap<String, Int> = remember { mutableMapOf() }
+        val contentPositions = remember { mutableStateMapOf<String, Int>() }
...
-        LaunchedEffect(Unit) {
-            if (targetImageUrl == null) return@LaunchedEffect
-            val targetPosition: Int = contentPositions[targetImageUrl] ?: return@LaunchedEffect
-
-            scrollState.animateScrollTo(targetPosition)
-        }
+        LaunchedEffect(targetImageUrl) {
+            if (targetImageUrl == null) return@LaunchedEffect
+            snapshotFlow { contentPositions[targetImageUrl] }
+                .filter { it != null }
+                .first()
+                .let { targetPosition ->
+                    scrollState.animateScrollTo(targetPosition)
+                }
+        }

Comment on lines +327 to +330
modifier = Modifier.onGloballyPositioned { coordinates ->
contentPositions[content.imageUrl] =
coordinates.positionInParent().y.toInt()
},

@coderabbitai coderabbitai Bot Jan 22, 2026

Copy link
Copy Markdown
Contributor

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:

#!/bin/bash
# Find the ContentModelNew data class definition
rg -n "data class ContentModelNew" --type kotlin -A 20

Repository: imflint/Flint-Android

Length of output: 2040


🏁 Script executed:

#!/bin/bash
# Search for how targetImageUrl is used and validated
rg -n "targetImageUrl" --type kotlin -B 3 -A 3

Repository: imflint/Flint-Android

Length of output: 8111


🏁 Script executed:

#!/bin/bash
# Search for contentPositions map definition and usage
rg -n "contentPositions" --type kotlin -B 2 -A 2

Repository: imflint/Flint-Android

Length of output: 2208


🏁 Script executed:

#!/bin/bash
# Check the full context of CollectionDetailScreen.kt around lines 327-330
cat app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt | sed -n '310,345p'

Repository: imflint/Flint-Android

Length of output: 1612


🏁 Script executed:

#!/bin/bash
# Check if there's any validation or documentation about imageUrl uniqueness
rg -i "imageurl.*unique|unique.*imageurl" --type kotlin

Repository: imflint/Flint-Android

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Check how content is loaded and if there's any deduplication
rg -n "ContentModelNew" app/src/main/java/com/flint/presentation/collectiondetail/ --type kotlin -B 2 -A 2

Repository: imflint/Flint-Android

Length of output: 9813


🏁 Script executed:

#!/bin/bash
# Check the Content composable to see if imageUrl is used anywhere else critical
rg -n "fun Content" app/src/main/java/com/flint/presentation/ --type kotlin -A 15 | head -40

Repository: imflint/Flint-Android

Length of output: 3739


스크롤 대상 위치 저장 시 고유한 식별자 사용 필요

content.imageUrl을 키로 사용하는 것보다 content.id를 사용하는 것이 더 안전합니다. ContentModelNew에서 id는 각 콘텐츠의 고유한 식별자이고, imageUrl은 URL 자산일 뿐입니다.

동일한 imageUrl을 가진 여러 콘텐츠가 있을 경우 마지막 항목의 위치만 저장되어 의도치 않은 스크롤 동작이 발생할 수 있습니다. 다음과 같이 수정하세요:

수정 예시
contentPositions[content.id] =
    coordinates.positionInParent().y.toInt()

그리고 네비게이션 레이어도 targetImageUrl 대신 targetContentId를 전달하도록 조정해야 합니다.

🤖 Prompt for AI Agents
In
`@app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt`
around lines 327 - 330, The code stores scroll positions using content.imageUrl
as the map key (in the Modifier.onGloballyPositioned block assigning
contentPositions[content.imageUrl] = coordinates.positionInParent().y.toInt()),
which can collide when multiple ContentModelNew items share the same imageUrl;
change the key to the unique identifier content.id (e.g.,
contentPositions[content.id] = ...) and update any related navigation/scroll
logic to pass and consume targetContentId instead of targetImageUrl so lookups
and scroll targets use the unique content id.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

이전 화면 ExploreScreen에서 작품에 대한 정보로 줄 수 잇는게 content.imageUrl 뿐이야.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the ExploreScreen to see what data structure is used for collections
cat app/src/main/java/com/flint/presentation/explore/ExploreScreen.kt

Length of output: 9913


@giovannijunseokim, 이해했습니다! ExploreScreen에서 CollectionsModel.Collection이 가진 데이터가 collectionIdimageUrl뿐이니, 컬렉션의 썸네일 이미지 URL을 넘겨서 해당 작품으로 스크롤하는 방식이 현재 구조에서는 합리적인 선택이네요.

컬렉션의 imageUrl이 실제로 컬렉션 내 특정 작품의 imageUrl과 일치한다는 가정 하에 잘 동작할 것 같습니다.

다만, 나중에 코드를 보는 다른 개발자를 위해 간단한 주석을 추가하면 좋을 것 같습니다:

val contentPositions: MutableMap<String, Int> = remember { mutableMapOf() }

LaunchedEffect(Unit) {
    if (targetImageUrl == null) return@LaunchedEffect
    // ExploreScreen에서 컬렉션 썸네일 imageUrl을 전달받아 해당 작품으로 스크롤
    val targetPosition: Int = contentPositions[targetImageUrl] ?: return@LaunchedEffect
    scrollState.animateScrollTo(targetPosition)
}

이 정도면 충분할 것 같습니다!


🧬 Code Graph Analysis Results
  • File: app/src/main/java/com/flint/presentation/collectiondetail/CollectionDetailScreen.kt

    • Snippet: Content(...) (the composable that renders each content item)
    • Description:
      • Parameters: content: ContentModelNew, onBookmarkIconClick: (contentId: String) -> Unit, onSpoilClick: (contentId: String) -> Unit, modifier: Modifier = Modifier
      • Returns: Unit (Composable)
      • Behavior: Displays a content item, using NetworkImage to load the image via content.imageUrl (imageUrl field), with content-scale Crop, followed by metadata (title, year, director), bookmark UI, spoiler handling (Spoiler component when isSpoiler is true), and bookmark/spoil action callbacks. This is the UI surface that relies on a URL image for each piece of content.
  • File: app/src/main/java/com/flint/core/designsystem/component/image/NetworkImage.kt

    • Snippet: NetworkImage(...) implementation
    • Description:
      • Parameters: imageUrl: Any?, modifier: Modifier = Modifier, shape: Shape = RectangleShape, placePainter, errorPainter, contentScale: ContentScale = ContentScale.Crop, contentDescription: String? = null
      • Returns: Unit (Composable)
      • Behavior: Renders an image from a network source. In preview mode, shows a dummy image; otherwise uses AsyncImage with model = imageUrl, placeholder and error painters, appropriate contentScale, and clipping to the provided shape. This is the core mechanism by which content.imageUrl is visualized.
  • File: app/src/main/java/com/flint/core/designsystem/component/image/ProfileImage.kt

    • Snippet: ProfileImage(...) implementation
    • Description:
      • Parameters: imageUrl: String?, modifier: Modifier = Modifier, contentDescription: String? = null
      • Returns: Unit (Composable)
      • Behavior: Displays a profile image; if imageUrl is null or blank, shows a default avatar resource; otherwise loads the image with AsyncImage (including placeholder and error images) and clips to a circle. Relevant for how author/profile images could be presented if content includes user avatars derived from URLs.

@nahy-512 nahy-512 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM 👍🏻

@nahy-512 nahy-512 merged commit f373040 into develop Jan 22, 2026
2 checks passed
@nahy-512 nahy-512 deleted the feat/#167-explore-self-qa branch January 22, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feat ✨ 신규 기능을 추가하거나 기존 기능의 동작, 정책을 변경 📱 UI feat - 컴포넌트 조립, 화면 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 탐색 화면 셀프 QA

2 participants