Conversation
Walkthrough여러 컴포저블 파일에서 UI 개선 및 API 수정을 진행합니다. SearchBarTopSheet의 공개 API를 확장하고 (새로운 콜백 추가, 매개변수 이름 변경), 다양한 Bottom Sheet 컴포넌트의 상태 관리와 레이아웃을 개선합니다. 일부 함수의 가시성을 조정하고 불필요한 import를 정리합니다. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~40 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Important Action Needed: IP Allowlist UpdateIf your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:
Failure to add the new IP will result in interrupted reviews. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
feature/file/src/main/java/com/example/file/ui/bottom/sheet/TopFolderEditBottomSheet.kt (1)
25-31: Non-null assertion (!!) 사용으로 인한 잠재적 NPE 위험
folderStateViewModel.readyToUpdateTopFolder!!.folderName에서!!연산자를 사용하고 있습니다.onColorIdDeliver콜백이 실행되는 시점에readyToUpdateTopFolder가 null일 경우 앱이 크래시됩니다.안전한 처리를 위해 null 체크를 추가하거나 early return 패턴을 사용하는 것을 권장합니다.
🔧 안전한 null 처리 제안
onColorIdDeliver = { colorId -> + val topFolder = folderStateViewModel.readyToUpdateTopFolder ?: return@TextFieldFileBottomSheet fileViewModel.updateCategoryColor( - categoryName = folderStateViewModel.readyToUpdateTopFolder!!.folderName, + categoryName = topFolder.folderName, colorId = (colorId + 1).toLong(), colorStyle = CategoryColorStyle.categoryStyleList[colorId] ) },feature/file/src/main/java/com/example/file/ui/bottom/sheet/NewBottomFolderBottomSheet.kt (1)
13-20:NewBottomFolderBottomSheet에서sheetState파라미터 누락 - 다른 폴더 관련 바텀 시트와 일관성 부족
BottomFolderEditBottomSheet,TopFolderEditBottomSheet,ShareBottomSheet,LinkCategorizationBottomSheet는 모두sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)를 명시적으로 전달하여 바텀 시트가 부분 확장 상태를 건너뛰고 바로 전체 확장되도록 합니다.
NewBottomFolderBottomSheet는sheetState를 전달하지 않아TextFieldFileBottomSheet의 기본값(skipPartiallyExpanded = false)을 사용하므로, 이 컴포넌트만 부분 확장 상태를 허용합니다.동일한 목적의 다른 폴더 관련 바텀 시트들과 동작을 맞추기 위해
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)를 전달하는 것을 권장합니다.
🤖 Fix all issues with AI agents
In `@design/src/main/java/com/example/design/top/search/SearchBarTopSheet.kt`:
- Around line 149-156: The debounce coroutine restarts because
LaunchedEffect(text) relaunches on every text change; change it to
LaunchedEffect(Unit) so the coroutine starts once and uses snapshotFlow to
observe text changes, keeping the existing snapshotFlow { text }. Ensure you
keep the chain .map { it.trim() }.filter { it.length >= 2
}.debounce(350).distinctUntilChanged().collectLatest(onQueryChange) and remove
the text key from LaunchedEffect so debounce works as intended with
onQueryChange.
- Around line 123-128: The LaunchedEffect(visible) in SearchBarTopSheet calls
resetAndDismiss() when visible becomes false, which can cause onDismiss() to be
invoked twice; change the behavior so the effect only resets internal state and
does not call onDismiss(): either remove or refactor the onDismiss() call out of
resetAndDismiss() (e.g., create a dedicated resetState() used by the
LaunchedEffect) and keep onDismiss() invocation only at the external dismiss
entrypoint; update calls to use resetState()/resetAndDismiss() accordingly and
retain names resetAndDismiss() and onDismiss() for clarity.
In `@feature/file/build.gradle.kts`:
- Line 56: The project mixes Compose BOM and an explicit version for
foundation.layout; decide one approach and make it consistent: either remove the
explicit version reference for foundation.layout (remove
version.ref="foundationLayout" on
implementation(libs.androidx.compose.foundation.layout) so the
androidx.compose.bom manages it) or remove the androidx.compose.bom dependency
and keep the explicit foundation.layout version; update the implementation entry
for foundation.layout or the BOM declaration accordingly and ensure
imports/usages (e.g., Box, Column, Row, Arrangement, padding) continue to
compile.
In
`@feature/file/src/main/java/com/example/file/ui/top/bar/component/TopFolderListMenu.kt`:
- Around line 197-201: The clickable menu item using noRippleClickable (where
enabled = selectedOption != selectedText and onClick = onClick) lacks an
explicit accessibility role; update the call to pass the semantics role (e.g.,
Role.Button) — i.e., add the role parameter to noRippleClickable so TalkBack
users receive the proper element role (you can also include onClickLabel if
desired for a more descriptive announcement).
🧹 Nitpick comments (9)
gradle/libs.versions.toml (1)
34-34: foundation과 foundation-layout 버전 불일치.
foundation버전(1.9.5, Line 33)과foundationLayout버전(1.10.0)이 다릅니다. 두 라이브러리는 같은 Compose Foundation 모듈 계열이므로 버전이 일치해야 호환성 문제를 방지할 수 있습니다.Compose BOM(
composeBom = "2024.09.00")을 사용 중이라면, BOM이 버전을 자동으로 관리하도록 개별 버전 지정 없이 사용하는 것을 권장합니다.♻️ BOM 사용 시 권장 수정안
BOM을 통해 버전을 관리하려면:
-androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "foundationLayout" } +androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" }또는 명시적 버전을 유지하려면 foundation과 동일한 버전으로 통일:
-foundationLayout = "1.10.0" +foundationLayout = "1.9.5"design/src/main/java/com/example/design/modifier/GradientTint.kt (1)
20-26: 중복된 주석을 제거하세요.새로 추가된 KDoc(lines 9-19)이 이미 파라미터들을 충분히 설명하고 있으므로, 함수 내부의 인라인 주석(lines 21-24)은 중복입니다.
♻️ 제안하는 수정
fun Modifier.gradientTint( - /* - * brush: 그라데이션 색상을 적용할 브러시 - * blendMode: 그라데이션 색상을 적용할 때 사용할 블렌드 모드 - * */ brush: Brush, blendMode: BlendMode = BlendMode.SrcIn ): Modifier = this.graphicsLayer(alpha = 0.99f)design/src/main/java/com/example/design/top/search/SearchBarTopSheet.kt (5)
130-139: 주석 처리된 코드를 제거하세요.이전 구현이 주석 처리되어 있습니다. 버전 관리 시스템에서 히스토리를 추적할 수 있으므로, 사용하지 않는 코드는 삭제하는 것이 좋습니다.
348-354:resetAndDismiss()함수를 일관되게 사용하세요.배경 클릭 핸들러와 뒤로가기 버튼(lines 411-414)에서 동일한 로직(
text = "",isEditMode = false,onDismiss())이 중복됩니다.resetAndDismiss()함수가 이미 정의되어 있으므로 이를 활용하세요.단, 앞서 언급한
LaunchedEffect(visible)수정을 적용한 후에 사용해야 합니다.♻️ 제안하는 수정 (배경 클릭)
.noRippleClickable { - text = "" - isEditMode = false - onDismiss() + resetAndDismiss() }
300-301: 사용되지 않는uriHandler변수.
uriHandler가 선언되었지만 사용되지 않습니다. 불필요한 변수는 제거하세요.♻️ 제안하는 수정
- // uri 핸들러 - val uriHandler = LocalUriHandler.current
LocalUriHandlerimport도 함께 제거하세요.
247-248: Regex 객체 생성 최적화를 고려하세요.
Regex객체가 매 리컴포지션마다 생성됩니다.searchTerm이 변경될 때만 재생성하도록remember를 사용할 수 있습니다.♻️ 제안하는 수정
+ val regex = remember(searchTerm) { + Regex("(?i)${Regex.escape(searchTerm)}") + } - val regex = Regex("(?i)${Regex.escape(searchTerm)}")단,
HighlightedText가 내부 컴포저블이므로 영향은 제한적입니다.
306-309: 에러 처리 로직 검토 필요.
runCatching의onFailure블록이 비어 있어 오류가 무시됩니다.onLinkClick은 콜백이므로 예외 발생 가능성이 낮지만, 필요시 로깅을 추가하거나runCatching을 제거하는 것을 고려하세요.feature/file/src/main/java/com/example/file/ui/top/bar/component/TopFolderListMenu.kt (2)
68-101: 상태 업데이트를 단일 ViewModel 함수로 묶어 일관성 확보
여러update*호출이 분산돼 있어 중간 상태가 노출되거나 불필요한 재구성이 발생할 수 있습니다. 선택 로직과getSharedFolders()호출을 ViewModel의 단일 메서드로 캡슐화해 원자적으로 처리하는 쪽을 권장합니다.
129-163: 하드코딩 문자열을 string resource로 분리 권장
"나의 폴더","공유받은 폴더"가 코드에 직접 박혀 있어 i18n 및 UI 테스트에 불리합니다.stringResource(R.string...)로 분리하면 관리가 쉬워집니다.
design/src/main/java/com/example/design/top/search/SearchBarTopSheet.kt
Outdated
Show resolved
Hide resolved
| // 체크가 안된 항목만 체크 가능 | ||
| .noRippleClickable( | ||
| enabled = selectedOption != selectedText, | ||
| onClick = onClick | ||
| ), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n "fun\s+noRippleClickable\b" -S --context 10Repository: LinkYou-2025/LinkU_Android
Length of output: 52
🏁 Script executed:
rg "noRippleClickable" -t kt --context 10Repository: LinkYou-2025/LinkU_Android
Length of output: 96
🏁 Script executed:
rg "noRippleClickable" --type-list | grep -i kotlinRepository: LinkYou-2025/LinkU_Android
Length of output: 459
🏁 Script executed:
rg "noRippleClickable" -A 15 --color neverRepository: LinkYou-2025/LinkU_Android
Length of output: 50382
🏁 Script executed:
cat design/src/main/java/com/example/design/modifier/NoRippleClickable.ktRepository: LinkYou-2025/LinkU_Android
Length of output: 1353
🏁 Script executed:
sed -n '190,210p' feature/file/src/main/java/com/example/file/ui/top/bar/component/TopFolderListMenu.ktRepository: LinkYou-2025/LinkU_Android
Length of output: 499
noRippleClickable 사용 시 명시적 접근성 Semantics 추가 권장
noRippleClickable 구현부는 role, onClickLabel 등 접근성 semantics를 올바르게 보존하고 있습니다. 다만 이 메뉴 항목(lines 197-201)에서는 role 파라미터가 명시되지 않아, TalkBack 사용자에게 요소의 역할이 명확하게 전달되지 않을 수 있습니다.
.noRippleClickable(enabled = selectedOption != selectedText, role = Role.Button, onClick = onClick)
와 같이 role 파라미터를 추가하여 더 나은 접근성 경험을 제공하시기 바랍니다.
🤖 Prompt for AI Agents
In
`@feature/file/src/main/java/com/example/file/ui/top/bar/component/TopFolderListMenu.kt`
around lines 197 - 201, The clickable menu item using noRippleClickable (where
enabled = selectedOption != selectedText and onClick = onClick) lacks an
explicit accessibility role; update the call to pass the semantics role (e.g.,
Role.Button) — i.e., add the role parameter to noRippleClickable so TalkBack
users receive the proper element role (you can also include onClickLabel if
desired for a more descriptive announcement).
gradle/libs.versions.toml
Outdated
| androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" } | ||
| androidx-compose-testing = { group = "androidx.xr.compose", name = "compose-testing", version.ref = "composeTesting" } | ||
| androidx-tools-core = { group = "androidx.privacysandbox.tools", name = "tools-core", version.ref = "toolsCore" } | ||
| androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout", version.ref = "foundationLayout" } |
There was a problem hiding this comment.
git 충돌이 있어서, 아침에 이 부분만 수정해서 아침에 커밋 푸쉬했습니다.
| enabled: Boolean = true, | ||
| onClickLabel: String? = null, | ||
| role: Role? = null, | ||
| onClick: () -> Unit |
There was a problem hiding this comment.
여기 변경된 파라미터에 맞춰서 수정하겠습니다.
|
|
||
| // 입력 변화 디바운스 수집 (2자 이상 + 350ms) | ||
| // - mapLatest: 새 입력이 오면 이전 요청(코루틴 Job) 자동 취소 → 레이스 방지 | ||
| // 닫힐 때 상태 초기화 로직 (visible이 false가 될 때) |
There was a problem hiding this comment.
여기에 사용자가 탑 시트를 열었을 때, 자동으로 검색창에 커서가 가고 키보드 올라오도록 FocusRequester를 이용하도록 리펙토링 해보시는건 어떨까요?
| LaunchedEffect(text) { | ||
| snapshotFlow { text } | ||
| .map { it.trim() } | ||
| .filter { it.length >= 2 } |
There was a problem hiding this comment.
검색했을 때, 한 글자만 검색한 사용자 입장에서 왜 검색이 안되지? 싶을 수 있어서, 디자이너와 상의 후 관련 ui 추가하는 건 어떠실까요?
There was a problem hiding this comment.
검색했을 때, 한 글자만 검색한 사용자 입장에서 왜 검색이 안되지? 싶을 수 있어서, 디자이너와 상의 후 관련 ui 추가하는 건 어떠실까요?
윤다인과 이다현의 TODO. 한글자일 때 비활성화+상태메시지 ui
| onSelectSharedFolders: () -> Unit | ||
| ){ | ||
| // 선택된 폴더 | ||
| val selectedText = if (isSharedFolders) "공유받은 폴더" else "나의 폴더" |
There was a problem hiding this comment.
enum으로 분리하는게 깔끔할 것 같아용
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
design/src/main/java/com/example/design/top/search/SearchBarTopSheet.kt (1)
400-414: 클릭 가능한 아이콘에 접근성 라벨을 추가해주세요.뒤로가기 및 검색어 지우기 버튼이
.noRippleClickable등으로 클릭 가능하지만contentDescription = null로 설정되어 있어 스크린리더에서 읽히지 않습니다. Jetpack Compose 접근성 가이드라인에 따르면, 클릭 가능한 Icon/Image 요소는 반드시 의미있는contentDescription을 제공해야 합니다.제안 변경
Icon( modifier = Modifier .height(40.dp) .noRippleClickable (onClick = onDismiss), tint = colors.gray[600], painter = painterResource(id = R.drawable.ic_back), - contentDescription = null + contentDescription = "뒤로가기" )if(text.isNotEmpty()){ Image( modifier = Modifier .padding(end = 18.dp) .size(18.dp) .noRippleClickable { text = "" }, painter = painterResource(R.drawable.ic_text_clear), - contentDescription = null + contentDescription = "검색어 지우기" ) }
🧹 Nitpick comments (2)
design/src/main/java/com/example/design/top/search/SearchBarTopSheet.kt (1)
129-138: 주석 처리된 이전 디바운스 블록은 제거하는 편이 좋습니다.
현재 구현이 있으므로 주석 블록은 혼동만 유발합니다.🧹 제안 변경
-/* - LaunchedEffect(Unit) { - snapshotFlow { text } - .map { it.trim() } - .filter { it.length >= 2 } // 2자 이상일 때만 - .debounce(350) // 350ms 주기 탐색 - .distinctUntilChanged() - .mapLatest(onQueryChange) - .collect { /* no-op */ } - }*/feature/home/src/main/java/com/example/home/screen/HomeScreen.kt (1)
203-205: itemsToRender/titleText 중복 선언과 하드코딩 이름은 정리 권장합니다.
바깥 titleText가 “세나님”으로 고정되고 내부에서 다시 선언되어 혼동 여지가 있습니다. 하나만 유지하고 userName 기반으로 통일하세요.♻️ 제안 변경
- val titleText = if (showRecs) "세나님의 오늘에 어울리는 콘텐츠예요!" + val titleText = if (showRecs) "${userName}님의 오늘에 어울리는 콘텐츠예요!" else "${userName}님이 최근에 열람한 링크" @@ - val itemsToRender = if (showRecs) recommendedLinks else recentLinks - val titleText = if (showRecs) "${userName}님의 오늘에 어울리는 콘텐츠예요!" - else "${userName}님이 최근에 열람한 링크"Also applies to: 257-264
c2f8ac9 to
9834a5c
Compare
- `feature/file/build.gradle.kts`에서 `androidx.compose.foundation.layout` 의존성을 `androidx.compose.foundation`으로 변경
- `LinksGrid.kt` 내 삭제 확인 모달의 버튼 텍스트를 "삭제" -> "삭제하기", "취소" -> "취소하기"로 변경 - 미사용 임포트(`lifecycleScope`, `rememberCoroutineScope`, `launch` 등) 제거 및 임포트 최적화
- `FileBottomSheet`에 0.5 투명도의 블랙 딤 컬러(`scrimColor`) 적용 - 텍스트 간격 조정을 위해 `Spacer` 추가 및 불필요한 `lineHeight`, `padding` 제거
- `animateFloatAsState`의 `label` 파라미터에 "화살표 회전 애니메이션" 명시 및 `targetValue` 네임드 파라미터 적용
- `LinkItemLayout`에 그림자 효과(`shadow`)를 적용하고 기존 `shadowElevation` 속성 제거 - 태그 UI 레이아웃 수정: `padding` 값 조정, 폰트 크기 변경(10sp -> 12sp), `includeFontPadding = false` 적용 - 컴포넌트 간 간격 조정을 위해 `Spacer` 추가 및 기존 `padding` 로직 수정 - 불필요한 `showDialog` 상태 및 미사용 import 제거 - 클릭 이벤트 로직 내 불필요한 null safety 체크 최적화
- `selectedLinks`를 `mutableStateListOf`로 변경하여 상태 추적 지원 - 바텀시트 '추가' 버튼 활성화 상태(`isReady`) 조건 추가 (선택된 링크가 있을 때만 활성화) - 링크 아이템 레이아웃 수정: `offset`, `padding`, `shape` 및 정렬 조정 - 체크박스 미선택 시 색상(`uncheckedColor`) 변경 - 링크 선택 시 데이터 정합성을 위해 `onOkay` 콜백 내 `selectedLinks.clear()` 로직 추가
- `ShareFolderIcon`, `LockFolderIcon`, `PencilIcon`, `BookMarkStar` 함수에 `internal` 접근 제한자 적용
`LaunchedEffect`의 key를 `text`에서 `Unit`으로 변경하여, 컴포저블이 처음 생성될 때만 검색 로직 관련 Flow가 생성되도록 수정했습니다. 이를 통해 불필요한 초기 호출을 방지합니다.
There was a problem hiding this comment.
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 (2)
feature/file/src/main/java/com/example/file/ui/bottom/sheet/LinkCategorizationBottomSheet.kt (1)
101-138:⚠️ Potential issue | 🟠 Major
checked상태와selectedLinks의 동기화 문제로 UI 불일치가 발생합니다.아이템마다
remember로 초기화된checked는selectedLinks.clear()이후에도 true로 유지되며, 목록이 재구성될 때 remember 상태가 위치 기반으로 재사용되어 상태가 꼬입니다.checked를selectedLinks.contains(link)에서 파생시키고, 중복된 토글 로직을 단일 함수로 통합하세요. 또한items()에 안정적인 key를 지정하여 remember 상태 뒤틀림을 방지하는 것을 권장합니다.✅ 제안 수정안
- items(links) { - val link = it + items(links, key = { it.id }) { + val link = it val title = it.title val url = it.url val icon = domainLogoPainterOrNull(it.url)?:painterResource(R.drawable.link_categorization_default) val img = painterResource(R.drawable.link_categorization_default)//it.img - var checked by remember { mutableStateOf(false)} + val checked = selectedLinks.contains(link) + val toggleSelection = { + if (selectedLinks.contains(link)) { + selectedLinks.remove(link) + } else { + selectedLinks.add(link) + } + } Row( modifier = Modifier .height(60.dp) .offset(x = (-20).dp) .noRippleClickable{ - if(checked){ - Log.d("LinkCategorizationBottomSheet", "checked: $it") - selectedLinks.remove(link) - checked = selectedLinks.contains(link) - } else { - Log.d("LinkCategorizationBottomSheet", "checked: $it") - selectedLinks.add(link) - checked = selectedLinks.contains(link) - } + Log.d("LinkCategorizationBottomSheet", "checked: $it") + toggleSelection() }, horizontalArrangement = Arrangement.spacedBy(10.dp), verticalAlignment = Alignment.CenterVertically ) { Checkbox( checked = checked, onCheckedChange = { - if(checked){ - Log.d("LinkCategorizationBottomSheet", "checked: $it") - selectedLinks.remove(link) - checked = selectedLinks.contains(link) - } else { - Log.d("LinkCategorizationBottomSheet", "checked: $it") - selectedLinks.add(link) - checked = selectedLinks.contains(link) - } + Log.d("LinkCategorizationBottomSheet", "checked: $it") + toggleSelection() },(주:
it.id는LinkItemInfo의 실제 고유 식별자로 대체하세요)feature/file/src/main/java/com/example/file/ui/item/LinkItemLayout.kt (1)
150-166:⚠️ Potential issue | 🟡 MinorSurface 모서리와 그림자 모양 일치 필요
shape파라미터가 주석 처리되어 있으면 Surface의 배경과 클리핑이 기본값(직사각형)으로 적용됩니다. 현재.shadow()수정자에서 18dp 라운드를 지정했지만, Surface 자체는 직사각형으로 렌더링되어 모서리와 그림자 outline이 불일치합니다.shape = RoundedCornerShape(18.dp)를 활성화하여 Surface의 배경 클리핑도 같은 라운드로 일치시키세요.🎯 모서리 일치용 수정안
Surface( modifier = Modifier .width(181.dp) .then(modifier) .shadow( elevation = 4.dp, shape = RoundedCornerShape(18.dp) ), - //shape = RoundedCornerShape(18.dp), + shape = RoundedCornerShape(18.dp), color = White, ) {
🤖 Fix all issues with AI agents
In
`@feature/file/src/main/java/com/example/file/ui/bottom/sheet/LinkCategorizationBottomSheet.kt`:
- Around line 80-90: 사용자가 폴더를 선택하지 않았을 때
requireNotNull(folderStateViewModel.selectedBottomFolder?.folderId)로 인해 앱이 크래시
발생하므로, selectedBottomFolder의 유효성을 검사하도록 수정하세요: UI 상태 검사 지점인 isReady에
folderStateViewModel.selectedBottomFolder != null 조건을 추가하거나, 더 안전하게 onOkay
블록(함수명/속성: onOkay, scope.launch) 시작부에서 val folderId =
folderStateViewModel.selectedBottomFolder?.folderId ?: return@launch 식의
early-return을 추가해 folderId가 없으면 작업을 중단하고 selectedLinks 순회나 updateLinkFolder 호출을
하지 않도록 하세요; 관련 식별자: selectedBottomFolder, isReady, onOkay, folderStateViewModel,
selectedLinks, updateLinkFolder, requireNotNull.
🧹 Nitpick comments (2)
design/src/main/java/com/example/design/top/search/SearchBarTopSheet.kt (1)
342-355: resetAndDismiss 재사용으로 중복 제거 가능.
동일 초기화 로직을 한 곳으로 모으면 유지보수성이 좋아집니다.feature/file/src/main/java/com/example/file/ui/item/LinkItemLayout.kt (1)
135-147: 터치 피드백 제거로 인해 클릭 인지가 약해질 수 있습니다.
indication = null로 기본 리플이 사라져 인터랙션 피드백이 없어집니다. 의도된 디자인이 아니라면 기본 indication을 살리거나 별도 피드백을 넣는 편이 UX에 유리합니다.✅ 기본 리플 복원 예시
- indication = null, interactionSource = remember { MutableInteractionSource() },
| isReady = selectedLinks.isNotEmpty(), | ||
| visible = folderStateViewModel.linkCategorizationBottomSheetVisible, | ||
| onOkay = { | ||
| scope.launch{ | ||
| selectedLinks.map { | ||
| fileViewModel.updateLinkFolder( | ||
| it, | ||
| folderStateViewModel.selectedBottomFolder?.folderId!! | ||
| ) | ||
| val folderId = requireNotNull(folderStateViewModel.selectedBottomFolder?.folderId) | ||
|
|
||
| selectedLinks.forEach { | ||
| fileViewModel.updateLinkFolder(it, folderId) | ||
| } | ||
|
|
||
| selectedLinks.clear() |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "LinkCategorizationBottomSheet.kt" -type fRepository: LinkYou-2025/LinkU_Android
Length of output: 164
🏁 Script executed:
wc -l ./feature/file/src/main/java/com/example/file/ui/bottom/sheet/LinkCategorizationBottomSheet.ktRepository: LinkYou-2025/LinkU_Android
Length of output: 168
🏁 Script executed:
cat -n ./feature/file/src/main/java/com/example/file/ui/bottom/sheet/LinkCategorizationBottomSheet.ktRepository: LinkYou-2025/LinkU_Android
Length of output: 11453
🏁 Script executed:
find . -name "FolderStateViewModel.kt" -type fRepository: LinkYou-2025/LinkU_Android
Length of output: 162
🏁 Script executed:
wc -l ./feature/file/src/main/java/com/example/file/viewmodel/folder/state/FolderStateViewModel.ktRepository: LinkYou-2025/LinkU_Android
Length of output: 166
🏁 Script executed:
cat -n ./feature/file/src/main/java/com/example/file/viewmodel/folder/state/FolderStateViewModel.ktRepository: LinkYou-2025/LinkU_Android
Length of output: 6378
selectedBottomFolder가 null이면 앱이 크래시됩니다.
selectedBottomFolder는 nullable 타입(FolderSimpleInfo?)으로 선언되어 있고, isReady는 선택된 링크 여부만 확인하므로 폴더 유효성을 검증하지 않습니다. 사용자가 폴더를 선택하지 않은 상태에서 버튼을 클릭하면 requireNotNull(folderStateViewModel.selectedBottomFolder?.folderId)에서 NPE가 발생하여 앱이 강제 종료됩니다. 폴더 존재 여부를 isReady에 추가하거나 onOkay에서 안전한 early-return 처리를 하세요.
🛡️ 제안 수정안
- isReady = selectedLinks.isNotEmpty(),
+ isReady = selectedLinks.isNotEmpty() &&
+ folderStateViewModel.selectedBottomFolder?.folderId != null,
visible = folderStateViewModel.linkCategorizationBottomSheetVisible,
onOkay = {
scope.launch{
- val folderId = requireNotNull(folderStateViewModel.selectedBottomFolder?.folderId)
+ val folderId =
+ folderStateViewModel.selectedBottomFolder?.folderId ?: return@launch
selectedLinks.forEach {
fileViewModel.updateLinkFolder(it, folderId)
}
selectedLinks.clear()
}
},🤖 Prompt for AI Agents
In
`@feature/file/src/main/java/com/example/file/ui/bottom/sheet/LinkCategorizationBottomSheet.kt`
around lines 80 - 90, 사용자가 폴더를 선택하지 않았을 때
requireNotNull(folderStateViewModel.selectedBottomFolder?.folderId)로 인해 앱이 크래시
발생하므로, selectedBottomFolder의 유효성을 검사하도록 수정하세요: UI 상태 검사 지점인 isReady에
folderStateViewModel.selectedBottomFolder != null 조건을 추가하거나, 더 안전하게 onOkay
블록(함수명/속성: onOkay, scope.launch) 시작부에서 val folderId =
folderStateViewModel.selectedBottomFolder?.folderId ?: return@launch 식의
early-return을 추가해 folderId가 없으면 작업을 중단하고 selectedLinks 순회나 updateLinkFolder 호출을
하지 않도록 하세요; 관련 식별자: selectedBottomFolder, isReady, onOkay, folderStateViewModel,
selectedLinks, updateLinkFolder, requireNotNull.
# Conflicts: # gradle/libs.versions.toml
- 비밀번호 재설정, 구분선, 회원가입 버튼의 배치를 `Box`의 오프셋 방식에서 `Row`를 사용한 정렬 방식으로 변경 - `Arrangement.spacedBy`를 사용하여 요소 간 간격 조정 및 중앙 정렬 적용
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
feature/login/src/main/java/com/example/login/ui/screen/EmailLoginScreen.kt (1)
236-238:⚠️ Potential issue | 🟡 Minor사용되지 않는 변수 (Dead code)
resetStartPos,dividerStartPos,signUpStartPos변수들이 정의되어 있지만, Row 기반 레이아웃으로 리팩토링되면서 더 이상 사용되지 않습니다. 이 변수들을 삭제해 주세요.🧹 제안된 수정 사항
- // 🔑 비율 기반 가로 위치 계산 - // 디자인 기준 너비 412 대비 현재 화면의 비율 지점 - val resetStartPos = (101.scaler) // 비밀번호 재설정 시작점 - val dividerStartPos = (220.scaler) // | 시작점 - val signUpStartPos = (247.scaler) // 회원가입 시작점 - Row(
🧹 Nitpick comments (1)
feature/login/src/main/java/com/example/login/ui/screen/EmailLoginScreen.kt (1)
245-245: 단위 일관성 확인 필요
Arrangement.spacedBy(25.dp, ...)에서25.dp를 사용하고 있는데, 이 파일의 다른 부분에서는 반응형 스케일링을 위해.scaler를 사용하고 있습니다. 의도적인 고정값이 아니라면 일관성을 위해25.scaler로 변경하는 것을 고려해 주세요.♻️ 제안된 수정 사항
- horizontalArrangement = Arrangement.spacedBy(25.dp, alignment = Alignment.CenterHorizontally), + horizontalArrangement = Arrangement.spacedBy(25.scaler, alignment = Alignment.CenterHorizontally),
📝 설명
✔️ PR 유형
어떤 변경 사항이 있나요?
📎 관련 이슈 번호
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항
스타일