Conversation
|
Caution Review failedThe pull request is closed. Walkthrough데이터 모델의 이미지 필드명을 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Nav as NavController
participant Feed as FeedScreen
participant VM as FeedViewModel
User->>Nav: 뒤로와서 Feed로 복귀 (savedState.refreshFeed=true)
Nav->>Feed: compose FeedScreen(refreshFeed=true)
Feed->>VM: LaunchedEffect(refreshFeed) -> VM.refreshData() / VM.pullToRefresh()
VM-->>Feed: 상태 업데이트 (isRefreshing / isPullToRefreshing, 리스트)
Feed->>Nav: onRefreshConsumed() -> savedState["refreshFeed"]=null
sequenceDiagram
autonumber
participant UI as SearchBookDetailScreen
participant Nav as NavController
participant FW as FeedWriteScreen
UI->>UI: onWriteFeedClick(bookDetail)
UI->>Nav: navigateToFeedWrite(isbn,title,author,imageUrl)
Nav->>FW: Compose FeedWriteScreen with prefilled BookData
sequenceDiagram
autonumber
participant Bar as GradationTopAppBar
participant Timer as CoroutineDelay
Bar->>Bar: count>0 && autoHideCount==true -> isCountVisible = true
Bar->>Timer: delay(countDisplayDurationMs)
Timer-->>Bar: timeout
Bar->>Bar: isCountVisible = false
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. 📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (4)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt (1)
185-191: onCustomerServiceClick 콜백이 호출되지 않습니다람다 참조만 있고 실제 호출이 없어 사이드이펙트(로그/트래킹/내부 네비게이션 등)가 발생하지 않습니다.
다음과 같이 호출하도록 수정해주세요.
- onClick = { - onCustomerServiceClick + onClick = { + onCustomerServiceClick() val intent = Intent(Intent.ACTION_VIEW, URL_CUSTOMER_SERVICE.toUri()) context.startActivity(intent) }app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt (1)
156-160: Null 안전성 버그: uiState 널 가능성 미고려로 컴파일 오류
!uiState.isLast는 uiState가 nullable이어서 컴파일 에러입니다. 아래와 같이 안전하게 검사해주세요.- if (shouldLoadMore && uiState?.isLoadingMore == false && !uiState.isLast) { + if (shouldLoadMore && (uiState?.isLoadingMore == false) && (uiState?.isLast == false)) { onLoadMore() }
🧹 Nitpick comments (32)
app/src/main/res/drawable/ic_notice_no.xml (1)
10-12: 하드코딩된 흰색 스트로크 → 테마 연동 고려strokeColor가 #FEFEFE로 고정되어 있어 테마(다크/라이트) 전환 시 일관성이 깨질 수 있습니다. 가능하면 벡터 색상을 고정하지 않고, Compose Icon에서 tint(MaterialTheme.colors...)로 제어하는 방식을 권장합니다. 이 부분은 LogoTopAppBar.kt에서의 tint 적용으로도 해소 가능합니다.
Also applies to: 15-17
app/src/main/java/com/texthip/thip/ui/common/topappbar/LogoTopAppBar.kt (2)
59-71: 접근성(i18n/터치 타겟/역할) 개선 제안
- contentDescription이 영문 하드코딩이며 상태를 반영하지 않습니다. stringResource로 지역화하고, 오른쪽 아이콘은 알림 유무에 따라 다른 문구를 노출하세요.
- 최소 48dp 터치 타겟을 보장하기 위해 minimumInteractiveComponentSize()를 추가하세요.
- 스크린리더가 버튼으로 인지하도록 role=Role.Button을 명시하세요.
- 테마 일관성을 위해 아이콘 tint를 MaterialTheme 색으로 지정하면 벡터의 하드코딩 색상 의존을 줄일 수 있습니다.
아래처럼 수정하는 것을 권장합니다.
@@ -import androidx.compose.material3.Icon +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.minimumInteractiveComponentSize import androidx.compose.runtime.Composable @@ -import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role @@ Icon( painter = leftIcon, - contentDescription = "Left Icon", - tint = Color.Unspecified, - modifier = Modifier.clickable { onLeftClick() } + contentDescription = stringResource(R.string.cd_search), + tint = MaterialTheme.colorScheme.onSurface, + modifier = Modifier + .minimumInteractiveComponentSize() + .clickable(role = Role.Button) { onLeftClick() } ) Icon( painter = rightIcon, - contentDescription = "Right Icon", - tint = Color.Unspecified, - modifier = Modifier.clickable { onRightClick() } + contentDescription = if (hasNotification) { + stringResource(R.string.cd_notifications_new) + } else { + stringResource(R.string.cd_notifications_none) + }, + tint = MaterialTheme.colorScheme.onSurface, + modifier = Modifier + .minimumInteractiveComponentSize() + .clickable(role = Role.Button) { onRightClick() } )참고: strings.xml에 다음 키를 추가해야 합니다.
- cd_search
- cd_notifications_new
- cd_notifications_none
41-41: 하드코딩된 배경색 대신 테마 사용 고려
.background(color = colors.Black)는 테마 전환/다양한 배리에이션에 취약합니다. MaterialTheme.colorScheme.surface(또는 top app bar 컨테이너 색)를 사용하는 편이 유지보수에 유리합니다.- .background(color = colors.Black) + .background(color = MaterialTheme.colorScheme.surface)app/src/main/java/com/texthip/thip/ui/common/forms/SingleDigitTextBox.kt (2)
41-49: ZWS 리터럴 하드코딩 제거로 가독성/안전성 개선 제안Zero Width Space 리터럴을 상수로 추출하면 중복/오탈자 위험을 줄이고 의도를 더 명확히 할 수 있습니다.
변경 예:
- val displayText = value.ifEmpty { "\u200B" } // Zero Width Space + val displayText = value.ifEmpty { ZWS.toString() } // Zero Width Space- val cleaned = newValue.text.replace("\u200B", "") + val cleaned = newValue.text.replace(ZWS.toString(), "") val filtered = cleaned.filter { it.isDigit() }.take(1)상수 선언(파일 상단 임의 위치, 함수 밖):
private const val ZWS: Char = '\u200B'Also applies to: 66-67
64-74: ASCII 숫자만 허용해야 하는 경우를 대비한 입력 필터 확인현재 필터는 Char.isDigit()을 사용하여 유니코드 전역의 숫자 문자를 허용합니다. 백엔드/검증 로직이 ASCII '0'..'9'만 받아야 한다면 범위를 명시하는 것이 안전합니다.
가능한 수정:
- val filtered = cleaned.filter { it.isDigit() }.take(1) + val filtered = cleaned.filter { it in '0'..'9' }.take(1)백엔드가 유니코드 숫자를 허용/정규화하는지 확인 부탁드립니다. ASCII로 제한해야 한다면 위 변경이 필요합니다.
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomUnlockScreen.kt (1)
156-159: 중복 사이즈 지정 제거 제안SingleDigitBox 내부에서 이미 .size(44.dp)를 강제하고 있습니다. 호출부에서도 .size(44.dp)를 중복 지정하면 추후 사이즈 변경 시 이중 관리가 필요합니다. 한쪽으로 정리하는 것이 유지보수에 유리합니다. 포커스 요청은 유지하세요.
- modifier = Modifier - .size(44.dp) - .focusRequester(focusRequesters[index]) + modifier = Modifier + .focusRequester(focusRequesters[index])참고: SingleDigitBox 측에서 BasicTextField에 modifier를 전달하도록 수정(제안)하면, 현재와 같은 호출부 포커스 연결이 정상 동작합니다.
app/src/main/java/com/texthip/thip/ui/search/component/SearchActiveField.kt (1)
70-76: Spacer 상단 패딩으로 리스트 간격이 이중 적용될 수 있어요현재 LazyColumn에
verticalArrangement = Arrangement.spacedBy(12.dp)가 있고, 항목 내부 구분선 Spacer에.padding(top = 12.dp)가 추가되어 카드-구분선-다음 항목 사이에 12dp + 12dp + 1dp(구분선 두께) 만큼 간격이 벌어집니다. 의도된 디자인이 아니라면 한쪽만 사용하도록 정리하는 것을 권장합니다.불필요한 이중 간격을 피하려면 다음 중 하나를 고려해주세요.
- 옵션 A: 구분선의 상단 패딩 제거(현재 spacedBy 유지)
- .padding(top = 12.dp) + // 리스트 간격은 spacedBy(12.dp)로만 관리
- 옵션 B: spacedBy를 0.dp로 바꾸고 구분선 패딩으로 간격 일원화(파일 외부 변경 예시)
// 외부 변경 예시: LazyColumn 설정 LazyColumn( state = listState, verticalArrangement = Arrangement.spacedBy(0.dp) ) { /* ... */ }app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt (1)
91-94: 탭 재선택/재진입 시 스크롤 초기화가 보장되지 않을 수 있음
LaunchedEffect(Unit)은 첫 컴포지션에서만 실행됩니다. 동일 컴포저블이 재사용되는 네비게이션 구조라면, 탭 재선택 시에는 스크롤 초기화가 동작하지 않을 수 있어요.가능한 대응:
- 상위(MyPageScreen)에서 탭 재선택 신호를
key로 내려받아LaunchedEffect(key)에 연결- 또는 navBackStackEntry/재진입 이벤트에 맞춰 트리거 전달
예시(외부 변경 예시):
// 상위에서 전달받은 trigger를 의존성으로 사용 LaunchedEffect(resetScrollTrigger) { listState.scrollToItem(0) }원하시면 현재 네비게이션 구조에 맞춘 구현 스케치를 제안드릴게요.
app/src/main/java/com/texthip/thip/ui/search/component/SearchBookFilteredResult.kt (1)
96-104: Spacer 상단 패딩 추가로 간격이 과도하게 벌어질 수 있어요이 컴포넌트도
verticalArrangement = spacedBy(12.dp)와 구분선 상단 패딩(12.dp)이 함께 적용되어, 실제 시각적 간격이 크게 늘어납니다. 의도된 디자인이 아니라면 한쪽만 유지하는 편이 낫습니다.간격을
spacedBy만으로 관리하려면:- .padding(top = 12.dp) + // 간격은 spacedBy(12.dp)로만 관리반대로 구분선 패딩만 쓸 계획이면 LazyColumn의
verticalArrangement를 0.dp로 변경하는 접근도 가능합니다(파일 외부 변경 필요).app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt (1)
55-60: 카테고리 미선택 시 태그를 전체 합집합으로 제공하도록 변경 — UX 의도 확인 필요
categories.flatMap { it.tagList }.distinct()로 변경되어, 카테고리 미선택 상태에서도 전체 태그 목록(중복 제거)이 노출됩니다. 피처 설명의 “태그 중복 선택 가능” 요구사항과 모순되지는 않는지(동일 이름 태그를 중복 선택 허용 vs. 목록 중복 제거) 기획 의도 확인 부탁드립니다. 성능 측면에서는 규모가 아주 크지 않다면 무리가 없지만, 필요 시 Sequence로 메모리 압력을 낮출 수 있습니다.원한다면 아래처럼 Sequence 기반으로 미세 최적화를 적용할 수 있습니다:
categories.asSequence().flatMap { it.tagList.asSequence() }.distinct().toList()app/src/main/java/com/texthip/thip/ui/feed/screen/MySubscriptionListScreen.kt (1)
109-129: 토스트 애니메이션/표시 시간의 체감 가독성 개선 제안현재 enter/exit 모두 2000ms이며, 표시 타이머가 2000ms라서 “완전히 정지된 상태로 보이는 시간”이 거의 없고, 총 체류 시간은 4초(2초 슬라이드 인 + 2초 슬라이드 아웃)로 길게 느껴질 수 있습니다. 일반적으로 enter/exit는 200
400ms 내외, 표시 시간은 1.52.5초가 무난합니다.아래처럼 애니메이션을 300ms로 단축하고, 표시 타이머를 유지(2000ms)하면 사용자 체감이 개선됩니다:
enter = slideInVertically( initialOffsetY = { -it }, - animationSpec = tween(durationMillis = 2000) + animationSpec = tween(durationMillis = 300) ), exit = slideOutVertically( targetOffsetY = { -it }, - animationSpec = tween(durationMillis = 2000) + animationSpec = tween(durationMillis = 300) ),추가로, 표시 타이머를 애니메이션 시간과 별도로 상수로 관리하면 스크린 간 일관성 유지가 쉽습니다.
app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageNotificationEditScreen.kt (1)
45-49: 표시 시간(3000ms)과 애니메이션 시간(각 2000ms)의 균형 조정 제안현재 설정은 2초 인 + 1초 정지 + 2초 아웃으로 총 5초 점유합니다. 인/아웃을 300ms 수준으로 줄이고 표시 시간은 2000~2500ms로 두면 체감 품질이 좋아집니다. 예:
- LaunchedEffect 지연: 2000
- enter/exit tween: 300
app/src/main/java/com/texthip/thip/ui/common/topappbar/GradationTopAppBar.kt (1)
98-107: 주석 처리된 오른쪽 메뉴 버튼 정리 권장장기간 미사용 예정이면 주석 보관 대신 제거하여 코드 가독성을 높이세요. 필요 시 git 히스토리에서 복원 가능합니다.
- /*IconButton( - onClick = onRightClick, - modifier = Modifier.align(Alignment.CenterEnd) - ) { - Icon( - painter = painterResource(R.drawable.ic_more), - contentDescription = "More Options", - tint = Color.Unspecified - ) - }*/app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (2)
195-205: Pull-to-Refresh 플로우 분리 추가 — 중복 최소화를 위한 공통화 제안
pullToRefresh()와refreshCurrentTab()이 플래그만 다르고 나머지 본체 로직이 동일합니다. 유지보수성을 위해 내부 공통 함수를 도입해 플래그 토글만 인자로 분리하는 것을 권장합니다. 기능상 문제는 없습니다.// 예시: 내부 공유 함수 private suspend fun doRefresh(toggle: (Boolean) -> Unit) { toggle(true) when (_uiState.value.selectedTabIndex) { 0 -> refreshAllFeeds() 1 -> refreshMyFeeds() } toggle(false) }
281-296: 초기화 시 보조 상태도 함께 리셋 고려탭/리스트/커서 초기화는 좋습니다. 추가로 아래도 함께 리셋하면 예외 상황에서 더 예측 가능한 초기 상태를 보장할 수 있습니다:
- 오류 메시지 초기화(
error = null)- (선택) 마이 피드 정보 초기화(
myFeedInfo = null) — 필요 시 첫 진입 시점에 재조회updateState { it.copy( selectedTabIndex = 0, allFeeds = emptyList(), myFeeds = emptyList(), isLastPageAllFeeds = false, - isLastPageMyFeeds = false + isLastPageMyFeeds = false, + error = null, + myFeedInfo = null ) }app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt (1)
307-312: 칩 좌측 정렬 전환은 👍. 다만 spacing을 Modifier.width로 넘기는 API는 가독성이 떨어집니다.현재 GenreChipRow에 spacing을 전달하기 위해
modifier = Modifier.width(8.dp)를 넘기고 있는데, 이modifier가 Row의 레이아웃이 아니라 칩 사이 간격으로만 쓰여 의미가 모호합니다. 차후chipSpacing: Dp와rowModifier: Modifier를 분리하는 형태로 API를 정리하면 호출부 가독성과 재사용성이 좋아집니다. 자세한 제안은 GenreChipRow.kt 코멘트에 남겼습니다.app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt (1)
149-154: 칩 좌측 정렬 + spacing(12.dp) 적용은 의도에 부합합니다. 다만 magic number 정리 제안여기서는 12.dp, FeedWriteScreen은 8.dp로 화면마다 간격 값이 달라졌습니다. 디자인 의도라면 OK지만, 일관성이 필요한 경우 Dimen(예: ThipTheme.spacing 또는 dimens.xml)로 추출해 공용 상수로 쓰면 유지보수에 유리합니다.
app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt (1)
21-27: API 명확성 향상 제안: spacing과 Row modifier를 분리하고 spacedBy 사용 고려현재
modifier파라미터가 칩 간격(Spacer) 용도로만 사용되고, Row에는 고정Modifier.fillMaxWidth()가 쓰여 호출부에서 Row 자체의 modifier를 줄 수 없습니다. 다음과 같이 정리하면 의미가 명확해지고 재사용성이 좋아집니다.
rowModifier: Modifier = ModifierchipSpacing: Dp = 4.dphorizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally- Row는
horizontalArrangement = Arrangement.spacedBy(chipSpacing, alignment = horizontalAlignment)사용- 칩 사이 Spacer 제거
예시:
@Composable fun GenreChipRow( rowModifier: Modifier = Modifier, genres: List<String>, selectedIndex: Int, onSelect: (Int) -> Unit, chipSpacing: Dp = 4.dp, horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, ) { Row( modifier = rowModifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(chipSpacing, alignment = horizontalAlignment) ) { genres.forEachIndexed { idx, genre -> OptionChipButton( modifier = Modifier.clip(RoundedCornerShape(20.dp)), text = genre, isFilled = true, isSelected = selectedIndex == idx, onClick = { if (selectedIndex == idx) onSelect(-1) else onSelect(idx) } ) } } }현 PR 범위를 넘는 변경이므로 참고용으로 남깁니다.
app/src/main/java/com/texthip/thip/ui/common/buttons/SubGenreChipRow.kt (1)
27-35: 선택 여부 계산의 비용을 낮출 수 있습니다
selectedGenres.contains(genre)는 O(n)입니다. 태그가 많아질 경우를 고려해 Set 기반으로 계산하거나,remember(selectedGenres)로 Set 캐싱을 고려해 보세요.예:
val selectedSet = remember(selectedGenres) { selectedGenres.toHashSet() } // ... isSelected = selectedSet.contains(genre)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (4)
75-78: 도서 상세 네비게이션 콜백 추가 (LGTM, 가드 권장)추가된
onNavigateToBookDetail은 사용성 개선에 유효합니다. ISBN이 빈 문자열일 가능성에 대비한 가드를 권장합니다.다음과 같이 클릭 시 ISBN 유효성을 확인하도록 제안합니다:
- onClick = { - onNavigateToBookDetail(feedDetail.isbn) - } + onClick = { + val isbn = feedDetail.isbn + if (isbn.isNotBlank()) { + onNavigateToBookDetail(isbn) + } + // else: 필요 시 토스트 노출 등 처리 + }
197-200: ActionBookButton 클릭 처리의 안정성 개선현재 즉시 ISBN으로 이동합니다. 빈/잘못된 ISBN 방지 가드를 위 코멘트의 diff와 같이 반영하면 안전합니다.
350-364: 토스트 애니메이션이 지나치게 길어 UX 저하 가능
enter/exit가 각각 2000ms는 토스트 체류(3s) 대비 과도합니다. 250~400ms 범위로 축소를 권장합니다.다음과 같이 조정:
- enter = slideInVertically( - initialOffsetY = { -it }, - animationSpec = tween(durationMillis = 2000) - ), + enter = slideInVertically( + initialOffsetY = { -it }, + animationSpec = tween(durationMillis = 300) + ), - exit = slideOutVertically( - targetOffsetY = { -it }, - animationSpec = tween(durationMillis = 2000) - ), + exit = slideOutVertically( + targetOffsetY = { -it }, + animationSpec = tween(durationMillis = 300) + ),
365-368: 하드코딩된 문자열을 stringResource로 이동국제화와 일관성을 위해 문자열 리소스를 사용하세요.
- ToastWithDate( - message = "게시글 신고를 완료했어요." - ) + ToastWithDate( + message = stringResource(R.string.feed_report_complete) + )별도의 리소스 추가가 필요합니다:
res/values/strings.xml에feed_report_complete.원하시면 strings.xml 패치도 제안드립니다.
app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt (2)
98-103: 진입 시 스크롤 최상단 이동 (LGTM)탭 전환/네비게이션 요구사항에 부합합니다. 필요 시 onTabSelected 이벤트에 묶는 것도 고려 가능합니다.
177-195: 토스트 애니메이션 지속시간 단축 제안여기서도 2000ms는 과도합니다. 앱 전반의 토스트 애니메이션 스펙을 300ms 전후로 통일하는 것을 권장합니다.
- enter = slideInVertically( - initialOffsetY = { -it }, - animationSpec = tween(durationMillis = 2000) - ), + enter = slideInVertically( + initialOffsetY = { -it }, + animationSpec = tween(durationMillis = 300) + ), - exit = slideOutVertically( - targetOffsetY = { -it }, - animationSpec = tween(durationMillis = 2000) - ), + exit = slideOutVertically( + targetOffsetY = { -it }, + animationSpec = tween(durationMillis = 300) + ),app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomRecruitScreen.kt (1)
465-483: 토스트 애니메이션 지속시간 단축 및 전역 일관화 제안다른 화면과 동일하게 300ms 전후로 축소 권장. 현재는 보이는 시간(3s) 대비 전환(2s)이 너무 깁니다.
- enter = slideInVertically( - initialOffsetY = { -it }, - animationSpec = tween(durationMillis = 2000) - ), + enter = slideInVertically( + initialOffsetY = { -it }, + animationSpec = tween(durationMillis = 300) + ), - exit = slideOutVertically( - targetOffsetY = { -it }, - animationSpec = tween(durationMillis = 2000) - ), + exit = slideOutVertically( + targetOffsetY = { -it }, + animationSpec = tween(durationMillis = 300) + ),app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (1)
108-121: Feed 작성 진입 시 책 정보 사전선택: 의존성 경계 재검토
com.texthip.thip.ui.group.makeroom.mock.BookData를 Feed 작성 VM에 직접 전달하는 것은 레이어 침식과 순환 의존 가능성을 키웁니다. 공용 모델 모듈로 추출하거나, Feed 도메인 전용 DTO를 정의하고 VM에서 변환하도록 하는 편이 바람직합니다.제안:
:core:model혹은:ui:common:model등 공용 모듈에BookData(prod용) 정의mock네임스페이스 사용은 테스트/프리뷰에서만 한정FeedWriteViewModel.selectBook()입력 타입을 공용 모델로 교체app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomRecruitingResponse.kt (2)
23-23: bookImageUrl의 null 허용 여부 확인 필요 (런타임 디코딩 실패 가능성)kotlinx.serialization 사용 시 서버가 null을 보내거나 필드를 누락하면 현재 비-null(String) 선언은 예외를 유발할 수 있습니다. 백엔드 계약이 "항상 값 제공"으로 보장되지 않는다면 nullable로 맞추는 편이 안전합니다. 추천 카드(RecommendRoomResponse)는
String?으로 선언되어 있어 상위 모델과 일관성도 맞출 수 있습니다.권장 수정안:
- @SerialName("bookImageUrl") val bookImageUrl: String, + @SerialName("bookImageUrl") val bookImageUrl: String?,서버에서 항상 값이 온다면 그대로 두셔도 되지만, 그 경우 스키마 보장(문서/스웨거) 링크 공유 부탁드립니다. 보장이 없다면 위와 같이 변경 후 UI 레벨에서 placeholder 처리하는 것이 안전합니다.
12-12: 요약과 불일치: roomImageUrl → bookImageUrl 일괄 변경 아님파일 내에는 roomImageUrl(String?)와 bookImageUrl(상위는 현재 String, 추천은 String?)가 동시에 존재합니다. AI 요약은 상위 필드가 bookImageUrl로 대체되었다고 기재하나, 실제 코드는 두 필드가 공존합니다. 의도된 모델 설계(방 이미지 vs 책 표지)인지 확인 부탁드립니다. 의도된 분리라면 주석으로 용도를 명확히 하면 좋습니다.
Also applies to: 23-23, 34-34
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (2)
120-122: 댓글과 다른 동작: “부드럽게” 스크롤을 원하면 animateScrollToItem 사용 권장주석은 “부드럽게 이동”이라 되어 있으나 실제로는 즉시 이동(scrollToItem)입니다. 애니메이션 이동으로 맞추는 것을 권장합니다.
- currentListState.scrollToItem(0) + currentListState.animateScrollToItem(0)
169-171: 중앙 로딩 오버레이와 PullToRefresh 동시 노출 가능성 점검
- PullToRefreshBox는 isRefreshing = isPullToRefreshing
- 화면 중앙 오버레이는 isRefreshing 플래그로 제어
두 플래그가 동시에 true가 될 수 있다면 이중 로딩 노출이 발생할 수 있습니다. 상태 정의(탭 전환 리프레시 vs 당김새로고침)가 상호 배타적이면, 하나의 소스로 파생하거나 우선순위를 정하는 게 안전합니다. 예: overlay는 “탭 전환/외부 갱신” 전용, PullToRefresh는 “사용자 제스처” 전용 등.
Also applies to: 416-426
app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt (1)
66-77: 불필요한 공개 파라미터 제거 제안: onRightClick 미사용상위 Composable 서명에는
onRightClick이 있으나, 내부에서 사용하지 않으며 Content에도 전달하지 않습니다. API 혼란을 줄이기 위해 제거를 권장합니다. 호출부도 함께 정리 필요합니다.fun SearchBookDetailScreen( modifier: Modifier = Modifier, isbn: String, onLeftClick: () -> Unit = {}, - onRightClick: () -> Unit = {}, onRecruitingGroupClick: () -> Unit = {}, onWriteFeedClick: (BookDetailResponse) -> Unit = {}, onFeedClick: (Int) -> Unit = {}, onBookmarkClick: (String, Boolean) -> Unit = { _, _ -> }, viewModel: BookDetailViewModel = hiltViewModel() )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (33)
app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomRecruitingResponse.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/common/bottomsheet/CustomBottomSheet.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBookButton.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/common/buttons/SubGenreChipRow.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/common/forms/SingleDigitTextBox.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/common/topappbar/GradationTopAppBar.kt(5 hunks)app/src/main/java/com/texthip/thip/ui/common/topappbar/LogoTopAppBar.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt(8 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/MySubscriptionListScreen.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/group/makeroom/screen/GroupMakeRoomScreen.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomRecruitScreen.kt(8 hunks)app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomUnlockScreen.kt(5 hunks)app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/group/viewmodel/GroupViewModel.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/mypage/component/BookContent.kt(0 hunks)app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageNotificationEditScreen.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt(6 hunks)app/src/main/java/com/texthip/thip/ui/navigator/navigations/SearchNavigation.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/search/component/SearchActiveField.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/search/component/SearchBookFilteredResult.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt(8 hunks)app/src/main/res/drawable/ic_notice_no.xml(1 hunks)app/src/main/res/values/strings.xml(1 hunks)
💤 Files with no reviewable changes (1)
- app/src/main/java/com/texthip/thip/ui/mypage/component/BookContent.kt
🧰 Additional context used
🧬 Code Graph Analysis (11)
app/src/main/java/com/texthip/thip/ui/group/viewmodel/GroupViewModel.kt (3)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (1)
updateState(66-68)app/src/main/java/com/texthip/thip/ui/group/makeroom/viewmodel/GroupMakeRoomViewModel.kt (1)
updateState(41-43)app/src/main/java/com/texthip/thip/ui/group/myroom/viewmodel/GroupMyViewModel.kt (1)
updateState(26-28)
app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt (1)
app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt (1)
GroupDatePicker(29-127)
app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/common/modal/ToastWithDate.kt (1)
ToastWithDate(24-61)
app/src/main/java/com/texthip/thip/ui/feed/screen/MySubscriptionListScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/common/modal/ToastWithDate.kt (1)
ToastWithDate(24-61)
app/src/main/java/com/texthip/thip/ui/common/topappbar/GradationTopAppBar.kt (1)
app/src/main/java/com/texthip/thip/ui/common/view/CountingBar.kt (1)
CountingBar(19-38)
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/common/modal/ToastWithDate.kt (1)
ToastWithDate(24-61)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/common/modal/ToastWithDate.kt (1)
ToastWithDate(24-61)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (3)
app/src/main/java/com/texthip/thip/ui/group/viewmodel/GroupViewModel.kt (1)
updateState(33-35)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (1)
updateState(34-36)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedDetailViewModel.kt (1)
updateState(28-30)
app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (1)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (1)
FeedScreen(63-428)
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomRecruitScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/common/modal/ToastWithDate.kt (1)
ToastWithDate(24-61)
app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/common/topappbar/GradationTopAppBar.kt (1)
GradationTopAppBar(31-109)
🔇 Additional comments (41)
app/src/main/res/drawable/ic_notice_no.xml (2)
1-19: 알림 비활성 아이콘 추가 LGTM새 아이콘 리소스가 TopAppBar 상태(hasNotification=false)와 일관되게 연결되도록 잘 추가되었습니다.
2-5: 아이콘 아트보드가 18x20dp라 터치 타겟이 작아질 수 있음현재 벡터의 실제 크기가 18x20dp라 Icon 자체에 별도 사이즈/최소 인터랙션 크기를 부여하지 않으면 접근성 가이드(48x48dp)를 만족하지 못할 가능성이 높습니다. Compose 쪽에서 최소 터치 타겟을 보장하는 처리가 있는지 확인 부탁드립니다.
app/src/main/java/com/texthip/thip/ui/common/topappbar/LogoTopAppBar.kt (1)
35-35: hasNotification=false 시 아이콘 교체 로직 적절알림 상태에 따라 ic_notice_yes/ic_notice_no를 분기하는 구현이 명확합니다. 신규 리소스와도 일관됩니다.
app/src/main/java/com/texthip/thip/ui/common/forms/SingleDigitTextBox.kt (1)
77-79: 빈 값일 때 투명 텍스트 처리 적절값이 비어 있을 때 텍스트를 투명 처리하여 ZWS 앵커가 보이지 않도록 한 점이 의도에 부합합니다. 커서는 cursorBrush로 표시되므로 가독성에도 문제 없습니다.
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomUnlockScreen.kt (3)
135-145: 입력/삭제에 따른 포커스 이동 로직(wasEmpty) 적절
- 입력 시 다음 칸으로, 삭제 시 이전 칸으로 이동하는 분기(wasEmpty) 로직이 깔끔하며 경계(index) 처리도 안전합니다.
149-153: 백스페이스 동작 분리 적절빈 박스에서의 백스페이스를 onBackspace로 처리해 이전 칸으로만 이동하도록 한 분리 전략이 예측 가능하고 UX 친화적입니다.
61-81: 비밀번호 검증 흐름의 상태 처리 적절passwordMatched true/false/null 분기와 실패 시 리셋/포커스 이동, 성공 시 키보드 숨김 및 콜백 호출 타이밍이 안정적입니다.
app/src/main/java/com/texthip/thip/ui/common/bottomsheet/CustomBottomSheet.kt (1)
97-99: IME(키보드) 대응 패딩 적용 적절
advancedImePadding()을 BottomSheet 컨테이너에 적용해 입력기 등장 시 내용이 가려지지 않도록 한 방향성 좋습니다. 드래그/애니메이션 로직과도 충돌 없어 보입니다.app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageScreen.kt (1)
112-115: 스크롤 상태 바인딩 LGTM
rememberLazyListState를state에 연결해 프로그램적 스크롤 제어 및 상태 보존을 가능하게 한 점 좋습니다.app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt (2)
73-77: 년(Year) 라벨 여백 추가는 가독성 측면에서 적절합니다픽커-라벨 간 시각적 간격이 개선됩니다. 동일 패턴을 월/일 라벨에도 일관되게 적용한 점 좋습니다.
99-103: 월(Month) 라벨 여백 조정 LGTM컨트롤-라벨 간 간격이 개선되어 터치 타깃 인접도와 가독성이 좋아집니다.
app/src/main/java/com/texthip/thip/ui/mypage/screen/MypageNotificationEditScreen.kt (1)
52-66: AnimatedVisibility로 토스트 컨테이너를 감싼 구조는 적절합니다상단 정렬, 패딩, zIndex를 컨테이너에 부여해 레이아웃 책임을 명확히 분리한 점 좋습니다. 다른 화면들과의 일관성도 확보됩니다.
app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupRoomDurationPicker.kt (4)
36-42: “내일부터 선택 가능” 요구사항 충족 확인기준일을
tomorrow로 전환하고 초기값도 내일/모레로 설정한 변경이 명세와 일치합니다.
47-49: 초기화 로직의 단발성 보장 처리 적절
isInitialized로 첫 진입 시에만 내일 기준으로 초기화하는 패턴은 재구성/상태 복원 시에도 안정적입니다.
111-112: minDate를 내일로 제한한 변경 LGTM양쪽 DatePicker에 동일 제약을 적용해 UX/검증 일관성이 확보됩니다.
Also applies to: 136-137
130-131: 구분자 패딩 축소는 밀도 높은 레이아웃에 유리합니다시각적 간격이 과도하지 않게 정리됩니다.
app/src/main/java/com/texthip/thip/ui/common/topappbar/GradationTopAppBar.kt (1)
42-60: 자동 숨김 동작 로직 적절 — 취소 안전성 확보되어 LGTM
LaunchedEffect(autoHideCount, count)로 코루틴이 키 변화 시 취소/재시작되며,autoHideCount3-state 분기와count > 0가드도 합리적입니다. 퍼블릭 API 확장도 기본값으로 역호환 유지됩니다.app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (2)
75-85: 탭 전환 시 강제 새로고침으로 UX 일관성 확보 — 좋습니다탭 전환마다
refreshCurrentTab()을 호출해 상태를 최신화하는 전략이 명확하고, My 탭에서myFeedInfo는 필요 시에만 가져오도록 가드한 것도 효율적입니다.
276-279: refreshData()가 기존 ‘탭 인디케이터’와 분리됨 — 호출부 의도 확인 권장이 경로는
isRefreshing/isPullToRefreshing을 토글하지 않으므로, UI 인디케이터 없이 데이터만 갱신됩니다. 화면 진입용 초기화 용도로만 사용된다면 OK이고, 사용자 트리거 새로고침에 연결된다면 인디케이터가 필요할 수 있습니다. 호출부에서 의도한 용도인지 확인 부탁드립니다.app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt (1)
328-328: 카테고리 전환 시 태그 유지 정책 — 서버 유효성 확인 필요이제 카테고리 변경 시
selectedTags를 초기화하지 않으므로, 선택된 태그가 새 카테고리에 속하지 않을 수 있습니다. 서버가 “카테고리-태그 불일치”를 허용/무시/검증 실패 중 어떤 동작을 하는지 확인 바랍니다. 허용되지 않는 경우:
- 전환 시 새 카테고리에 없는 태그만 필터링 제거, 또는
- 저장 전 유효성 검사로 경고/자동 수정
원하시면 저장 시 간단한 유효성 검사를 추가하는 패치를 제안드리겠습니다.
app/src/main/java/com/texthip/thip/ui/group/viewmodel/GroupViewModel.kt (1)
209-222: 초기화 엔트리포인트 추가 LGTM장르 인덱스 초기화와 주요 섹션 클리어 후
refreshGroupData()호출 흐름이 명확합니다.GroupScreen에서의 진입 초기화 요구사항과 잘 맞습니다.app/src/main/java/com/texthip/thip/ui/navigator/navigations/SearchNavigation.kt (2)
10-10: 피드 작성 네비게이션 import 추가 OK필요한 확장 함수를 명시적으로 가져오는 방향 좋아요.
47-53: navigateToFeedWrite 네비게이션 null 허용 일치 확인됨
- FeedNavigationExtensions.kt의 navigateToFeedWrite 확장 함수 파라미터는 모두 String? = null로 선언되어 있어
- BookDetailResponse.imageUrl도 String? = null로 정의되어 있습니다.
- FeedRoutes.Write 데이터 클래스 역시 isbn, bookTitle, bookAuthor, bookImageUrl 모두 nullable로 선언되어 있기 때문에
bookDetail.imageUrl가 null인 경우에도 안전하게 호출됩니다.
라우트 인코딩 시 null 파라미터는 쿼리스트링에 포함되지 않거나 빈값 처리되는데, 기본 NavType.StringType 설정에서 문제없습니다.
필요하다면 NavGraph에서 FeedRoutes.Write를 사용하는 부분의 NavType null 허용 여부만 간단히 확인해 주세요.app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBookButton.kt (1)
11-11: widthIn import 사용 OK가로 폭 제한으로 레이아웃 파손을 방지하려는 의도 명확합니다.
app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt (1)
21-27: 수평 정렬을 외부에서 제어 가능하게 한 변경, 잘 반영됐습니다.
horizontalArrangement를 public 파라미터로 열어 화면 요구사항에 맞춘 정렬 제어가 쉬워졌습니다. 기본값 유지로 기존 호출부 호환성도 보장됩니다.app/src/main/java/com/texthip/thip/ui/common/buttons/SubGenreChipRow.kt (1)
19-21: 칩 정렬의 유연성 확보, 기본 동작 유지됨 (LGTM)
horizontalAlignment파라미터 추가와Arrangement.spacedBy(…, alignment)적용으로 정렬 제어가 가능해졌고, 기본값이 CenterHorizontally라 기존 동작도 보존됩니다.Also applies to: 24-26
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt (1)
3-6: 애니메이션 의존성 추가 (LGTM)
AnimatedVisibility및 슬라이드 인/아웃 도입을 위한 import 추가가 적절합니다.app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt (3)
3-6: 애니메이션 의존성 추가 (LGTM)토스트 전환 일관화를 위한 import 추가가 적절합니다.
61-62: 초기화 동작 변경: resetToInitialState 호출 검증 권장진입 시
resetToInitialState()로 초기화하도록 변경되었습니다. 사용자 선택(장르, 스크롤, 캐시된 데이터 등)을 모두 초기화하는 설계가 의도인지 확인 필요합니다. 기존의 “데이터 새로고침”과 의미가 달라 회귀 가능성이 있습니다.필요 시, 초기화 범위를 제한하거나 조건부 초기화로 조정을 도와드릴 수 있습니다.
192-195: 토스트 메시지 바인딩 (LGTM)
uiState.toastMessage바인딩으로 i18n/동적 메시지 모두 대응 가능합니다.app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomRecruitScreen.kt (4)
3-6: 애니메이션 의존성 추가 (LGTM)토스트 전환 일관성을 위한 import 추가가 적절합니다.
346-349: 모집 마감 표기 변경 (LGTM)
replace("뒤","남음")제거하고 서버 제공 포맷을 그대로 노출하는 변경은 일관성을 높입니다. 서버 포맷 변경 시 UI 영향 범위를 파악하기 쉽습니다.
406-407: 이미지 소스 변경 (roomImageUrl → bookImageUrl) 적용 (LGTM)추천 모임 카드의 이미지 필드 변경이 데이터 모델 리네임과 일치합니다. 관련 모델/매핑 파일과의 타입 일관성 확인만 부탁드립니다.
543-565: 프리뷰 데이터 bookImageUrl 전환 (LGTM)프리뷰도 실제 모델 변경과 동기화되어 있습니다.
app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt (4)
15-16: 알림 네비게이션 확장 (LGTM)
navigateToAlarm()연결이 깔끔합니다.
28-39: 피드 새로고침 플래그 저장/소비 흐름 (LGTM)
savedStateHandle로refreshFeed를 전달하고 소비 후 제거하는 패턴이 명확합니다.
131-133: 작성 완료 후 피드 목록 갱신 트리거 (LGTM)
feedId와 함께refreshFeed플래그를 설정하는 방식이 Feed 화면에서의 안전한 리프레시를 보장합니다.
161-163: 댓글 화면에서 도서 상세 네비게이션 연결 (LGTM)
onNavigateToBookDetail경로 연결이 완결됐습니다.app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (2)
85-92: 탭별 LazyListState 분리 도입 좋습니다탭 전환 시 개별 스크롤 상태를 유지/복원할 수 있어 UX가 개선됩니다. 이후 페이지네이션 상태도 탭 단위로 분리되어 있다면 현재 구조와 잘 맞습니다.
175-181: LogoTopAppBar의 hasNotification=false 설정 시 우측 아이콘 노출 여부 확인onRightClick은
onNavigateToNotification으로 연결했지만 hasNotification=false이면 우측 아이콘이 숨겨질 수 있습니다. 컴포넌트 구현에 따라 우측 아이콘 자체가 사라지면 클릭 경로가 없어집니다. 의도한 UX(우측 알림 버튼 제공)라면 hasNotification 플래그/파라미터 사용법을 한 번 더 확인해주세요.app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt (1)
132-134: 상단 이미지 영역 개선, 그라데이션/TopAppBar 동작 👍
- 화면 크기 기반 최대 높이 적용으로 태블릿 대응이 좋아졌습니다.
- 그라데이션 농도 조정으로 텍스트 가독성 향상됩니다.
- GradationTopAppBar의 autoHideCount=true 적용으로 읽는 사람 수 배지가 자동으로 사라지는 UX가 자연스럽습니다.
Also applies to: 224-225, 236-241, 503-510
| // 저자명과 "저"를 분리 | ||
| Text( | ||
| text = bookAuthor + stringResource(R.string.author), | ||
| text = bookAuthor, | ||
| style = typography.info_r400_s12_h24, | ||
| color = colors.Grey, | ||
| modifier = Modifier.width(100.dp), | ||
| modifier = Modifier | ||
| .widthIn(max = 80.dp) | ||
| .padding(start = 8.dp), | ||
| textAlign = TextAlign.Right, | ||
| overflow = TextOverflow.Ellipsis, | ||
| maxLines = 1 | ||
| ) | ||
|
|
||
| Text( | ||
| text = stringResource(R.string.author), | ||
| style = typography.info_r400_s12_h24, | ||
| color = colors.Grey, | ||
| ) |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
저자명과 ‘저’를 분리한 건 👍. 다음 두 가지를 개선해 주세요 (현지화/접근성 친화).
- RTL 안전 정렬:
TextAlign.Right대신TextAlign.End를 사용해 레이아웃 방향을 따른 정렬을 권장합니다. - 공백/따옴표를 문자열 리소스에 넣지 않기: 문자열에 선행 공백이나 따옴표를 포함시키면 현지화, 접근성, 줄바꿈 제어가 어려워집니다. UI에서
padding(start = …)로 간격을 주는 방식으로 정리하는 것을 권장합니다.
아래 diff는 컴포넌트 내에서 가능한 최소 변경입니다(문자열 리소스의 선행 공백 제거가 전제되면 시각적 일관성이 좋아집니다).
- textAlign = TextAlign.Right,
+ textAlign = TextAlign.End,
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
- Text(
+ Text(
text = stringResource(R.string.author),
style = typography.info_r400_s12_h24,
- color = colors.Grey,
+ color = colors.Grey,
+ modifier = Modifier.padding(start = 4.dp),
)추가로, widthIn(max = 80.dp)는 폰트 스케일이 큰 환경에서 너무 촘촘할 수 있습니다. 필요시 96dp 정도로 여유를 주거나, 상황에 따라 조건부로 완화하는 것도 고려해 볼 수 있습니다.
문자열 리소스에 선행 공백이나 따옴표가 포함되어 있는지 확인해 주세요. 포함되어 있다면 제거하는 것을 권장합니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "author 문자열 리소스 내용 확인"
fd strings.xml | xargs -I{} rg -n '<string name="author">.*</string>' {}Length of output: 192
RTL 정렬 및 문자열 리소스 정리 필수 반영
- ActionBookButton.kt (55–72줄)
•TextAlign.Right→TextAlign.End(RTL 안전 정렬)
• 두 번째Text에Modifier.padding(start = 4.dp)추가 (문자열 리소스에 공백 제거)
• (선택) 첫 번째Text의widthIn(max = 80.dp)를96.dp로 여유 부여 고려 - strings.xml
•<string name="author">" 저"</string>→<string name="author">저</string>
--- a/app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBookButton.kt
@@ -55,8 +55,9 @@
Text(
text = bookAuthor,
style = typography.info_r400_s12_h24,
color = colors.Grey,
modifier = Modifier
.widthIn(max = 80.dp)
.padding(start = 8.dp),
- textAlign = TextAlign.Right,
+ textAlign = TextAlign.End,
overflow = TextOverflow.Ellipsis,
maxLines = 1
)
@@ -64,6 +65,7 @@
Text(
text = stringResource(R.string.author),
style = typography.info_r400_s12_h24,
color = colors.Grey,
+ modifier = Modifier.padding(start = 4.dp),
)--- a/app/src/main/res/values/strings.xml
@@ -27,7 +27,7 @@
<!-- 저자 표시 -->
- <string name="author">" 저"</string>
+ <string name="author">저</string>🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/common/buttons/ActionBookButton.kt
around lines 55 to 72, replace TextAlign.Right with TextAlign.End for RTL-safe
alignment, add Modifier.padding(start = 4.dp) to the second Text (the author
label) to restore spacing removed from the string resource, and optionally
increase the first Text's widthIn(max = 80.dp) to widthIn(max = 96.dp) if extra
space is desired; also update strings.xml to change <string name="author">"
저"</string> to <string name="author">저</string> (remove quotes and leading
space).
| BasicTextField( | ||
| value = value, | ||
| onValueChange = { input -> | ||
| val filtered = input.filter { it.isDigit() }.take(1) | ||
| value = textFieldValue, | ||
| onValueChange = { newValue -> | ||
| val cleaned = newValue.text.replace("\u200B", "") |
There was a problem hiding this comment.
포커스 이동이 동작하지 않음/크래시 가능성: FocusRequester가 BasicTextField에 연결되지 않았습니다
현재 FocusRequester는 호출 측에서 SingleDigitBox의 modifier에 붙고, 내부에서는 그 modifier를 Box에만 적용합니다. Box는 포커스 타겟이 아니므로 requestFocus() 시 IllegalStateException이 발생하거나 포커스가 무시될 수 있습니다. BasicTextField에 modifier를 전달해 FocusRequester가 실제 포커스 타겟에 연결되도록 해야 합니다.
다음 최소 수정으로 해결 가능합니다(공개 API 변경 없음):
BasicTextField(
+ modifier = modifier,
value = textFieldValue,
onValueChange = { newValue ->📝 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.
| BasicTextField( | |
| value = value, | |
| onValueChange = { input -> | |
| val filtered = input.filter { it.isDigit() }.take(1) | |
| value = textFieldValue, | |
| onValueChange = { newValue -> | |
| val cleaned = newValue.text.replace("\u200B", "") | |
| BasicTextField( | |
| modifier = modifier, | |
| value = textFieldValue, | |
| onValueChange = { newValue -> | |
| val cleaned = newValue.text.replace("\u200B", "") |
| onGenreToggle = onToggleTag, | ||
| horizontalAlignment = Alignment.Start | ||
| ) |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
서브 장르 그리드 좌측 정렬 반영 OK
horizontalAlignment = Alignment.Start 적용으로 시각적 일관성이 좋아졌습니다. 해당 컴포저블 시그니처가 업데이트되었는지만 확인하면 됩니다.
다음 스크립트로 SubGenreChipGrid 시그니처에 horizontalAlignment 파라미터가 존재하는지 확인할 수 있습니다:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "SubGenreChipGrid 선언부 검색"
rg -nP --type=kt -C2 'fun\s+SubGenreChipGrid\s*\('
echo
echo "horizontalAlignment 파라미터 포함 여부 확인"
rg -nP --type=kt -C1 'fun\s+SubGenreChipGrid\s*\([^)]*horizontalAlignment'Length of output: 156
아래와 같이 --type=kt 대신 -g '*.kt'를 사용해 Kotlin 파일 전체에서 검색해 보세요. SubGenreChipGrid 선언부와 horizontalAlignment 파라미터 포함 여부를 다시 확인할 수 있습니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "SubGenreChipGrid 선언부 검색"
rg -nP -g '*.kt' -C2 'fun\s+SubGenreChipGrid\s*\('
echo
echo "horizontalAlignment 파라미터 포함 여부 확인"
rg -nP -g '*.kt' -C1 'fun\s+SubGenreChipGrid\s*\([^)]*horizontalAlignment'Length of output: 764
SubGenreChipGrid 시그니처에 horizontalAlignment 파라미터 추가 필요
SubGenreChipGrid 선언부(app/src/main/java/com/texthip/thip/ui/common/buttons/SubGenreChipRow.kt:16)에 horizontalAlignment 파라미터가 없어, 현재 사용 중인 horizontalAlignment = Alignment.Start가 컴파일 에러를 발생시킵니다. 해당 컴포저블 시그니처를 다음처럼 수정해주세요.
- 파일:
app/src/main/java/com/texthip/thip/ui/common/buttons/SubGenreChipRow.kt - 위치:
fun SubGenreChipGrid( 선언부
제안 수정 예시:
@Composable
fun SubGenreChipGrid(
subGenres: List<String>,
selectedGenres: List<String>,
onGenreToggle: (String) -> Unit,
+ horizontalAlignment: Alignment.Horizontal = Alignment.Start
) {
// …
}🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt around
lines 320-322, the call passes horizontalAlignment = Alignment.Start but
SubGenreChipGrid does not accept that parameter; open
app/src/main/java/com/texthip/thip/ui/common/buttons/SubGenreChipRow.kt at the
SubGenreChipGrid declaration and add a horizontalAlignment parameter (type
Alignment.Horizontal, provide a sensible default such as Alignment.Start) to the
function signature, then rebuild and ensure all other call sites compile (adjust
or pass the parameter where needed).
| val isRefreshing: Boolean = false, // 탭 전환용 로딩 | ||
| val isPullToRefreshing: Boolean = false, // Pull to refresh용 로딩 | ||
| val isLoadingMore: Boolean = false, |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Pull-to-Refresh 중 무한 스크롤 방지 가드 추가 필요
canLoadMoreAllFeeds/canLoadMoreMyFeeds가 isPullToRefreshing을 고려하지 않아 당기는 새로고침 중에도 추가 페이지 로드가 트리거될 수 있습니다. 아래처럼 가드를 추가해 경쟁 호출을 예방하세요. 이 변경만으로 loadMoreFeeds()의 조기 반환 조건도 자연스럽게 보강됩니다.
- val canLoadMoreAllFeeds: Boolean get() = !isLoading && !isLoadingMore && !isRefreshing && !isLastPageAllFeeds
- val canLoadMoreMyFeeds: Boolean get() = !isLoading && !isLoadingMore && !isRefreshing && !isLastPageMyFeeds
+ val canLoadMoreAllFeeds: Boolean
+ get() = !isLoading && !isLoadingMore && !isRefreshing && !isPullToRefreshing && !isLastPageAllFeeds
+ val canLoadMoreMyFeeds: Boolean
+ get() = !isLoading && !isLoadingMore && !isRefreshing && !isPullToRefreshing && !isLastPageMyFeeds🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt around
lines 24 to 26, the feed-loading guards don't consider pull-to-refresh so
load-more can be triggered during a pull-to-refresh; update the
canLoadMoreAllFeeds and canLoadMoreMyFeeds checks to return false when
isPullToRefreshing is true (and optionally when isRefreshing is true) so that
loadMoreFeeds cannot start while a pull-to-refresh is active, which will also
strengthen loadMoreFeeds' early-return behavior.
| Row( | ||
| modifier = modifier.fillMaxWidth(), | ||
| verticalAlignment = Alignment.CenterVertically, | ||
| horizontalArrangement = Arrangement.SpaceBetween | ||
| verticalAlignment = Alignment.CenterVertically | ||
| ) { |
There was a problem hiding this comment.
Row 루트에 전달된 modifier가 반영되지 않아 상위에서 주는 weight 등이 무시됩니다
현재 Row에 modifier 파라미터가 적용되지 않아, 상위(예: GroupRoomDurationPicker)에서 modifier = Modifier.weight(1f)로 폭을 제어하려는 시도가 전부 무시됩니다. 이로 인해 두 DatePicker를 균등 분할하려는 레이아웃 의도가 깨집니다. Row 루트에 modifier를 전달해 주세요.
다음 패치를 제안합니다:
Row(
- verticalAlignment = Alignment.CenterVertically
+ modifier = modifier,
+ verticalAlignment = Alignment.CenterVertically
) {📝 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.
| Row( | |
| modifier = modifier.fillMaxWidth(), | |
| verticalAlignment = Alignment.CenterVertically, | |
| horizontalArrangement = Arrangement.SpaceBetween | |
| verticalAlignment = Alignment.CenterVertically | |
| ) { | |
| Row( | |
| modifier = modifier, | |
| verticalAlignment = Alignment.CenterVertically | |
| ) { |
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/makeroom/component/GroupDatePicker.kt
around lines 51–53, the Row root ignores the incoming modifier so parent layout
modifiers like Modifier.weight(1f) are discarded; update the Row call to pass
the composable's modifier (e.g., Row(modifier = modifier, verticalAlignment =
Alignment.CenterVertically) { ... }), and if the composable signature doesn't
already declare a modifier parameter, add one with a default of Modifier so
callers can supply layout modifiers.
| modifier = Modifier | ||
| .weight(1f) | ||
| .fillMaxWidth() | ||
| .pointerInput(Unit) { |
There was a problem hiding this comment.
GroupDatePicker에 전달한 weight가 현재 무효 — 레이아웃 붕괴 가능성
여기서 modifier = Modifier.weight(1f)를 넘기고 있으나, GroupDatePicker 루트가 해당 modifier를 적용하지 않아(weight 미적용) 균등 분할 의도가 반영되지 않습니다. 결과적으로 두 픽커가 중앙에 압축되어 보일 수 있습니다.
해결책 1(권장): GroupDatePicker 루트 Row에 modifier를 전달
- 해당 파일 코멘트 참조. 아래 패치를 GroupDatePicker.kt에 적용하세요.
-Row(
- verticalAlignment = Alignment.CenterVertically
-) {
+Row(
+ modifier = modifier,
+ verticalAlignment = Alignment.CenterVertically
+) {해결책 2(대안): 이 파일에서 래퍼 컨테이너에 weight 적용
- Row의 직계 자식에 weight가 먹도록 Box로 감싸는 방법입니다. 다만 코드베이스 전반 일관성을 위해 1안을 권장합니다.
Also applies to: 141-143
| AnimatedVisibility( | ||
| visible = activeToast != null, | ||
| enter = slideInVertically( | ||
| initialOffsetY = { -it }, | ||
| animationSpec = tween(durationMillis = 2000) | ||
| ), | ||
| exit = slideOutVertically( | ||
| targetOffsetY = { -it }, | ||
| animationSpec = tween(durationMillis = 2000) | ||
| ), | ||
| modifier = Modifier | ||
| .align(Alignment.TopCenter) | ||
| .padding(horizontal = 20.dp, vertical = 16.dp) | ||
| .zIndex(3f) | ||
| ) { | ||
| when (activeToast) { | ||
| ToastType.DAILY_GREETING_LIMIT -> { | ||
| ToastWithDate(color = colors.Red) | ||
| } | ||
|
|
||
| ToastType.FIRST_WRITE -> { | ||
| ToastWithDate() | ||
| } | ||
| ToastType.FIRST_WRITE -> { | ||
| ToastWithDate() | ||
| } | ||
|
|
||
| null -> {} | ||
| null -> {} | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
중복 AnimatedVisibility로 2중 애니메이션/패딩 적용됨 — 하나로 단순화하세요
바깥/안쪽 모두 visible = activeToast != null에 동일한 슬라이드 인/아웃과 동일한 패딩/정렬을 적용하고 있어, 애니메이션이 이중으로 실행되고(잔상/지연 가능성), 패딩도 2배로 적용됩니다. 또한 내부 AnimatedVisibility의 .align()는 BoxScope가 아니어서 효과가 없습니다. 바깥 래퍼만 남기고 내부는 제거하세요.
아래처럼 내부 AnimatedVisibility를 제거하고 토스트만 직접 그리면 됩니다(동시에 FIRST_WRITE 메시지도 명시적으로 분리하는 예 포함 — 리소스 키는 실제 키로 교체 필요).
- AnimatedVisibility(
- visible = activeToast != null,
- enter = slideInVertically(
- initialOffsetY = { -it },
- animationSpec = tween(durationMillis = 2000)
- ),
- exit = slideOutVertically(
- targetOffsetY = { -it },
- animationSpec = tween(durationMillis = 2000)
- ),
- modifier = Modifier
- .align(Alignment.TopCenter)
- .padding(horizontal = 20.dp, vertical = 16.dp)
- .zIndex(3f)
- ) {
- when (activeToast) {
- ToastType.DAILY_GREETING_LIMIT -> {
- ToastWithDate(color = colors.Red)
- }
-
- ToastType.FIRST_WRITE -> {
- ToastWithDate()
- }
-
- null -> {}
- }
- }
+ when (activeToast) {
+ ToastType.DAILY_GREETING_LIMIT -> {
+ ToastWithDate(color = colors.Red)
+ }
+ ToastType.FIRST_WRITE -> {
+ // TODO: 올바른 문자열 리소스 키로 교체하세요.
+ // 예) stringResource(R.string.group_room_first_write_message)
+ ToastWithDate(
+ message = stringResource(R.string.group_room_first_write_message)
+ )
+ }
+ null -> {}
+ }| ToastType.FIRST_WRITE -> { | ||
| ToastWithDate() | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
❓ Verification inconclusive
FIRST_WRITE 토스트 메시지가 기본값(채팅 한도)과 동일하게 표시될 수 있음
ToastWithDate() 기본 메시지가 group_room_chat_max로 설정되어 있어 FIRST_WRITE 케이스에도 동일 문구가 노출됩니다. FIRST_WRITE 전용 문구를 명시적으로 전달하세요. 위 제안된 diff 참고. 리소스 키는 실제 정의된 키로 교체 필요합니다.
FIRST_WRITE 케이스에 전용 토스트 메시지 지정 필요
기본 ToastWithDate()의 messageRes 디폴트 값이 R.string.group_room_chat_max으로 설정되어 있어, FIRST_WRITE에서도 동일 문구가 출력됩니다. FIRST_WRITE 전용 메시지를 리소스에 정의한 뒤, 아래 위치에서 명시적으로 전달해주세요.
• 파일: app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt
위치: 284–286행
- ToastType.FIRST_WRITE -> {
- ToastWithDate()
- }
+ ToastType.FIRST_WRITE -> {
+ ToastWithDate(R.string.group_room_chat_first_write)
+ }리소스 키(group_room_chat_first_write)는 실제 정의된 값으로 교체 바랍니다.
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt
around lines 284 to 286, the ToastType.FIRST_WRITE branch currently calls
ToastWithDate() which uses the default messageRes
(R.string.group_room_chat_max); create a dedicated string resource (e.g.
R.string.group_room_chat_first_write) in your strings.xml and change the branch
to call ToastWithDate(messageRes = R.string.group_room_chat_first_write)
(replace the resource key with the actual defined name).
| Row( | ||
| Modifier.fillMaxWidth(), | ||
| verticalAlignment = Alignment.CenterVertically, | ||
| horizontalArrangement = Arrangement.SpaceBetween | ||
| verticalAlignment = Alignment.CenterVertically | ||
| ) { |
There was a problem hiding this comment.
🛠️ Refactor suggestion
하드코딩된 패딩(90.dp) 대신 레이아웃 배치로 정렬 유지
Arrangement.SpaceBetween을 제거하고 start=90.dp로 간격을 맞추는 방식은 화면 크기/폰트 스케일 변화에 취약합니다. 기존처럼 Row에 SpaceBetween을 주는 편이 견고합니다. 그에 따라 패딩을 제거하세요.
- Row(
- Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically
- ) {
+ Row(
+ Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
@@
- Column(
- verticalArrangement = Arrangement.Center,
- modifier = Modifier.padding(start = 90.dp)
- ) {
+ Column(
+ verticalArrangement = Arrangement.Center,
+ ) {대안: Spacer(Modifier.weight(1f))로 두 블록 사이에 가변 공간을 주는 방법도 있습니다.
Also applies to: 281-285
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomRecruitScreen.kt
around lines 247-250 (also applicable to 281-285), the layout currently uses a
hardcoded start padding of 90.dp and removes Arrangement.SpaceBetween to align
items; replace that approach by restoring Arrangement.SpaceBetween on the Row
(or alternatively insert a Spacer(Modifier.weight(1f)) between the two child
blocks) and remove the fixed 90.dp padding so alignment is responsive to screen
size and font scaling.
|
|
||
| <!-- 버튼 컴포넌트 --> | ||
| <string name="author">저</string> | ||
| <string name="author">" 저"</string> |
There was a problem hiding this comment.
문자열 값에 불필요한 큰따옴표와 선행 공백이 포함되어 있습니다
" 저"는 실제 화면에 따옴표와 공백까지 노출됩니다. i18n/레이아웃 관점에서 선행 공백은 레이아웃으로, 따옴표는 디자인 의도가 명확할 때만 사용하는 것이 안전합니다.
다음과 같이 정정하는 것을 권장합니다.
- <string name="author">" 저"</string>
+ <string name="author">저</string>레이아웃에서 간격이 필요하면 Spacer 또는 modifier.padding(start = …)로 처리해주세요.
📝 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.
| <string name="author">" 저"</string> | |
| <string name="author">저</string> |
🤖 Prompt for AI Agents
In app/src/main/res/values/strings.xml around line 30, the string value contains
unnecessary surrounding double quotes and a leading space (" 저"), which will
render literal quotes and extra space in the UI; remove the quotes and leading
whitespace so the value becomes just 저, and handle any required leading spacing
in the layout (e.g., Spacer or modifier.padding(start = ...)) instead of
embedding spaces in the string resource.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (1)
34-35: Pull-to-refresh 중 무한 스크롤 방지 가드 추가 필요
canLoadMoreAllFeeds와canLoadMoreMyFeeds가isPullToRefreshing을 고려하지 않아 Pull-to-refresh 중에도 추가 페이지 로드가 트리거될 수 있습니다.- val canLoadMoreAllFeeds: Boolean get() = !isLoading && !isLoadingMore && !isRefreshing && !isLastPageAllFeeds - val canLoadMoreMyFeeds: Boolean get() = !isLoading && !isLoadingMore && !isRefreshing && !isLastPageMyFeeds + val canLoadMoreAllFeeds: Boolean + get() = !isLoading && !isLoadingMore && !isRefreshing && !isPullToRefreshing && !isLastPageAllFeeds + val canLoadMoreMyFeeds: Boolean + get() = !isLoading && !isLoadingMore && !isRefreshing && !isPullToRefreshing && !isLastPageMyFeeds
🧹 Nitpick comments (2)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (1)
273-273:isPullToRefreshing검사 추가 필요
loadMoreFeeds()함수가 Pull-to-refresh 상태를 체크하지 않습니다.canLoadMoreCurrentTab에 이미 포함되어야 하지만, 방어적 프로그래밍을 위해 추가 검사를 권장합니다.- if (!_uiState.value.canLoadMoreCurrentTab || _uiState.value.isRefreshing) return + if (!_uiState.value.canLoadMoreCurrentTab || _uiState.value.isRefreshing || _uiState.value.isPullToRefreshing) returnapp/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (1)
133-136: 탭 전환 시 스크롤 위치 초기화
scrollToItem(0)대신animateScrollToItem(0)을 사용하면 더 부드러운 UX를 제공할 수 있습니다.LaunchedEffect(feedUiState.selectedTabIndex) { - currentListState.scrollToItem(0) + currentListState.animateScrollToItem(0) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (8)
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt(8 hunks)app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt(6 hunks)app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteUiState.kt
- app/src/main/java/com/texthip/thip/ui/feed/screen/FeedWriteScreen.kt
- app/src/main/java/com/texthip/thip/ui/feed/screen/FeedCommentScreen.kt
- app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedWriteViewModel.kt
- app/src/main/java/com/texthip/thip/ui/navigator/navigations/FeedNavigation.kt
🧰 Additional context used
🧬 Code Graph Analysis (1)
app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/common/topappbar/GradationTopAppBar.kt (1)
GradationTopAppBar(31-109)
🔇 Additional comments (14)
app/src/main/java/com/texthip/thip/ui/feed/viewmodel/FeedViewModel.kt (1)
286-301:resetToInitialState()구현이 깔끔합니다!전체 피드 상태를 초기화하고 데이터를 다시 로드하는 로직이 명확하게 구현되었습니다. 탭 인덱스를 0으로 리셋하고 모든 피드 리스트를 초기화하는 접근이 적절합니다.
app/src/main/java/com/texthip/thip/ui/feed/screen/FeedScreen.kt (4)
89-96: 탭별 스크롤 상태 분리가 잘 구현되었습니다각 탭마다 독립적인
LazyListState를 유지하여 탭 전환 시에도 스크롤 위치가 보존되는 UX 개선이 좋습니다.
99-111: 무한 스크롤 로직에서currentListState사용이 적절합니다탭별로 분리된 리스트 상태를 정확히 참조하여 무한 스크롤을 트리거하는 로직이 올바르게 구현되었습니다.
158-165: 외부 새로고침 트리거 처리 로직 확인 필요
refreshFeed가true일 때resultFeedId가null인 경우에만refreshData()를 호출합니다. 만약 두 값이 동시에 설정되는 경우 새로고침이 발생하지 않을 수 있습니다. 의도된 동작인지 확인이 필요합니다.
429-440: 탭 전환 시 로딩 인디케이터 표시가 적절합니다탭 전환 중 화면 중앙에 로딩 인디케이터를 표시하여 사용자에게 명확한 피드백을 제공하는 점이 좋습니다.
app/src/main/java/com/texthip/thip/ui/search/screen/SearchBookDetailScreen.kt (9)
40-40: 새로운 import가 적절히 추가되었습니다.태블릿 대응을 위한
LocalConfigurationimport가 추가되어 화면 크기 기반 동적 레이아웃 구현을 가능하게 합니다.
73-73: 함수 시그니처 변경이 적절합니다.
onWriteFeedClick콜백이BookDetailResponse를 매개변수로 받도록 변경되어, 피드 작성 시 책 정보를 전달할 수 있게 되었습니다. 이는 PR 목표와 일치합니다.
118-118: private 함수도 일관되게 업데이트되었습니다.공개 함수와 동일하게
onWriteFeedClick시그니처가 변경되어 코드 일관성이 유지되고 있습니다.
129-134: 태블릿 대응을 위한 동적 높이 설정이 잘 구현되었습니다.화면 높이의 60%를 최대값으로 하되 620dp를 상한선으로 설정하여, 다양한 화면 크기에서 적절한 이미지 표시가 가능하도록 구현했습니다.
224-224: 최대 높이 제한이 적절히 적용되었습니다.계산된
maxImageHeight를 사용하여 이미지가 과도하게 크게 표시되지 않도록 제한하고 있습니다.
237-241: 그라데이션 투명도 조정이 개선되었습니다.기존보다 초기 어둡기를 줄이고(0.2f에서 시작) 하단으로 갈수록 점진적으로 진해지도록 조정하여 더 자연스러운 그라데이션 효과를 만들어냅니다.
327-327: 책 정보를 전달하는 콜백 호출이 올바릅니다.
onWriteFeedClick(bookDetail)로 현재 책 정보를 전달하여 피드 작성 화면에서 해당 책과 연관된 피드를 작성할 수 있게 됩니다.
397-413: 장식용 그라데이션 박스가 시각적 전환을 개선합니다.책 정보 영역에서 피드 섹션으로의 자연스러운 전환을 위한 그라데이션을 추가하여 UI의 연속성을 높였습니다.
503-509: GradationTopAppBar 사용법이 새로운 API에 맞게 업데이트되었습니다.
autoHideCount: true로 설정하여 카운트를 자동으로 숨기는 기능 활용countDisplayDurationMs: 5000L로 표시 시간 명시onRightClick을 빈 람다로 설정하여 오른쪽 버튼 비활성화이 변경사항은
GradationTopAppBar의 새로운 기능과 완벽하게 일치합니다.
➕ 이슈 링크
🔎 작업 내용
📸 스크린샷
😢 해결하지 못한 과제
[] 비밀글일때 lock 아이콘 구현은 해당 책으로 모집중인 모임방 api가 수정되면 저도 맞춰서 수정하겠습니다
📢 리뷰어들에게
Summary by CodeRabbit
New Features
Style
Chores