[REFACTOR] 완료된 모임방 구현, 모임방 사용성 개선작업#139
Conversation
Walkthrough완료(만료) 플래그(isExpired)와 마감일(deadlineDate)을 모델·서비스·내비게이션·UI 전반에 추가·전파하여 만료된 모임방에 대해 읽기 전용 동작(입력 숨김, 액션 대신 토스트)을 적용하고, 검색/내비게이션에 전체 보기(viewAll/isAllCategory) 및 최근 생성 섹션을 도입했습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Done as GroupDoneScreen
participant Nav as NavController
participant Routes as GroupRoutes
participant Room as GroupRoomScreen
Note right of Done: 카드 클릭 시 onRoomClick 전달
User->>Done: 클릭(roomId)
Done->>Nav: navigateToGroupRoom(roomId, isExpired=true)
Nav->>Routes: build route Room(roomId, isExpired=true)
Routes->>Room: Compose with isExpired=true
Room-->>User: 읽기 전용 UI (입력 숨김, 액션 토스트)
sequenceDiagram
actor User
participant Chat as GroupRoomChatContent
participant Toast as ToastHost
participant Bottom as BottomSheet
User->>Chat: 카드 클릭
alt isExpired == true
Chat->>Toast: show(expired_room_read_only_message)
else isExpired == false
Chat->>Bottom: openBottomSheet()
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (12)
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt (3)
234-235: TODO 주석에 대한 구체적인 계획 필요
// TODO: 방 삭제 기능 추가 시 변경주석이 있는데, 현재 로직(!isExpired || !isOwner)이 의도한 동작인지 명확하지 않습니다. 만료된 방의 소유자가 kebab 메뉴를 볼 수 없도록 하는 것이 맞는지 확인이 필요합니다.이슈를 생성하거나 더 구체적인 설명을 주석에 추가하는 것을 권장합니다.
114-114: 에러 메시지 색상 일관성에러 상태에서
colors.White를 사용하고 있는데, 다른 화면들과 일관성 있게 에러 메시지는colors.Red를 사용하는 것이 좋을 것 같습니다.- Text(text = state.message, color = colors.White) + Text(text = state.message, color = colors.Red)
62-62: isExpired 기본값을 호출에서 명시적으로 전달하세요app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt(라인 74)에서 isExpired: Boolean = false로 정의되어 있습니다. 다음 호출들이 값을 생략해 기본값(false)을 사용합니다: app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt:94, 233. 일부 호출은 명시적으로 전달함(isExpired = true @148, isExpired = false @167). 생략이 의도된 동작인지 확인하고, 의도가 아니면 명시적으로 true/false를 전달하거나 주석으로 의도 표기.
app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt (1)
74-76:isExpired파라미터 전달 구조 개선 고려세 개의 네비게이션 함수에서
isExpired파라미터를 추가했는데, 향후 더 많은 상태 정보가 필요할 경우 파라미터가 계속 늘어날 수 있습니다.향후 확장성을 고려하여 상태 정보를 담는 data class를 만들어 사용하는 방법을 고려해보세요:
data class RoomNavigationState( val isExpired: Boolean = false, // 향후 추가 상태들 ) fun NavHostController.navigateToGroupRoom( roomId: Int, state: RoomNavigationState = RoomNavigationState() ) { navigate(GroupRoutes.Room(roomId, state.isExpired)) }Also applies to: 84-86, 89-96
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt (3)
77-77: 중복된 토스트 메시지 설정 로직
expiredRoomMessage를 통해 토스트를 표시하는 로직이onShowExpiredRoomToast콜백에 중복되어 있습니다.토스트 메시지 설정 로직을 ViewModel로 이동하거나, 토스트 타입에
EXPIRED_ROOM을 추가하여 일관된 방식으로 처리하는 것이 좋겠습니다:- val expiredRoomMessage = stringResource(R.string.expired_room_read_only_message) LaunchedEffect(key1 = Unit) { viewModel.eventFlow.collectLatest { event -> when (event) { is GroupRoomChatEvent.ShowToast -> { when (event.type) { + ToastType.EXPIRED_ROOM -> { + toastMessage = stringResource(R.string.expired_room_read_only_message) + toastColor = colorWhite + } // ... 다른 케이스들Also applies to: 133-136
275-281: 만료된 방에서의 메뉴 클릭 처리 개선 필요
onMenuClick핸들러에서isExpired체크 로직이 있지만, 사용자에게 더 명확한 피드백을 제공할 수 있습니다.메뉴 아이콘 자체를 비활성화 상태로 표시하거나, 시각적 힌트를 제공하는 것이 좋겠습니다:
CardCommentGroup( data = message, isMenuEnabled = !isExpired, // 메뉴 활성화 상태 전달 onMenuClick = { if (isExpired) { onShowExpiredRoomToast() } else { selectedMessage = message isBottomSheetVisible = true } } )
288-295:CommentTextField숨김 처리 확인만료된 방에서
CommentTextField를 숨기는 것은 적절하나, 사용자에게 왜 입력이 불가능한지 안내 메시지를 표시하는 것이 좋을 것 같습니다.if (!isExpired) { CommentTextField( input = inputText, hint = stringResource(R.string.group_room_chat_hint), onInputChange = onInputTextChanged, onSendClick = onSendClick ) +} else { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp, vertical = 10.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = stringResource(R.string.expired_room_read_only_message), + style = typography.info_r400_s12_h24, + color = colors.Grey01 + ) + } }app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt (1)
147-149: 완료된 방 네비게이션 시isExpired = true하드코딩
GroupDoneScreen에서 모든 방을isExpired = true로 네비게이션하고 있는데, 실제 방의 만료 상태를 서버에서 받아와 전달하는 것이 더 정확할 것 같습니다.서버에서 각 방의 실제 만료 상태를 받아와서 전달하도록 개선이 필요합니다. 현재는 완료된 방 목록에 있다고 해서 모두 만료된 것으로 처리하고 있습니다.
app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt (4)
197-197: 토스트 메시지 중복 선언
expiredRoomMessage가GroupNoteContent내부에서 선언되었지만,showExpiredToast람다에서만 사용됩니다.토스트 메시지를 한 곳에서 관리하도록 개선:
- val expiredRoomMessage = stringResource(R.string.expired_room_read_only_message) - // ... - val showExpiredToast = { - toastMessage = expiredRoomMessage - showToast = true - } + fun showExpiredToast() { + toastMessage = stringResource(R.string.expired_room_read_only_message) + showToast = true + }Also applies to: 374-377
388-391: 만료 상태 체크 로직 중복여러 곳에서
if (isExpired) showExpiredToast() else { ... }패턴이 반복됩니다.공통 함수로 추출하여 중복 제거:
inline fun executeIfNotExpired( isExpired: Boolean, onExpired: () -> Unit, action: () -> Unit ) { if (isExpired) onExpired() else action() } // 사용 예시: onLongPress = { executeIfNotExpired( isExpired = isExpired, onExpired = showExpiredToast, action = { selectedPostForMenu = post } ) }Also applies to: 397-400, 411-415, 420-423
493-508: FAB 버튼 숨김 처리 개선만료된 방에서 FAB를 완전히 숨기는 대신, 비활성화 상태로 표시하고 클릭 시 안내 메시지를 보여주는 것이 더 나은 UX일 수 있습니다.
- if (!isExpired) { - ExpandableFloatingButton( - menuItems = listOf( - // ... - ) - ) - } + ExpandableFloatingButton( + isEnabled = !isExpired, + onDisabledClick = { showExpiredToast() }, + menuItems = listOf( + // ... + ) + )
652-652: 토스트 색상 로직 개선
isErrorToast플래그 대신 토스트 타입 enum을 사용하면 더 확장 가능한 구조가 될 것 같습니다.enum class ToastType { SUCCESS, ERROR, WARNING, INFO } // 색상 결정 로직 val toastColor = when(toastType) { ToastType.ERROR -> colors.Red ToastType.WARNING -> colors.Yellow else -> colors.White }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (13)
app/src/main/java/com/texthip/thip/data/model/rooms/response/JoinedRoomListResponse.kt(1 hunks)app/src/main/java/com/texthip/thip/data/service/RoomsService.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/group/done/screen/GroupDoneScreen.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupMainCard.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt(6 hunks)app/src/main/java/com/texthip/thip/ui/group/note/component/CommentSection.kt(4 hunks)app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt(10 hunks)app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt(6 hunks)app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt(5 hunks)app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt(2 hunks)app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt(7 hunks)app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt(2 hunks)app/src/main/res/values/strings.xml(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-01T07:19:10.981Z
Learnt from: rbqks529
PR: THIP-TextHip/THIP-Android#30
File: app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoomSmall.kt:108-112
Timestamp: 2025-07-01T07:19:10.981Z
Learning: The user rbqks529 prefers to extract hardcoded UI strings to StringResource files (strings.xml) rather than keeping them inline in Compose components, which is a good practice for maintainability and localization in Android development.
Applied to files:
app/src/main/java/com/texthip/thip/ui/group/note/component/CommentSection.kt
🧬 Code graph analysis (5)
app/src/main/java/com/texthip/thip/ui/group/note/component/CommentSection.kt (1)
app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt (1)
onEvent(59-71)
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/common/forms/CommentTextField.kt (1)
CommentTextField(42-152)
app/src/main/java/com/texthip/thip/ui/group/note/screen/GroupNoteScreen.kt (3)
app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/GroupNoteViewModel.kt (1)
onEvent(131-175)app/src/main/java/com/texthip/thip/ui/group/note/viewmodel/CommentsViewModel.kt (1)
onEvent(59-71)app/src/main/java/com/texthip/thip/ui/common/buttons/ExpandableFloatingButton.kt (1)
ExpandableFloatingButton(45-114)
app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt (1)
app/src/main/java/com/texthip/thip/ui/common/forms/CommentTextField.kt (1)
CommentTextField(42-152)
app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt (2)
app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt (1)
GroupRoomScreen(59-118)app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt (1)
GroupRoomChatScreen(59-138)
🔇 Additional comments (14)
app/src/main/java/com/texthip/thip/ui/group/note/component/CommentBottomSheet.kt (3)
54-54: 기본값을 명시적으로 설정한 좋은 API 설계!
isExpired: Boolean = false파라미터 추가가 잘 구현되었습니다. Compose 컴포넌트에 필요한 파라미터만 전달하는 것이 모범 사례이며, 기본값을 false로 설정하여 기존 호출 코드와의 호환성을 유지했습니다.
149-175: 만료된 모임방에서 댓글 입력 차단 로직이 명확합니다.
if (!isExpired)조건을 사용하여 완료된 모임방에서는CommentTextField를 렌더링하지 않도록 구현했습니다. 이는 읽기 전용 모드를 명확하게 구현하는 좋은 방법입니다.
126-126:isExpired파라미터가 하위 컴포넌트까지 일관성 있게 전달됩니다.
CommentLazyList와CommentSection으로isExpired상태가 올바르게 전파되어 전체적인 만료 모드 처리가 일관성 있게 구현되었습니다.Also applies to: 232-232, 273-273
app/src/main/res/values/strings.xml (2)
178-178: 적절한 문자열 리소스 추가!
until_start문자열이 추가되어 마감일 표시 기능에 사용됩니다. 하드코딩된 문자열 대신 리소스를 사용하는 것이 유지보수와 지역화에 좋은 접근입니다.
260-260: 만료된 모임방 안내 메시지가 명확합니다.
expired_room_read_only_message문자열이 사용자에게 완료된 모임방에서는 조회만 가능하다는 것을 명확하게 안내합니다. 사용자 경험을 고려한 좋은 메시지입니다.app/src/main/java/com/texthip/thip/data/model/rooms/response/JoinedRoomListResponse.kt (1)
22-22: 데이터 모델에 마감일 필드 추가가 적절합니다.
deadlineDate: String? = null필드가 추가되어 UI에서 마감일 정보를 표시할 수 있게 되었습니다. nullable과 기본값 설정으로 기존 API와의 호환성을 유지했습니다.app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupMainCard.kt (2)
125-158: 조건부 렌더링 로직이 깔끔하게 구현되었습니다.
data.deadlineDate의 존재 여부에 따라 진행도 표시와 시작까지 남은 시간 표시를 선택적으로 렌더링하는 로직이 명확합니다. 사용자에게 상황에 맞는 정보를 제공하는 좋은 UX입니다.
172-175: 배경 modifier 포맷팅이 일관성 있게 개선되었습니다.멀티라인으로
RoundedCornerShape파라미터를 명시적으로 표현하여 코드 가독성이 향상되었습니다.app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt (1)
34-34: 네비게이션 라우트에 만료 상태 지원이 잘 추가되었습니다.
Room,RoomChat,Note라우트에isExpired: Boolean = false파라미터가 추가되어 만료된 모임방으로의 네비게이션을 지원합니다. 기본값 설정으로 기존 네비게이션 코드와의 호환성을 유지했습니다.Also applies to: 40-40, 43-48
app/src/main/java/com/texthip/thip/ui/group/done/screen/GroupDoneScreen.kt (1)
40-40: 완료된 모임방 클릭 이벤트 처리가 올바르게 구현되었습니다.
onRoomClick: (Int) -> Unit콜백이 추가되어 완료된 모임방을 클릭했을 때 해당 모임방으로 네비게이션할 수 있게 되었습니다. 콜백이 컴포넌트 계층을 통해 적절히 전파되고 있습니다.Also applies to: 59-59, 124-124
app/src/main/java/com/texthip/thip/ui/group/note/component/CommentSection.kt (3)
11-11: 문자열 리소스 사용이 우수한 관례를 따릅니다.이전 학습 내용에 따르면, 사용자는 하드코딩된 문자열 대신 StringResource 사용을 선호하므로
stringResource(R.string.expired_room_read_only_message)사용이 적절합니다.Also applies to: 14-14, 31-31
23-23: 만료 모드 지원을 위한 파라미터 추가가 적절합니다.
isExpired: Boolean = false와onShowToast: (String) -> Unit = {}파라미터가 추가되어 완료된 모임방에서의 상호작용을 제어할 수 있게 되었습니다.Also applies to: 29-29
44-67: 만료된 모임방에서의 상호작용 차단이 일관성 있게 구현되었습니다.답글, 좋아요, 길게 누르기 등 모든 상호작용에서
isExpired조건을 확인하여 토스트 메시지를 표시하거나 실제 액션을 수행하도록 구현되었습니다. 사용자에게 일관된 경험을 제공합니다.Also applies to: 78-99
app/src/main/java/com/texthip/thip/data/service/RoomsService.kt (1)
111-111: API 엔드포인트 변경 확인 — 클라이언트 일관성 확보리포지토리 내부 Kotlin 검색 결과 '/rooms/{roomId}/playing' 사용처가 없고 RoomsService.kt가 @get("rooms/{roomId}")로 업데이트되어 클라이언트 쪽 변경은 완료되었습니다.
app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt
Show resolved
Hide resolved
| val feedViewModel: FeedViewModel = | ||
| hiltViewModel(navController.getBackStackEntry(MainTabRoutes.Group)) | ||
| val feedUiState by feedViewModel.uiState.collectAsState() |
There was a problem hiding this comment.
FeedViewModel 인스턴스 생성 위치
FeedViewModel을 MainTabRoutes.Group의 백스택 엔트리에서 가져오고 있는데, 해당 엔트리가 없을 경우 크래시가 발생할 수 있습니다.
- val feedViewModel: FeedViewModel =
- hiltViewModel(navController.getBackStackEntry(MainTabRoutes.Group))
+ val feedViewModel: FeedViewModel = try {
+ hiltViewModel(navController.getBackStackEntry(MainTabRoutes.Group))
+ } catch (e: IllegalArgumentException) {
+ hiltViewModel() // 기본 인스턴스 생성
+ }📝 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.
| val feedViewModel: FeedViewModel = | |
| hiltViewModel(navController.getBackStackEntry(MainTabRoutes.Group)) | |
| val feedUiState by feedViewModel.uiState.collectAsState() | |
| val feedViewModel: FeedViewModel = try { | |
| hiltViewModel(navController.getBackStackEntry(MainTabRoutes.Group)) | |
| } catch (e: IllegalArgumentException) { | |
| hiltViewModel() // 기본 인스턴스 생성 | |
| } | |
| val feedUiState by feedViewModel.uiState.collectAsState() |
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt
around lines 290-292, the code assumes
navController.getBackStackEntry(MainTabRoutes.Group) always exists which can
crash if the entry is absent; change it to safely obtain the NavBackStackEntry
(e.g., check navController.getBackStackEntry(...) existence or use
navController.getBackStackEntry(...) inside a try/catch or runCatching) and fall
back to a safe owner (like navController.currentBackStackEntry or the local
composable owner) before calling hiltViewModel, ensuring you only call
hiltViewModel with a non-null owner to prevent the crash.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (14)
app/src/main/java/com/texthip/thip/ui/feed/component/MySubscribelistBar.kt (2)
140-147: 접근성(a11y) 개선: 클릭 가능한 영역에 Role 지정시맨틱 Role을 명시하면 보조기기에서 버튼으로 올바르게 인식합니다.
- .clickable { onClick() } + .clickable(role = Role.Button, onClick = onClick)필요 시 import:
import androidx.compose.ui.semantics.Role
50-56: 중첩 clickable 제거 제안 — 이중 터치 타겟/시맨틱 중복 가능성현재 상위
BoxWithConstraints와 하위(Empty/Row) 둘 다clickable입니다. 중첩 시 내부만 소비되어 UX 일관성이 깨질 수 있고 시맨틱이 중복됩니다. 상위의clickable을 제거하고, 빈 상태는EmptyMySubscriptionBar, 데이터가 있는 상태는 Row에만 클릭을 유지하는 편이 안전합니다.BoxWithConstraints( modifier = modifier .fillMaxWidth() .height(82.dp) - .clickable { onClick() } ) {Row 쪽은 Role을 함께 지정해 주세요:
Row( modifier = Modifier .fillMaxWidth() .height(58.dp) - .clickable { onClick() }, + .clickable(role = Role.Button, onClick = onClick), verticalAlignment = Alignment.CenterVertically ) {Also applies to: 84-90
app/src/main/java/com/texthip/thip/ui/group/search/component/GroupFilteredSearchResult.kt (4)
53-60: 선택 인덱스 범위 안전화(경계값 처리)
selectedGenreIndex가 [-1, genres.lastIndex] 범위를 벗어나면 하이라이트가 사라지는 등 미묘한 UX가 생길 수 있습니다. 안전하게 클램프하세요.- val finalSelectedIndex = if (selectedGenreIndex != -1) { - // 특정 장르가 선택되었다면, '전체' 칩 때문에 +1 된 인덱스를 사용 - selectedGenreIndex + 1 - } else { - // 특정 장르가 선택되지 않았다면, '전체' 칩(인덱스 0)을 선택 - 0 - } + val safeIndex = selectedGenreIndex.coerceIn(-1, genres.lastIndex) + val finalSelectedIndex = if (safeIndex != -1) { + // 특정 장르가 선택되었다면, '전체' 칩 때문에 +1 + safeIndex + 1 + } else { + // 특정 장르가 선택되지 않았다면, '전체'(0) + 0 + }
113-117: 무한 스크롤 중복 호출 방지(로딩 중 재호출 게이트)
LaunchedEffect(shouldLoadMore)는 값 변화 때만 재실행되지만, 스크롤 경계에서 false→true가 잦으면 로딩 중 중복 호출이 발생할 여지가 있습니다.isLoadingMore를 키와 조건에 포함해 방지하세요.- LaunchedEffect(shouldLoadMore) { - if (shouldLoadMore) { - onLoadMore() - } - } + LaunchedEffect(shouldLoadMore, isLoadingMore) { + if (shouldLoadMore && !isLoadingMore) { + onLoadMore() + } + }
119-140: LazyColumn에 안정 키 부여로 스크롤/애니메이션 안정화항목 키가 없으면 데이터 변경 시 스크롤 위치 보존/효율적인 재조합이 어려울 수 있습니다.
roomId를 키로 지정하세요.- LazyColumn(state = listState, contentPadding = PaddingValues(bottom = 20.dp)) { - itemsIndexed(roomList) { index, room -> + LazyColumn(state = listState, contentPadding = PaddingValues(bottom = 20.dp)) { + itemsIndexed(items = roomList, key = { _, room -> room.roomId }) { index, room ->
210-216: Preview 2: "전체" 중복 생성됨 — 호출 규약과 불일치컴포저블 내부에서 이미 '전체' 칩을 추가하므로 미리 전달하면 칩이 중복됩니다. Preview도 계약에 맞춰 수정하세요.
- GroupFilteredSearchResult( - genres = listOf("전체", "소설", "에세이", "자기계발", "경제/경영", "과학"), + GroupFilteredSearchResult( + genres = listOf("소설", "에세이", "자기계발", "경제/경영", "과학"),또한 KDoc에 “genres에는 '전체'를 포함하지 말 것(-1이 '전체' 의미)”을 명시하면 호출부 혼선을 줄일 수 있습니다.
app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt (2)
103-105: 변수명 섀도잉으로 가독성 저하상단의 scale(=0.94f)과 여기의 scale이 섀도잉됩니다. currentScale 등으로 분리하면 혼동을 줄일 수 있습니다.
- val scale = if (isCurrent) 1f else 0.94f + val currentScale = if (isCurrent) 1f else 0.94f @@ - scaleX = scale - scaleY = scale + scaleX = currentScale + scaleY = currentScale
82-84: room_section_recent 리소스 확인 — 기본값 존재, 지역화 미등록app/src/main/res/values/strings.xml:416에 최근 생성된 독서 모임방로 정의되어 있습니다; values-/strings.xml(다국어)에서는 해당 키가 없습니다 — 필요한 언어에 번역을 추가하세요.
app/src/main/java/com/texthip/thip/ui/group/search/screen/GroupSearchScreen.kt (2)
98-102: 포커스 해제 조건에 isAllCategory 포함 제안"전체 보기" 진입 시에도 키보드/포커스를 내리는 편이 자연스럽습니다.
- LaunchedEffect(uiState.isCompleteSearching) { - if (uiState.isCompleteSearching) { + LaunchedEffect(uiState.isCompleteSearching, uiState.isAllCategory) { + if (uiState.isCompleteSearching || uiState.isAllCategory) { focusManager.clearFocus() } }
170-191: 중복 조건 상수로 추출하여 드리프트 방지uiState.isCompleteSearching || uiState.isAllCategory 조건이 2곳에 반복됩니다. 상수로 올려 재사용하세요.
+ val isCompleteOrAll = uiState.isCompleteSearching || uiState.isAllCategory @@ - uiState.isCompleteSearching || uiState.isAllCategory -> { + isCompleteOrAll -> { @@ - if (uiState.isCompleteSearching || uiState.isAllCategory) { + if (isCompleteOrAll) {Also applies to: 196-201
app/src/main/java/com/texthip/thip/ui/group/search/component/GroupRecentSearch.kt (1)
39-40: "전체 모임 둘러보기" 액션 추가 👍 — 접근성 속성 보완 권장Row 전체 클릭은 좋지만 역할/포커스 힌트가 없어 스크린리더 접근성이 떨어집니다. semantics로 버튼 역할을 부여하세요.
- Row( - modifier = Modifier - .fillMaxWidth() - .clickable { - onViewAllRoomsClick() - }, + Row( + modifier = Modifier + .fillMaxWidth() + .semantics { role = Role.Button } + .clickable { onViewAllRoomsClick() } ,필요 import:
import androidx.compose.ui.semantics.role import androidx.compose.foundation.interaction.MutableInteractionSource // 선택 import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.RoleAlso applies to: 72-92
app/src/main/java/com/texthip/thip/ui/group/search/viewmodel/GroupSearchViewModel.kt (2)
53-53: 빈 화면 표시 조건 보강 검토현재
(isCompleteSearching || isAllCategory) && searchResults.isEmpty() && !isSearching입니다. 초기 진입에서isInitial이false로 전환되므로 실사용상 문제는 없겠지만, 방어적으로!isInitial을 함께 두면 추후 상태 추가 시 안전합니다.- val showEmptyState: Boolean get() = (isCompleteSearching || isAllCategory) && searchResults.isEmpty() && !isSearching + val showEmptyState: Boolean get() = + !isInitial && (isCompleteSearching || isAllCategory) && searchResults.isEmpty() && !isSearching
195-203: load more 호출 가드 보강 제안
canLoadMore만으로는nextCursor == null인 상태에서 호출될 수 있습니다. 커서 기반 페이징이면nextCursor체크를 추가하세요.- if (currentState.canLoadMore) { + if (currentState.canLoadMore && currentState.nextCursor != null) {app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt (1)
186-190: LaunchedEffect 키를 route.viewAll로 지정 권장
Unit대신route.viewAll을 키로 써야 재조립/복원 시 의도치 않은 재호출을 줄일 수 있습니다.- LaunchedEffect(Unit) { + LaunchedEffect(route.viewAll) { if (route.viewAll) { viewModel.onViewAllRooms() } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (14)
app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomMainResponse.kt(1 hunks)app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt(2 hunks)app/src/main/java/com/texthip/thip/data/service/RoomsService.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/feed/component/MySubscribelistBar.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt(6 hunks)app/src/main/java/com/texthip/thip/ui/group/search/component/GroupFilteredSearchResult.kt(1 hunks)app/src/main/java/com/texthip/thip/ui/group/search/component/GroupRecentSearch.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/group/search/screen/GroupSearchScreen.kt(6 hunks)app/src/main/java/com/texthip/thip/ui/group/search/viewmodel/GroupSearchViewModel.kt(7 hunks)app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt(3 hunks)app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt(11 hunks)app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt(2 hunks)app/src/main/res/values/strings.xml(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/res/values/strings.xml
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-01T07:19:10.981Z
Learnt from: rbqks529
PR: THIP-TextHip/THIP-Android#30
File: app/src/main/java/com/texthip/thip/ui/common/cards/CardItemRoomSmall.kt:108-112
Timestamp: 2025-07-01T07:19:10.981Z
Learning: The user rbqks529 prefers to extract hardcoded UI strings to StringResource files (strings.xml) rather than keeping them inline in Compose components, which is a good practice for maintainability and localization in Android development.
Applied to files:
app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt
🧬 Code graph analysis (4)
app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/feed/component/MySubscribelistBar.kt (1)
EmptyMySubscriptionBar(133-168)
app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt (3)
app/src/main/java/com/texthip/thip/ui/group/search/screen/GroupSearchScreen.kt (1)
GroupSearchScreen(40-62)app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomScreen.kt (1)
GroupRoomScreen(59-118)app/src/main/java/com/texthip/thip/ui/group/room/screen/GroupRoomChatScreen.kt (1)
GroupRoomChatScreen(59-138)
app/src/main/java/com/texthip/thip/ui/group/search/component/GroupFilteredSearchResult.kt (1)
app/src/main/java/com/texthip/thip/ui/common/buttons/GenreChipRow.kt (1)
GenreChipRow(21-114)
app/src/main/java/com/texthip/thip/ui/group/search/screen/GroupSearchScreen.kt (1)
app/src/main/java/com/texthip/thip/ui/common/buttons/FilterButton.kt (1)
FilterButton(37-128)
🔇 Additional comments (33)
app/src/main/java/com/texthip/thip/ui/group/search/component/GroupFilteredSearchResult.kt (2)
48-49: 트레일링 콤마 추가 OKCompose 파라미터 포맷팅/디프 관리에 유용합니다. 문제 없습니다.
65-76: 인덱스 변환 로직 명확하고 일관적입니다-1(해제/토글)과 0(전체)에 대한 매핑, 그 외
newIndex - 1로의 복원 모두 기대 동작입니다.
GenreChipRow내부 토글 규칙과 본 매핑이 가정대로 동작하는지, 실제 기기에서 "전체 → 특정 장르 → 전체 재선택" 순서로 빠르게 탭하며 시각적 하이라이트가 일관한지 한번 확인 부탁드립니다.app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt (3)
22-22: Search 라우트의 viewAll 기본값 추가 적절기본값(false) 덕분에 역호환성이 유지됩니다. 프로세스 복원 시 SavedState 직렬화에도 문제 없을 것으로 보입니다.
43-48: Note 라우트 확장 OK — 딥링크/백스택 복원 동작 수동 확인 필요isExpired 추가는 역호환(기본값)으로 보입니다. 리포지토리에서 GroupRoutes.Note 사용처를 찾지 못했으니 딥링크 파서와 Nav 백스택 복원 로직에서 기본값 경로만 사용되는지 수동으로 확인해 결과를 코멘트로 남겨주세요.
34-41: isExpired 전달 경로 수동 확인 필요app/src/main/java/com/texthip/thip/ui/navigator/routes/GroupRoutes.kt에서 Room/RoomChat에 isExpired(Boolean, 기본값=false) 추가됨. rg로 navigate 호출을 찾지 못해 자동 검증 실패; 모든 navigate(...)에서 isExpired 전달 여부와 기본값(false) 사용 시 UI 동작이 의도된 것인지 수동으로 확인.
app/src/main/java/com/texthip/thip/data/repository/RoomsRepository.kt (2)
60-70: 섹션 주석 업데이트 👍"마감임박/인기/최근 생성" 명시로 API 기대치가 명확해졌습니다.
141-152: 검색 API에 isAllCategory 추가 — RoomsService.searchRooms 시그니처 일치 여부 확인 필요RoomsRepository의 searchRooms 호출에서 인자(순서/추가: isAllCategory)가 변경되었습니다. 코드베이스 검색으로 RoomsService.searchRooms 심볼을 찾지 못해 자동 검증이 불가하므로, RoomsService.searchRooms의 시그니처(인자 개수·순서·기본값 포함)가 Repository 호출과 정확히 일치하는지 수동 확인 필요.
app/src/main/java/com/texthip/thip/ui/group/search/screen/GroupSearchScreen.kt (2)
179-181: 장르 토글 로직 간결화 Good현재 선택 항목을 다시 누르면 null 해제, 범위 체크 포함되어 있어 안정적입니다.
49-61: onViewAllRooms 연결 LGTM — ViewModel에서 부수효과 관리 수동 확인 필요onViewAllRooms가 ViewModel로 전달되는 것은 확인됩니다. 코드베이스 검색에서 'onViewAllRooms'/'isAllCategory' 결과가 없어 자동 검증에 실패했으니, isAllCategory 전환 시 초기화/스크롤 상태 등 부수효과가 ViewModel에서 관리되는지 수동 확인 필요.
app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt (2)
40-42: 검색 라우트 viewAll 전달 LGTMEmptyMySubscriptionBar와의 연결만 마지막으로 검증해주세요.
74-76: isExpired 전파 일관성 확보 — 검증 필요Room/RoomChat/Note 확장함수 시그니처가 라우트와 일치하고 기본값(false)으로 호출부 호환성이 유지됩니다. 다만 제공한 검증 스크립트가 빈 결과를 반환해 자동 확인에 실패했습니다.
레포 전체에서 재검색하여 정의/호출을 확인하세요:
#!/bin/bash rg -n --hidden --glob '!**/build/**' -C3 '\bnavigateToGroup\w*\(' -S파일: app/src/main/java/com/texthip/thip/ui/navigator/extensions/GroupNavigationExtensions.kt (74-76) — 또한 84-86, 90-96에 적용.
app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt (2)
153-157: "전체 모임 둘러보기" 바 추가 — 네비게이션 호출부 확인 필요UI 구성은 적절합니다. onNavigateToGroupSearchAllRooms가 실제로 GroupRoutes.Search(viewAll = true)로 라우팅되는지 호출부(정의/전달 위치)를 확인해 주세요. 자동 검증에서 ripgrep이 파일을 검색하지 못해 호출 추적을 완료하지 못했습니다. 파일: app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt (L153-157)
58-60: 새 콜백 파라미터 전파 LGTM — 자동 검증 실패(파일 검색 불가)rg가 파일을 검색하지 못해 자동 검증에 실패했습니다 — ViewModel/Navigation에서 람다 캡처로 인한 불필요한 재컴포지션이 없도록 콜백을 불변으로 전달하고(remember/rememberUpdatedState 사용 권장) 수동 검증 필요.
위치: app/src/main/java/com/texthip/thip/ui/group/screen/GroupScreen.kt (라인 58–60, 77–82)
app/src/main/java/com/texthip/thip/data/model/rooms/response/RoomMainResponse.kt (1)
20-22: recentRoomList 추가 — LGTM (필드명 검증 필요)emptyList() 기본값으로 역직렬화 안전. 제공하신 검색 결과가 비어 있어 코드베이스에서 'recentRoomList' 사용처를 찾지 못했으니 백엔드 필드명이 정확히 'recentRoomList'인지 최종 확인 필요.
app/src/main/java/com/texthip/thip/ui/group/search/viewmodel/GroupSearchViewModel.kt (8)
27-27: isAllCategory 플래그 추가 적절전체 보기 플로우를 명확히 구분하는 불리언 플래그 도입은 상태 전이 단순화에 도움이 됩니다.
112-114: 라이브 검색 전환 시 플래그 리셋 OK
isCompleteSearching=false,isAllCategory=false로 리셋하여 의도한 대로 라이브/일반/전체 보기 상태가 충돌하지 않습니다.
126-148: 빈 검색어 시 전체 보기로 라우팅하는 UX 합리적트림한 쿼리 기준으로 분기하고, 작업 전 기존 잡을 취소하는 순서도 적절합니다.
150-163: 전체 보기에서 빈 키워드 전송 호환성 확인 필요
onViewAllRooms()가searchQuery=""상태로performSearchWithCurrentQuery()를 호출하여, 결국roomsRepository.searchRooms(keyword = "")가 됩니다. 서버가isAllCategory=true일 때keyword=""를 허용하지 않으면 400/빈 결과가 날 수 있습니다. 백엔드가 “미전달(null)”과 “빈 문자열(“”)”을 동일하게 처리하는지 확인해 주세요. 필요 시 Retrofit 쿼리를 nullable로 바꾸고 빈 경우 전달 생략을 권장합니다.다음 두 방향 중 하나를 제안합니다:
- 서비스/레포 시그니처 변경:
@Query("keyword") keyword: String?로 두고, 호출부에서query.takeIf { it.isNotEmpty() }전달- 서버가
""를 허용하는지 QA로 확인 후 주석으로 계약 명시
168-179: 필터/정렬 변경 시 재검색 조건 합리적완료 검색 또는 전체 보기 모드에서만 재조회하도록 한 것은 불필요한 트래픽을 막아줍니다.
181-193: isFinalized 파생 로직 명확
!isLiveSearching || isAllCategory파생은 의도에 부합합니다. 중복 호출 방지를 위해 이 경로가 연달아 호출될 수 있는 지점(필터/정렬 탭 전환 등)에서searchJob취소가 선행되는 것도 좋습니다.
227-263: 성공/실패 상태 전이 일관성 양호성공 시엔 기존 플래그를 유지하고, 실패/널 응답 시에만
isLiveSearching/isCompleteSearching를 갱신하는 정책이 UI 깜빡임을 줄입니다.
205-226: 검증 필요 — 빈 문자열 대신 null 전달 여부 확인GroupSearchViewModel의 searchRooms 호출에서 category = selectedGenre?.apiCategory ?: "" 및 keyword가 빈 문자열로 전달될 수 있습니다. 백엔드가 빈 문자열을 '필터 없음'으로 처리하지 않으면 호출부에서 빈 문자열 대신 null을 전달하고 roomsRepository/service/API 시그니처를 nullable로 변경하세요. 권장 예: keyword = query.ifBlank { null }, category = selectedGenre?.apiCategory ?: null.
위치: app/src/main/java/com/texthip/thip/ui/group/search/viewmodel/GroupSearchViewModel.kt (약 205–226행). 저장소 검색 실패로 자동 검증 불가 — 수동 확인 필요.
app/src/main/java/com/texthip/thip/data/service/RoomsService.kt (3)
55-55: API 주석 업데이트 LGTM“마감임박/인기/최근 생성” 설명 반영 좋습니다.
111-116: 엔드포인트 변경(rooms/{roomId}/playing → rooms/{roomId}) — 서버 전환 및 응답 필드(모델) 확인 필요레포에서 @get("rooms/{roomId}")만 사용되며 '/playing' 경로는 존재하지 않습니다. 서버 전환이 완료되지 않았다면 404 위험이 있으니 백엔드 전환 일정과 응답 필드 동일성(RoomsPlayingResponse 재사용 가능 여부)을 확인하세요. 영향 범위: RoomsService.getRoomsPlaying, RoomsRepository, GroupRoomViewModel, GroupRoomScreen, RoomsPlayingResponse.
/** 기록장 API들 */ - @GET("rooms/{roomId}") + @GET("rooms/{roomId}") suspend fun getRoomsPlaying( @Path("roomId") roomId: Int ): BaseResponse<RoomsPlayingResponse> + + /** [LEGACY] 서버 전환 완료 전 임시 유지 — 완료 후 제거 */ + @GET("rooms/{roomId}/playing") + suspend fun getRoomsPlayingLegacy( + @Path("roomId") roomId: Int + ): BaseResponse<RoomsPlayingResponse>
101-107: isAllCategory 쿼리 파라미터 지원 여부 확인 필요
백엔드 API 스펙(문서/Swagger)에서isAllCategory쿼리 파라미터가 정의되어 있는지 반드시 확인해주세요.app/src/main/java/com/texthip/thip/ui/navigator/navigations/GroupNavigation.kt (8)
31-31: GroupSearchViewModel 직접 주입 추가 OK내비게이션 진입 시 뷰모델 초기화 흐름이 명확해졌습니다.
86-99: 검색/전체보기 진입 포인트 분리 좋습니다탭에서 바로 전체 보기로 진입 가능한 훅이 생겨 사용성에 도움됩니다.
151-153: 완료 목록 → 방 진입 시 isExpired=true 전달 적절완료 방은 읽기 전용이므로 명시적으로 전달하는 방향 타당합니다.
279-293: 만료 상태 라우팅 전달 일관성 확보 OKRoom → Chat/Note로
isExpired를 그대로 전파하는 구조가 읽기 전용 가드에 도움됩니다.
331-337: 채팅 화면 만료 가드 전달 OK만료 방에서 입력 차단 및 토스트 노출에 필요한 정보가 잘 전달됩니다.
171-171: My 화면에서 isExpired=false 하드코딩 — 잘못된 상태 전달 가능성진행 중이 아닌 모든 방에 대해
isExpired=false로 고정 전달하면 실제 만료 방도 쓰기 가능 UI가 노출될 수 있습니다. 이전 리뷰에서도 동일 지적이 있었습니다.해결 옵션:
- A(권장): 호출부에서 실제 만료 여부를 계산해 전달(예:
deadlineDate/progressEndDate/room.type기반 또는RoomUtils유틸 사용).- B(대안):
navigateToGroupRoom의isExpired파라미터를 제거하고,GroupRoomScreen이 서버 응답으로 만료 여부를 판별.
원하시면 선택한 방향에 맞춰 관련 호출부/라우트/스크린 시그니처 일괄 패치를 제안하겠습니다.
305-307: BackStackEntry 없을 때 크래시 위험 — 안전 가드 추가
navController.getBackStackEntry(MainTabRoutes.Group)가 없으면IllegalArgumentException로 크래시합니다. try/catch로 폴백 인스턴스를 만드세요.- val feedViewModel: FeedViewModel = - hiltViewModel(navController.getBackStackEntry(MainTabRoutes.Group)) + val feedViewModel: FeedViewModel = runCatching { + hiltViewModel(navController.getBackStackEntry(MainTabRoutes.Group)) + }.getOrElse { + hiltViewModel() + }
352-354: FeedViewModel 백스택 의존 재발 — 동일 가드 적용 필요위와 동일한 위험. 동일한 가드를 적용해 주세요.
- val feedViewModel: FeedViewModel = - hiltViewModel(navController.getBackStackEntry(MainTabRoutes.Group)) + val feedViewModel: FeedViewModel = runCatching { + hiltViewModel(navController.getBackStackEntry(MainTabRoutes.Group)) + }.getOrElse { + hiltViewModel() + }
| fun EmptyMySubscriptionBar( | ||
| modifier: Modifier = Modifier, | ||
| text: String = stringResource(R.string.find_thip_mate), | ||
| onClick: () -> Unit = {} | ||
| ) { |
There was a problem hiding this comment.
Composable 기본값에 stringResource 사용은 컴파일 오류를 유발합니다.
stringResource(...)는 @composable 컨텍스트에서만 호출 가능하므로 기본 파라미터 값에서 호출하면 빌드가 실패합니다. 리소스 ID를 기본으로 받고 본문에서 문자열을 해석하세요.
다음 수정안을 적용해 주세요:
@Composable
fun EmptyMySubscriptionBar(
- modifier: Modifier = Modifier,
- text: String = stringResource(R.string.find_thip_mate),
- onClick: () -> Unit = {}
+ modifier: Modifier = Modifier,
+ @StringRes textResId: Int = R.string.find_thip_mate,
+ onClick: () -> Unit = {}
) {
+ val displayText = stringResource(textResId)
@@
- text = text,
+ text = displayText,파일 상단에 import 추가:
import androidx.annotation.StringResAlso applies to: 152-153
🤖 Prompt for AI Agents
In app/src/main/java/com/texthip/thip/ui/feed/component/MySubscribelistBar.kt
around lines 134-138 (and similarly at 152-153), the composable currently uses
stringResource(...) as a default parameter which causes a compile error because
stringResource is only valid in a @Composable context; change the parameter to
accept a @StringRes Int resource ID (add import androidx.annotation.StringRes at
the top), give it a default resource ID (e.g. R.string.find_thip_mate), and
inside the composable body call stringResource(resId) to obtain the actual
String; apply the same pattern for the other affected composable at lines
152-153.
| val actualPageCount = roomSections.size | ||
|
|
||
| val effectivePagerState = rememberPagerState( | ||
| initialPage = 0, | ||
| pageCount = { roomSections.size } | ||
| initialPage = 2, | ||
| pageCount = { Int.MAX_VALUE } | ||
| ) |
There was a problem hiding this comment.
"무한 스와이프" 구현 불완전 — 초기 페이지가 2면 좌측(이전) 방향이 막힙니다
pageCount를 Int.MAX_VALUE로 두면서 initialPage=2는 시작점이 너무 앞쪽이라 0 이하로는 이동 불가합니다. 가운데 근처에서 시작하고 섹션 개수에 정렬되도록 초기 페이지를 계산하세요.
적용 제안:
- val actualPageCount = roomSections.size
+ val actualPageCount = roomSections.size
- val effectivePagerState = rememberPagerState(
- initialPage = 2,
- pageCount = { Int.MAX_VALUE }
- )
+ val startPage = remember(actualPageCount) {
+ if (actualPageCount > 0) {
+ val mid = Int.MAX_VALUE / 2
+ mid - (mid % actualPageCount)
+ } else 0
+ }
+ val effectivePagerState = rememberPagerState(
+ initialPage = startPage,
+ pageCount = { if (actualPageCount > 0) Int.MAX_VALUE else 1 }
+ )
@@
- val actualPage = page % actualPageCount
+ val actualPage = if (actualPageCount > 0) page % actualPageCount else 0Also applies to: 100-102
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/myroom/component/GroupDeadlineRoomSection.kt
around lines 87-92 (and also apply same change at lines ~100-102), the pager
uses pageCount = Int.MAX_VALUE with a fixed initialPage = 2 which prevents
swiping left past 0; compute an initial page positioned near the middle of the
Int.MAX_VALUE range but aligned to the actual number of sections: take center =
Int.MAX_VALUE / 2, subtract center % actualPageCount to align to a multiple of
actualPageCount, then add the desired small offset (e.g., 2) to get the
initialPage; replace the hardcoded initialPage with this computed value and
reuse the same logic for the other occurrence.
| val allChipText = stringResource(id = R.string.all) | ||
| val chipList = remember(genres) { listOf(allChipText) + genres } | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
'전체' 칩 중복 가능성과 로케일 변경 시 갱신 누락
- 현재
chipList는 호출 측이 이미"전체"(=R.string.all)를 포함해 전달하면 중복 칩이 생겨 인덱스 매핑이 틀어질 수 있습니다(아래 Preview 2가 실제로 그 케이스). remember(genres)만 키로 사용하여 로케일이 바뀌어allChipText가 변해도chipList가 재계산되지 않습니다.
아래처럼 allChipText를 키에 포함하고, 전달된 genres에서 동일 텍스트를 제거해 중복을 방지해주세요.
- val allChipText = stringResource(id = R.string.all)
- val chipList = remember(genres) { listOf(allChipText) + genres }
+ val allChipText = stringResource(id = R.string.all)
+ // 로케일 변화(allChipText 변경)에 반응하고, 호출 측에 "전체"가 포함돼도 중복 제거
+ val chipList = remember(allChipText, genres) {
+ val filtered = genres.filterNot { it == allChipText }
+ listOf(allChipText) + filtered
+ }다음 스크립트로 호출부에서 "전체"를 직접 전달하는 곳이 있는지 빠르게 확인해 주세요.
🏁 Script executed:
#!/bin/bash
# GroupFilteredSearchResult 호출부 훑기
rg -n --type=kt -C3 '\bGroupFilteredSearchResult\('
echo
echo '리터럴로 "전체"를 전달하는 목록 리터럴 탐지(오탐 가능):'
rg -n --type=kt -C2 'listOf\([^)]*"전체"'Length of output: 88
rg의 --type=kt 오류로 검색이 실패했습니다. 확장자 필터 방식으로 재검사합니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching .kt files for GroupFilteredSearchResult calls..."
rg -n -C3 '\bGroupFilteredSearchResult\(' -g '**/*.kt' || true
echo; echo 'Searching for GroupFilteredSearchResult calls that include the literal "전체"...'
rg -n -C3 'GroupFilteredSearchResult\([^)]*"전체"[^)]*\)' -g '**/*.kt' || true
echo; echo 'Searching for listOf(... "전체" ...) in .kt files (may be false positives)...'
rg -n -C2 'listOf\([^)]*"전체"[^)]*\)' -g '**/*.kt' || true
echo; echo 'Searching for any literal "전체" in .kt files (broad search)...'
rg -n -C1 '"전체"' -g '**/*.kt' || trueLength of output: 5093
중복 '전체' 제거 및 로케일 변경 반영
Preview 호출에서 "전체"를 직접 전달해 중복 인덱스 오류와 로케일 변경 미반영 문제가 발생합니다. allChipText를 remember 키에 포함하고 전달된 genres에서 동일 텍스트를 제거하세요.
- val allChipText = stringResource(id = R.string.all)
- val chipList = remember(genres) { listOf(allChipText) + genres }
+ val allChipText = stringResource(id = R.string.all)
+ // 로케일 변화(allChipText 변경)에 반응하고, 호출 측에 "전체"가 포함돼도 중복 제거
+ val chipList = remember(allChipText, genres) {
+ val filtered = genres.filterNot { it == allChipText }
+ listOf(allChipText) + filtered
+ }발견된 호출 예:
- app/src/main/java/com/texthip/thip/ui/group/search/component/GroupFilteredSearchResult.kt:211 (listOf("전체", ...))
- app/src/main/java/com/texthip/thip/ui/group/search/screen/GroupSearchScreen.kt:171 (genres = genreDisplayNames)
📝 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.
| val allChipText = stringResource(id = R.string.all) | |
| val chipList = remember(genres) { listOf(allChipText) + genres } | |
| val allChipText = stringResource(id = R.string.all) | |
| // 로케일 변화(allChipText 변경)에 반응하고, 호출 측에 "전체"가 포함돼도 중복 제거 | |
| val chipList = remember(allChipText, genres) { | |
| val filtered = genres.filterNot { it == allChipText } | |
| listOf(allChipText) + filtered | |
| } |
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/search/component/GroupFilteredSearchResult.kt
around lines 50–52, the code creates chipList without accounting for locale
changes and can duplicate the "전체" item; update the remember call to include
allChipText as a key and build chipList by prepending allChipText to a genres
list that has any identical text removed (e.g., genres.filterNot { it ==
allChipText }), and update preview/call sites (where "전체" is passed literally)
to stop injecting the localized "all" string so callers supply only genre items.
| text = stringResource(R.string.look_around_all_rooms_title), | ||
| color = colors.White, | ||
| style = typography.smalltitle_sb600_s18_h24 | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
리소스 키/아이콘 존재 여부 확인
look_around_all_rooms_title, ic_chevron 리소스가 정상 추가/배치되었는지 확인 바랍니다.
Also applies to: 88-91
🏁 Script executed:
#!/bin/bash
rg -n --glob '!**/build/**' '<string name="look_around_all_rooms_title"|ic_chevron' app/src/mainLength of output: 0
추가 검증 스크립트 실행합니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "검색: 리소스 키와 드로어블 이름을 전체 저장소에서 찾습니다..."
rg -n --hidden --no-ignore-vcs --glob '!**/build/**' -S 'look_around_all_rooms_title' || true
rg -n --hidden --no-ignore-vcs --glob '!**/build/**' -S 'ic_chevron' || true
echo "검색: 일반적인 Android 참조 패턴 검사(@string/@drawable/R.string/R.drawable)..."
rg -n --hidden --no-ignore-vcs --glob '!**/build/**' -S 'R\.string\.look_around_all_rooms_title|@string/look_around_all_rooms_title' || true
rg -n --hidden --no-ignore-vcs --glob '!**/build/**' -S 'R\.drawable\.ic_chevron|@drawable/ic_chevron|@mipmap/ic_chevron' || true
echo "strings.xml 파일 목록(최대 10개)과 각 파일 상위 200라인 출력..."
find . -type f -path '*/res/values*/strings*.xml' | head -n 10 | while read -r f; do
echo "===> $f"
sed -n '1,200p' "$f"
done || true
echo "drawable / mipmap 경로에서 ic_chevron 파일 목록 출력..."
find . -type f \( -path '*/res/drawable*/*ic_chevron*' -o -path '*/res/mipmap*/*ic_chevron*' \) -print || true
echo "검사 완료."Length of output: 10007
리소스 확인 — look_around_all_rooms_title 누락, ic_chevron 존재
- ic_chevron drawable 확인: app/src/main/res/drawable/ic_chevron.xml (및 ic_chevron_left.xml, ic_chevron_right.xml) 존재.
- look_around_all_rooms_title 문자열 리소스 미발견: app/src/main/res/values/strings.xml에서 해당 키가 없음. strings.xml에 키 추가하거나 코드에서 사용중인 리소스 키를 수정하세요.
- 사용 위치: app/src/main/java/com/texthip/thip/ui/group/search/component/GroupRecentSearch.kt (라인 82–85, 88–91).
🤖 Prompt for AI Agents
In
app/src/main/java/com/texthip/thip/ui/group/search/component/GroupRecentSearch.kt
around lines 82–85 (and also review nearby usage at 88–91), the code references
stringResource(R.string.look_around_all_rooms_title) but that key is missing
from app/src/main/res/values/strings.xml; either add a new entry named
look_around_all_rooms_title to strings.xml with the appropriate localized text,
or change the reference here to an existing string resource key; after updating,
rebuild to ensure no missing resource errors and verify any other uses of this
key in the file are consistent.
There was a problem hiding this comment.
전체 모임방을 볼 수 있는 버튼이 생겼는데 이 컴포넌트와 동일해서 string을 받을 수 있도록 했고, onClick을 추가했습니다 !
There was a problem hiding this comment.
여기는 또 어떤게 수정된건가요? 최근 검색어 보여주는 곳 아닌감
There was a problem hiding this comment.
맞습니두 여기에서도 전체 모임방을 볼 수 있는 버튼이 추가돼서 해당 버튼 추가했습니닷
➕ 이슈 링크
🔎 작업 내용
📸 스크린샷
😢 해결하지 못한 과제
[] TASK
📢 리뷰어들에게
Summary by CodeRabbit
New Features
UI Changes