[Feat]: UI 채팅 목록 화면 구현 및 "채팅 목록 -> 채팅방 내비게이션 로직" 구현(#6)#22
Conversation
- 프로필, 이름, 메시지, 읽지 않은 메시지 수 등을 포함하는 ChatSummary 도메인 모델 추가 - ChattingScreen을 ChatListScreen으로 변경하고 빈 ChatScreen 스텁 추가 - 스와이프 제스처를 지원하는 ChatListItem 컴포넌트 추가 - 스와이프 상호작용을 위한 FrontRow 및 BackgroundRow 컴포넌트 추가 - SwipeableBox 및 UnReadMessageCountBox 유틸리티 컴포넌트 추가 - ic_chat_bell 및 ic_chat_pin 드로어블 리소스 추가 - 이름 변경에 맞춰 ChattingEntryBuilder가 ChatListScreen을 참조하도록 수정
- 채팅 목록 스켈레톤 애니메이션을 위한 LoadingColumn 컴포넌트 추가 - 클릭 시 리플 효과를 제거하는 noRippleClickable Modifier 추가 - 로딩 상태를 위한 shimmer 애니메이션 기반 skeleton Modifier 추가 - 새로운 로딩 컴포넌트를 적용하도록 ChatListScreen 업데이트
- FrontRow.kt: 메인 캐릭터 아이콘(`ic_chat_pin`)이 관리자(`isManager`)가 아닐 때만 노출되도록 조건식 수정 - FrontRow.kt: 읽지 않은 메시지 수(`UnReadMessageCountBox`) 상단의 불필요한 4.dp 간격(`Spacer`) 제거
- `ChatSummary` 데이터 클래스의 모든 프로퍼티에 기본값 추가 (매니저 채팅방 대응 등) - `ChatListScreen`의 메인 컨테이너에 `statusBarsPadding()`을 적용하여 상태 표시줄 겹침 방지 - `ChatListScreen`에서 매니저용 `FrontRow` 호출 시 `ChatSummary` 기본값을 활용하도록 코드 간소화 - `core/domain` 모듈 내 미사용 `.gitkeep` 파일 삭제
- ChattingRoute.kt: 채팅방 이동을 위한 `ChatRoomNavKey` 및 `ChatListNavKey` 정의 - ChatSummary.kt: `chatRoomId` 필드 타입을 `Int`에서 `Long`으로 변경 - ChatRoomScreen.kt: 채팅방 화면 구성을 위한 `ChatRoomScreen` 컴포저블 신규 생성 - ChattingEntryBuilder.kt: `chattingEntry`에 `ChatRoomNavKey` 진입점 추가 및 화면 이동을 위한 콜백 파라미터 정의 - ChatListScreen.kt: 채팅 아이템 클릭 이벤트를 처리할 수 있도록 `onChatRoomClick` 콜백 구현 및 데이터 바인딩 - AppScreen.kt: `AppNavigator`를 통해 실제 채팅방 이동(`navigate`) 및 뒤로가기(`popBackStack`) 로직 연결 - AppNavigator.kt: 불필요한 주석 제거 및 내비게이션 관련 TODO 문구 정리
- ChatListIntent: 채팅방 클릭 액션을 정의하는 ClickChatRoom 추가 - ChatListSideEffect: 화면 이동 처리를 위한 NavigateToChatRoom 추가 - ChatListViewModel: ClickChatRoom 인텐트 수신 시 NavigateToChatRoom 사이드 이펙트를 방출하도록 구현 - ChatListScreen: collectSideEffect를 통해 채팅방 이동 로직을 처리하고, 클릭 이벤트를 ViewModel로 전달하도록 수정 - ChattingRoute: 사용하지 않는 ChatListNavKey 제거
- ChatListScreen.kt: ViewModel의 상태 구독(state) 로직에 설명 주석 추가 - ChatListScreen.kt: SideEffect 수신 및 처리 로직에 설명 주석 추가
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough채팅 요약·메시지 도메인 모델과 저장소 구현, 채팅 목록 UI 및 상태 관리, 채팅방 진입 라우팅, Compose 디자인 컴포넌트와 채팅방 기반 구조가 추가되었습니다. Changes채팅 기능
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant ChatListScreen
participant ChatListViewModel
participant ChatRepository
participant AppScreen
participant ChatRoomScreen
User->>ChatListScreen: 채팅방 항목 클릭
ChatListScreen->>ChatListViewModel: ClickChatRoom(roomId, name)
ChatListViewModel->>ChatRepository: getChatList()
ChatListViewModel-->>ChatListScreen: NavigateToChatRoom(roomId, name)
ChatListScreen->>AppScreen: 채팅방 이동 콜백
AppScreen->>ChatRoomScreen: ChatRoomNavKey로 roomId, name 전달
Suggested reviewers: 🚥 Pre-merge checks | ✅ 2 | ❌ 5❌ Failed checks (5 warnings)
✅ Passed checks (2 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Gemini AI 코드리뷰안녕하세요, 시니어 Android 개발자로서 코드리뷰를 진행하겠습니다. 전반적으로 MVI 아키텍처와 Compose, Hilt 등 최신 기술 스택을 잘 활용하고 계시며, 각 레이어의 책임 분리도 명확하게 잘 되어 있습니다. 특히 Compose 컴포넌트의 재사용성, 성능(recomposition 방지), 접근성 고려 등 많은 부분에서 좋은 관행을 따르고 있습니다. 몇 가지 개선 사항과 TODO 항목에 대한 제안을 드립니다. 1.
|
Gemini AI 코드리뷰안녕하세요, 시니어 Android 개발자입니다. PR 잘 봤습니다. 전반적으로 코드 구조와 Jetpack Compose, Orbit MVI 패턴을 잘 적용하려는 노력이 돋보입니다. 특히 LoadStatus를 사용하여 로딩 상태를 관리하고, LazyColumn에 key를 지정한 점, 몇 가지 개선 사항과 피드백을 드리니 참고하여 반영해주시면 좋겠습니다. General Feedback
Kotlin Code Review
Jetpack Compose Code Review
Repository/DataSource 레이어
ViewModel Review
FCM/SSE/실시간 통신 관련 코드
추가 제안사항:
전반적으로 매우 깔끔하고 모범적인 코드를 작성해주셨습니다. 위 피드백을 반영하여 더욱 견고하고 유지보수하기 쉬운 코드를 만들어주세요! 수고 많으셨습니다. |
Gemini AI 코드리뷰안녕하세요! GitHub PR 리뷰 요청 주셔서 감사합니다. 시니어 Android 개발자의 관점에서 PR diff를 꼼꼼히 살펴보았습니다. 전반적으로 Jetpack Compose, Hilt, Orbit MVI 패턴을 잘 적용하여 현대적인 아키텍처를 구성하려는 노력이 돋보입니다. 특히 몇 가지 개선점을 제안하며 코드 리뷰를 시작하겠습니다. PR 종합 의견이번 PR은 채팅 기능의 핵심 UI, 도메인 모델, 데이터 레이어, 그리고 ViewModel을 성공적으로 도입했습니다. Jetpack Compose의 효율적인 recomposition을 위한 다만, 대부분의 UI 컴포넌트에서 하드코딩된 문자열과 매직 넘버가 많이 발견됩니다. 이는 향후 다국어 지원, 디자인 시스템의 일관성 유지, 그리고 코드 변경 용이성 측면에서 개선이 필요합니다. 또한, 데이터 타입의 일관성 및 에러 핸들링 부분도 보완하면 더욱 견고한 코드가 될 것입니다. 세부 코드 리뷰1. Kotlin 코드 리뷰
2. Jetpack Compose 코드 확인
3. Repository/DataSource 레이어 확인
4. ViewModel 확인
5. FCM/SSE/실시간 통신 관련 코드 확인
정리 및 최종 의견이번 PR은 기술적으로 매우 잘 구현된 상태이며, 현대 Android 개발 스택과 아키텍처 패턴을 모범적으로 적용하고 있습니다. 특히 Jetpack Compose와 Orbit MVI의 활용이 인상 깊습니다. 가장 시급한 개선 사항은 하드코딩된 문자열과 매직 넘버를 String Resource 및 상수로 분리하는 것입니다. 그 다음으로는 이러한 개선 사항들을 반영한다면, 더욱 유지보수하기 좋고 확장 가능한 코드가 될 것입니다. 수고 많으셨습니다! |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (6)
core/domain/src/main/java/kr/co/call/domain/model/chatting/ChatSummary.kt (1)
20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift표시용 시간 문자열을 도메인 모델에서 분리해 주세요.
whenSubmitted에"오후 3:21","어제"같은 로케일·상대시간 문자열을 저장하면 도메인 계층이 한국어 UI 포맷에 결합되고, 다국어·시간대·정렬 처리가 어려워집니다. 원시 timestamp를 보관한 뒤 UI 계층에서 포맷하세요. 현재ChatRepositoryImpl.ktLine 22, Line 31, Line 40, Line 49에서도 같은 형식이 직접 주입됩니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@core/domain/src/main/java/kr/co/call/domain/model/chatting/ChatSummary.kt` at line 20, ChatSummary의 whenSubmitted를 로케일·상대시간 표시 문자열 대신 원시 timestamp 타입으로 변경하고, ChatRepositoryImpl의 해당 생성 지점들에서도 포맷된 문자열이 아닌 timestamp 값을 전달하세요. 표시용 시간 포맷팅은 도메인 계층에서 제거해 UI 계층이 담당하도록 유지하세요.feature/chatting/impl/src/main/java/kr/co/call/impl/component/UnReadMessageCountBox.kt (1)
20-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value함수명을 lower camelCase로 바꾸세요.
UnReadMessageCountBox는 함수명 규칙에 맞지 않습니다.unreadMessageCountBox로 변경하고FrontRow.kt의 호출도 함께 갱신하세요.As per path instructions, “함수/변수 camelCase.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/chatting/impl/src/main/java/kr/co/call/impl/component/UnReadMessageCountBox.kt` around lines 20 - 22, Rename the composable function UnReadMessageCountBox to unreadMessageCountBox to follow lower camelCase naming, and update its invocation in FrontRow.kt accordingly. Ensure all references use the new function name.Source: Path instructions
feature/chatting/impl/src/main/java/kr/co/call/impl/component/FrontRow.kt (1)
35-40: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win재사용 컴포저블의 Modifier 계약을 통일하세요.
모든 재사용 컴포저블은 호출자가 전달하는
Modifier를 첫 인자로 받아야 하며, 기본값을 두지 않아야 합니다.
feature/chatting/impl/src/main/java/kr/co/call/impl/component/FrontRow.kt#L35-L40:modifier기본값을 제거하고 모든 호출 지점에서 명시적으로 전달하세요.feature/chatting/impl/src/main/java/kr/co/call/impl/component/UnReadMessageCountBox.kt#L19-L22: 첫 인자로 기본값 없는modifier: Modifier를 추가해 루트Box에 연결하세요.As per path instructions, “Modifier 파라미터 위치(파라미터 앞쪽) 및 기본값 없이 전달 여부.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/chatting/impl/src/main/java/kr/co/call/impl/component/FrontRow.kt` around lines 35 - 40, 재사용 컴포저블의 Modifier 계약을 통일하세요. FrontRow의 modifier를 첫 번째 파라미터로 유지하되 기본값을 제거하고 모든 호출부에서 명시적으로 전달하세요. UnReadMessageCountBox에는 기본값 없는 modifier: Modifier를 첫 번째 파라미터로 추가하고 루트 Box에 연결하세요. 변경 대상은 FrontRow.kt 35-40행과 UnReadMessageCountBox.kt 19-22행입니다.Source: Path instructions
feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeState.kt (1)
3-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winenum 상수명을 UPPER_SNAKE_CASE로 변경하세요.
Closed와Opened는 프로젝트 규칙의 상수 명명 규칙을 따르지 않습니다.CLOSED,OPENED으로 변경하고SwipeableBox.kt의 앵커 참조도 함께 갱신하세요.수정 예시
enum class SwipeState { - Closed, - Opened + CLOSED, + OPENED, }As per path instructions,
상수 UPPER_SNAKE_CASE.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeState.kt` around lines 3 - 5, SwipeState enum constants currently use nonstandard casing. Rename Closed and Opened to CLOSED and OPENED, and update all corresponding anchor references in SwipeableBox.kt while preserving the existing state behavior.Source: Path instructions
feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.kt (1)
39-46: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win상태와 SideEffect를 수명주기 인지 방식으로 수집하세요.
현재 구현은 경로 규칙의
collectAsStateWithLifecycle및LaunchedEffect + collect패턴을 따르지 않습니다. 상태는 lifecycle-aware API로, SideEffect는 안정적인 key를 둔LaunchedEffect에서sideEffectFlow를 수집하도록 교체하세요.As per path instructions,
**/*Screen.kt: "Orbit MVI Container의 state는 collectAsStateWithLifecycle로, sideEffect는 LaunchedEffect + collect로 구독하는지."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.kt` around lines 39 - 46, Update ChatListScreen’s state collection to use collectAsStateWithLifecycle, and replace viewModel.collectSideEffect with a LaunchedEffect using a stable key that collects viewModel.sideEffectFlow. Preserve the existing NavigateToChatRoom handling and callback arguments inside the flow collector.Source: Path instructions
feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListViewModel.kt (1)
12-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winViewModel이 Repository 대신 UseCase에 의존하도록 변경하세요.
ChatListViewModel이ChatRepository를 직접 주입·호출합니다. 목록 조회 UseCase를core:domain에 두고 ViewModel은 상태 전환과 SideEffect 조정만 담당하게 분리하세요.As per path instructions,
**/*ViewModel.kt: "비즈니스 로직이 UseCase(core:domain) 호출로 위임되고 ViewModel에 과도하게 몰려있지 않은지 확인."Also applies to: 33-45
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListViewModel.kt` around lines 12 - 14, ChatListViewModel의 ChatRepository 직접 의존성을 제거하고 목록 조회 전용 UseCase를 core:domain에 추가해 주입·호출하도록 변경하세요. ViewModel은 UseCase 결과를 바탕으로 ChatListState 전환과 ChatListSideEffect 조정만 담당하게 하며, 관련 조회 비즈니스 로직은 UseCase로 이동하세요.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@core/data/src/main/java/kr/co/call/data/repositoryImpl/ChatRepositoryImpl.kt`:
- Around line 14-15: Update getChatList so CancellationException from delay is
rethrown rather than captured as Result.failure, while continuing to wrap
ordinary exceptions in the returned Result. Keep the existing delay and
repository behavior unchanged.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/component/ChatListItem.kt`:
- Around line 21-35: The modifier parameter is currently only applied to the
BackgroundRow child component instead of the SwipeableBox parent component,
which prevents the parent layout, accessibility, and click area from being
applied correctly. Pass the modifier directly to the SwipeableBox component in
the ChatListItem function to ensure the entire swipeable item receives the
intended layout and interaction handling from its parent.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeableBox.kt`:
- Around line 37-49: Update the LaunchedEffect in the SwipeableBox anchor-update
flow to key on swipeWidthPx in addition to its current trigger, so anchors are
recalculated whenever the swipe width or density-derived pixel value changes.
Keep the existing Closed and Opened anchor definitions unchanged.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/component/UnReadMessageCountBox.kt`:
- Around line 25-35: Update the unread-count container in the composable
containing the Box and Text so counts of three digits or more are not clipped.
Replace the fixed 20.dp sizing with a minimum size and horizontal padding that
allow the content to expand, or constrain the displayed count to the established
99+ representation while preserving the existing styling and alignment.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.kt`:
- Line 132: Remove the unsafe cast from the onChatRoomClick argument in the
Preview and provide a two-parameter no-op lambda that accepts the chat room ID
and name.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatRoomScreen.kt`:
- Around line 9-24: ChatRoomScreen과 ChatRoomScreenContent를 구현해 빈 화면을 제거하세요.
ChatRoomScreen에서 roomId와 name을 진입 Intent로 전달하고 Orbit MVI Container의 state는
collectAsStateWithLifecycle로, sideEffect는 LaunchedEffect에서 collect로 구독하세요. 수집한
state와 방 정보를 ChatRoomScreenContent에 전달해 실제 UI를 렌더링하고, 뒤로가기 side effect 및 앱 내
뒤로가기를 onBack에 연결하세요.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListViewModel.kt`:
- Around line 42-44: Update ChatListViewModel.kt lines 42-44 in the onFailure
callback to store the failure message in LoadStatus.Error. Update
ChatListScreen.kt lines 82-85 to render an error message and provide retry
behavior that re-runs the chat-list load operation.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatRoomViewModel.kt`:
- Around line 10-15: ChatRoomViewModel의 container 초기화에서 initialState의 TODO()를
제거하고 ChatRoomUiState의 기본 초기값을 사용하도록 변경하세요. 생성 시 예외가 발생하지 않도록 하며, 초기 데이터 로딩이
필요하다면 별도의 intent 블록에서 처리하세요.
---
Nitpick comments:
In `@core/domain/src/main/java/kr/co/call/domain/model/chatting/ChatSummary.kt`:
- Line 20: ChatSummary의 whenSubmitted를 로케일·상대시간 표시 문자열 대신 원시 timestamp 타입으로
변경하고, ChatRepositoryImpl의 해당 생성 지점들에서도 포맷된 문자열이 아닌 timestamp 값을 전달하세요. 표시용 시간
포맷팅은 도메인 계층에서 제거해 UI 계층이 담당하도록 유지하세요.
In `@feature/chatting/impl/src/main/java/kr/co/call/impl/component/FrontRow.kt`:
- Around line 35-40: 재사용 컴포저블의 Modifier 계약을 통일하세요. FrontRow의 modifier를 첫 번째
파라미터로 유지하되 기본값을 제거하고 모든 호출부에서 명시적으로 전달하세요. UnReadMessageCountBox에는 기본값 없는
modifier: Modifier를 첫 번째 파라미터로 추가하고 루트 Box에 연결하세요. 변경 대상은 FrontRow.kt 35-40행과
UnReadMessageCountBox.kt 19-22행입니다.
In `@feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeState.kt`:
- Around line 3-5: SwipeState enum constants currently use nonstandard casing.
Rename Closed and Opened to CLOSED and OPENED, and update all corresponding
anchor references in SwipeableBox.kt while preserving the existing state
behavior.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/component/UnReadMessageCountBox.kt`:
- Around line 20-22: Rename the composable function UnReadMessageCountBox to
unreadMessageCountBox to follow lower camelCase naming, and update its
invocation in FrontRow.kt accordingly. Ensure all references use the new
function name.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.kt`:
- Around line 39-46: Update ChatListScreen’s state collection to use
collectAsStateWithLifecycle, and replace viewModel.collectSideEffect with a
LaunchedEffect using a stable key that collects viewModel.sideEffectFlow.
Preserve the existing NavigateToChatRoom handling and callback arguments inside
the flow collector.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListViewModel.kt`:
- Around line 12-14: ChatListViewModel의 ChatRepository 직접 의존성을 제거하고 목록 조회 전용
UseCase를 core:domain에 추가해 주입·호출하도록 변경하세요. ViewModel은 UseCase 결과를 바탕으로
ChatListState 전환과 ChatListSideEffect 조정만 담당하게 하며, 관련 조회 비즈니스 로직은 UseCase로 이동하세요.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 83ac8cac-112a-4a04-8701-a5a7d10073f6
⛔ Files ignored due to path filters (1)
core/designsystem/src/main/res/drawable/ic_chat_manager.pngis excluded by!**/*.png
📒 Files selected for processing (44)
app/src/main/java/kr/co/call/callfromai/AppNavigator.ktapp/src/main/java/kr/co/call/callfromai/AppScreen.ktcore/data/src/main/java/kr/co/call/data/di/RepositoryModule.ktcore/data/src/main/java/kr/co/call/data/repositoryImpl/ChatRepositoryImpl.ktcore/designsystem/src/main/java/kr/co/call/designsystem/modifier/noRippleClickable.ktcore/designsystem/src/main/java/kr/co/call/designsystem/modifier/skeleton.ktcore/designsystem/src/main/res/drawable/ic_chat_back.xmlcore/designsystem/src/main/res/drawable/ic_chat_bell.xmlcore/designsystem/src/main/res/drawable/ic_chat_bell_off.xmlcore/designsystem/src/main/res/drawable/ic_chat_call.xmlcore/designsystem/src/main/res/drawable/ic_chat_pin.xmlcore/domain/src/main/java/kr/co/call/domain/model/chatting/.gitkeepcore/domain/src/main/java/kr/co/call/domain/model/chatting/ChatMessage.ktcore/domain/src/main/java/kr/co/call/domain/model/chatting/ChatSummary.ktcore/domain/src/main/java/kr/co/call/domain/repository/ChatRepository.ktcore/domain/src/main/java/kr/co/call/domain/repository/gitkeepfeature/chatting/api/src/main/java/kr/co/call/api/ChattingRoute.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/.gitkeepfeature/chatting/impl/src/main/java/kr/co/call/impl/component/BackgroundRow.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/ChatListItem.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/ChatRoomTopBar.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/FrontRow.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/LoadingColumn.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeState.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeableBox.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/UnReadMessageCountBox.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/entry/.gitkeepfeature/chatting/impl/src/main/java/kr/co/call/impl/entry/ChattingEntryBuilder.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/mapper/UiModelMapper.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/model/ChatItemUiModel.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/screen/.gitkeepfeature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatRoomScreen.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatScreen.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChattingScreen.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/.gitkeepfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListIntent.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListSideEffect.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListState.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListViewModel.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatRoomIntent.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatRoomSideEffect.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatRoomUiState.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatRoomViewModel.kt
💤 Files with no reviewable changes (2)
- core/domain/src/main/java/kr/co/call/domain/repository/gitkeep
- feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChattingScreen.kt
| override suspend fun getChatList(): Result<List<ChatSummary>> = runCatching { | ||
| delay(1000.milliseconds) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
git ls-files 'core/data/src/main/java/kr/co/call/data/repositoryImpl/ChatRepositoryImpl.kt' \
'core/domain/**' \
'core/data/**' | sed 's#^\./##' | sort
printf '\n--- ChatRepositoryImpl.kt ---\n'
cat -n core/data/src/main/java/kr/co/call/data/repositoryImpl/ChatRepositoryImpl.kt
printf '\n--- Search for getChatList usages ---\n'
rg -n "getChatList\(" core -S
printf '\n--- Search for runCatching + delay patterns ---\n'
rg -n "runCatching|CancellationException|delay\(1000\.milliseconds\)" core/data -SRepository: call-from-ai/CallFromAi_Android
Length of output: 4380
코루틴 취소 예외를 재전파하세요. core/data/src/main/java/kr/co/call/data/repositoryImpl/ChatRepositoryImpl.kt:14-15의 runCatching은 delay(1000.milliseconds)가 던지는 CancellationException까지 Result.failure로 바꿉니다. 화면 이탈이나 상위 작업 취소가 실패로 처리되지 않도록, 취소 예외는 그대로 던지고 일반 예외만 감싸세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@core/data/src/main/java/kr/co/call/data/repositoryImpl/ChatRepositoryImpl.kt`
around lines 14 - 15, Update getChatList so CancellationException from delay is
rethrown rather than captured as Result.failure, while continuing to wrap
ordinary exceptions in the returned Result. Keep the existing delay and
repository behavior unchanged.
| SwipeableBox( | ||
| swipeState = state, | ||
| backgroundContent = { | ||
| BackgroundRow( | ||
| modifier = modifier, | ||
| isAlarmEnabled = chatSummary.isAlarmEnabled, | ||
| onAlarmClick = onAlarmClick, | ||
| onDeleteClick = onDeleteClick | ||
| ) | ||
| }, | ||
| ) { | ||
| FrontRow( | ||
| chatSummary = chatSummary, | ||
| onClick = onItemClick | ||
| ) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "[1] Locate the target file and related SwipeableBox definition"
git ls-files | rg 'feature/chatting/impl/src/main/java/kr/co/call/impl/component/ChatListItem\.kt|SwipeableBox'
echo
echo "[2] Show the target file with line numbers"
cat -n feature/chatting/impl/src/main/java/kr/co/call/impl/component/ChatListItem.kt
echo
echo "[3] Search for SwipeableBox declaration and usages"
rg -n "fun\s+SwipeableBox|class\s+SwipeableBox|SwipeableBox\(" -S .Repository: call-from-ai/CallFromAi_Android
Length of output: 2631
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "[1] Show SwipeableBox file with line numbers"
cat -n feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeableBox.kt
echo
echo "[2] Show any modifier-related call sites in the component package"
rg -n "modifier\s*=|Modifier\." feature/chatting/impl/src/main/java/kr/co/call/impl/component -SRepository: call-from-ai/CallFromAi_Android
Length of output: 5179
modifier를 SwipeableBox에 전달하세요. feature/chatting/impl/src/main/java/kr/co/call/impl/component/ChatListItem.kt:21-35 현재 modifier가 BackgroundRow에만 붙어 있어, 부모가 기대한 전체 항목의 레이아웃/접근성/클릭 영역에 적용되지 않습니다.
수정 예시
SwipeableBox(
+ modifier = modifier,
swipeState = state,
backgroundContent = {
BackgroundRow(
- modifier = modifier,
isAlarmEnabled = chatSummary.isAlarmEnabled,📝 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.
| SwipeableBox( | |
| swipeState = state, | |
| backgroundContent = { | |
| BackgroundRow( | |
| modifier = modifier, | |
| isAlarmEnabled = chatSummary.isAlarmEnabled, | |
| onAlarmClick = onAlarmClick, | |
| onDeleteClick = onDeleteClick | |
| ) | |
| }, | |
| ) { | |
| FrontRow( | |
| chatSummary = chatSummary, | |
| onClick = onItemClick | |
| ) | |
| SwipeableBox( | |
| modifier = modifier, | |
| swipeState = state, | |
| backgroundContent = { | |
| BackgroundRow( | |
| isAlarmEnabled = chatSummary.isAlarmEnabled, | |
| onAlarmClick = onAlarmClick, | |
| onDeleteClick = onDeleteClick | |
| ) | |
| }, | |
| ) { | |
| FrontRow( | |
| chatSummary = chatSummary, | |
| onClick = onItemClick | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/component/ChatListItem.kt`
around lines 21 - 35, The modifier parameter is currently only applied to the
BackgroundRow child component instead of the SwipeableBox parent component,
which prevents the parent layout, accessibility, and click area from being
applied correctly. Pass the modifier directly to the SwipeableBox component in
the ChatListItem function to ensure the entire swipeable item receives the
intended layout and interaction handling from its parent.
| val swipeWidthPx = with(LocalDensity.current) { swipeWidth.toPx() } | ||
| val offsetX = swipeState.offset | ||
| val clampedOffset = if (offsetX.isNaN()) 0 else offsetX.roundToInt() | ||
| val backgroundAlpha = if (swipeWidthPx == 0f) 0f else (-clampedOffset / swipeWidthPx).coerceIn(0f, 1f) | ||
|
|
||
| LaunchedEffect(Unit) { | ||
| swipeState.updateAnchors( | ||
| DraggableAnchors { | ||
| SwipeState.Closed at 0f | ||
| SwipeState.Opened at -swipeWidthPx | ||
| } | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
ast-grep outline feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeableBox.kt --view expandedRepository: call-from-ai/CallFromAi_Android
Length of output: 263
🏁 Script executed:
sed -n '1,220p' feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeableBox.ktRepository: call-from-ai/CallFromAi_Android
Length of output: 2532
LaunchedEffect 키에 swipeWidthPx를 포함하세요.
지금은 밀도나 swipeWidth가 바뀌어도 앵커가 다시 계산되지 않아, 열림 위치와 배경 알파가 이전 값에 맞춰질 수 있습니다.
수정 예시
- LaunchedEffect(Unit) {
+ LaunchedEffect(swipeState, swipeWidthPx) {
swipeState.updateAnchors(📝 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 swipeWidthPx = with(LocalDensity.current) { swipeWidth.toPx() } | |
| val offsetX = swipeState.offset | |
| val clampedOffset = if (offsetX.isNaN()) 0 else offsetX.roundToInt() | |
| val backgroundAlpha = if (swipeWidthPx == 0f) 0f else (-clampedOffset / swipeWidthPx).coerceIn(0f, 1f) | |
| LaunchedEffect(Unit) { | |
| swipeState.updateAnchors( | |
| DraggableAnchors { | |
| SwipeState.Closed at 0f | |
| SwipeState.Opened at -swipeWidthPx | |
| } | |
| ) | |
| } | |
| val swipeWidthPx = with(LocalDensity.current) { swipeWidth.toPx() } | |
| val offsetX = swipeState.offset | |
| val clampedOffset = if (offsetX.isNaN()) 0 else offsetX.roundToInt() | |
| val backgroundAlpha = if (swipeWidthPx == 0f) 0f else (-clampedOffset / swipeWidthPx).coerceIn(0f, 1f) | |
| LaunchedEffect(swipeState, swipeWidthPx) { | |
| swipeState.updateAnchors( | |
| DraggableAnchors { | |
| SwipeState.Closed at 0f | |
| SwipeState.Opened at -swipeWidthPx | |
| } | |
| ) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeableBox.kt`
around lines 37 - 49, Update the LaunchedEffect in the SwipeableBox
anchor-update flow to key on swipeWidthPx in addition to its current trigger, so
anchors are recalculated whenever the swipe width or density-derived pixel value
changes. Keep the existing Closed and Opened anchor definitions unchanged.
| Box( | ||
| modifier = Modifier | ||
| .size(20.dp) | ||
| .background(color = CallTheme.colors.subRed, shape = shape), | ||
| contentAlignment = Alignment.Center, | ||
| ) { | ||
| Text( | ||
| text = unReadMessageCount, | ||
| style = CallTheme.typography.bodySmallBold, | ||
| color = CallTheme.colors.white, | ||
| ) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
세 자릿수 이상의 읽지 않은 메시지 수가 잘립니다.
20.dp 고정 폭에서는 "100" 이상을 온전히 표시할 수 없습니다. 최소 크기와 가로 패딩을 사용해 내용에 맞게 늘어나게 하거나, 표시 값을 99+로 제한하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/component/UnReadMessageCountBox.kt`
around lines 25 - 35, Update the unread-count container in the composable
containing the Box and Text so counts of three digits or more are not clipped.
Replace the fixed 20.dp sizing with a minimum size and horizontal padding that
allow the content to expand, or constrain the displayed count to the established
99+ representation while preserving the existing styling and alignment.
| private fun ChatListScreenPreview() { | ||
| CallFromAiTheme { | ||
| ChatListScreenContent( | ||
| onChatRoomClick = {} as (Long, String) -> Unit, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.kt"
echo "== file outline =="
ast-grep outline "$FILE" --view expanded || true
echo
echo "== relevant lines around 120-145 =="
sed -n '120,145p' "$FILE" | cat -nRepository: call-from-ai/CallFromAi_Android
Length of output: 1327
Preview의 unsafe cast를 제거하세요.
{}를 (Long, String) -> Unit으로 캐스팅하면 Preview에서 ClassCastException이 납니다. onChatRoomClick = { _, _ -> },로 바꾸면 됩니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.kt`
at line 132, Remove the unsafe cast from the onChatRoomClick argument in the
Preview and provide a two-parameter no-op lambda that accepts the chat room ID
and name.
Source: Path instructions
| fun ChatRoomScreen( | ||
| roomId: Long, | ||
| name: String, | ||
| modifier: Modifier = Modifier, | ||
| viewModel: ChatRoomViewModel = hiltViewModel(), | ||
| onBack: () -> Unit = {}, | ||
| ) { | ||
|
|
||
| } | ||
|
|
||
| @Composable | ||
| fun ChatRoomScreenContent( | ||
|
|
||
| ) { | ||
|
|
||
| } No newline at end of file |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
채팅방 라우트가 빈 화면으로 종료됩니다.
전달받은 roomId, name, onBack을 사용하지 않고 어떤 UI도 렌더링하지 않습니다. 현재 목록에서 방을 선택하면 빈 화면으로 이동하며 앱 내 뒤로가기 처리도 연결되지 않습니다. 진입 Intent에 방 정보를 전달하고, lifecycle-aware state 및 side effect를 구독한 뒤 실제 콘텐츠와 onBack 처리를 연결하세요.
As per path instructions, Orbit MVI Container의 state는 collectAsStateWithLifecycle로, sideEffect는 LaunchedEffect + collect로 구독하는지.
🧰 Tools
🪛 detekt (1.23.8)
[warning] 15-17: This empty block of code can be removed.
(detekt.empty-blocks.EmptyFunctionBlock)
[warning] 22-24: This empty block of code can be removed.
(detekt.empty-blocks.EmptyFunctionBlock)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatRoomScreen.kt`
around lines 9 - 24, ChatRoomScreen과 ChatRoomScreenContent를 구현해 빈 화면을 제거하세요.
ChatRoomScreen에서 roomId와 name을 진입 Intent로 전달하고 Orbit MVI Container의 state는
collectAsStateWithLifecycle로, sideEffect는 LaunchedEffect에서 collect로 구독하세요. 수집한
state와 방 정보를 ChatRoomScreenContent에 전달해 실제 UI를 렌더링하고, 뒤로가기 side effect 및 앱 내
뒤로가기를 onBack에 연결하세요.
Source: Path instructions
| onFailure = { | ||
|
|
||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the target files around the cited lines
sed -n '1,160p' feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListViewModel.kt
printf '\n----\n'
sed -n '1,180p' feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.ktRepository: call-from-ai/CallFromAi_Android
Length of output: 7135
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect state and status definitions used by ChatListViewModel / ChatListScreen
sed -n '1,200p' feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListState.kt
printf '\n----\n'
sed -n '1,200p' core/domain/src/main/java/kr/co/call/domain/util/LoadStatus.kt
printf '\n----\n'
sed -n '1,200p' core/domain/src/main/java/kr/co/call/domain/repository/ChatRepository.ktRepository: call-from-ai/CallFromAi_Android
Length of output: 951
채팅 목록 실패를 상태로 올려야 합니다.
onFailure가 비어 있어 실패 시 LoadStatus.Error로 전환되지 않고 Loading 상태가 계속 남습니다. 화면 쪽도 LoadStatus.Error 분기가 비어 있어 오류 안내나 재시도가 없습니다.
feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListViewModel.kt#L42-L44: 실패 메시지를 담아LoadStatus.Error(...)로 전환하세요.feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.kt#L82-L85: 오류 UI와 재시도 동작을 추가하세요.
📍 Affects 2 files
feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListViewModel.kt#L42-L44(this comment)feature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatListScreen.kt#L82-L85
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatListViewModel.kt`
around lines 42 - 44, Update ChatListViewModel.kt lines 42-44 in the onFailure
callback to store the failure message in LoadStatus.Error. Update
ChatListScreen.kt lines 82-85 to render an error message and provide retry
behavior that re-runs the chat-list load operation.
| class ChatRoomViewModel @Inject constructor( | ||
| private val chatRepository: ChatRepository | ||
| ): ViewModel(), ContainerHost<ChatRoomUiState, ChatRoomSideEffect> { | ||
| override val container: Container<ChatRoomUiState, ChatRoomSideEffect> = container( | ||
| initialState = TODO() | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate relevant files
fd -a 'ChatRoomViewModel.kt|ChatRoomScreen.kt' .
# Show file sizes
for f in $(fd -a 'ChatRoomViewModel.kt|ChatRoomScreen.kt' .); do
echo "== $f =="
wc -l "$f"
done
# Print relevant sections with line numbers
for f in $(fd -a 'ChatRoomViewModel.kt|ChatRoomScreen.kt' .); do
echo "===== $f ====="
cat -n "$f" | sed -n '1,220p'
doneRepository: call-from-ai/CallFromAi_Android
Length of output: 2386
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for Hilt annotations and ViewModel usage related to chat room
rg -n --hidden --glob '!**/build/**' --glob '!**/.gradle/**' '`@HiltViewModel`|hiltViewModel\(|ContainerHost<ChatRoomUiState, ChatRoomSideEffect>|TODO\(\)|initialState\s*=' .Repository: call-from-ai/CallFromAi_Android
Length of output: 1046
initialState의 TODO()를 실제 초기 상태로 교체하세요. ChatRoomViewModel은 생성 시 TODO() 때문에 바로 예외가 발생합니다. ChatRoomUiState의 기본값으로 바꾸고, 필요하면 초기 데이터 로딩은 intent {} 안에서 처리하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatRoomViewModel.kt`
around lines 10 - 15, ChatRoomViewModel의 container 초기화에서 initialState의 TODO()를
제거하고 ChatRoomUiState의 기본 초기값을 사용하도록 변경하세요. 생성 시 예외가 발생하지 않도록 하며, 초기 데이터 로딩이
필요하다면 별도의 intent 블록에서 처리하세요.
- SwipeState.kt: Enum 상수를 Kotlin 명명 규칙에 맞춰 Closed, Opened에서 대문자 형태인 CLOSED, OPENED로 변경 - ChatListItem.kt, SwipeableBox.kt: 변경된 SwipeState enum 상수를 반영하여 참조 코드 수정 - .gitignore: IDE 설정 파일인 .idea/planningMode.xml 추가 배제 설정 정의
Gemini AI 코드리뷰Gemini API 응답을 읽지 못했습니다. {
"error": {
"code": 503,
"message": "This model is currently experiencing high demand. Spikes in demand are usually temporary. Please try again later.",
"status": "UNAVAILABLE"
}
} |
There was a problem hiding this comment.
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/kr/co/call/callfromai/AppScreen.kt (2)
72-101: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winPR 설명에 변경 화면의 스크린샷 또는 녹화를 첨부하세요.
AppScreen의 BottomBar 레이아웃과 채팅방 이동 흐름이 변경됐지만, 제공된 PR 설명에는 시각 검증 자료가 없습니다. As per coding guidelines, “Jetpack Compose 화면 파일이 변경된 경우, PR 설명에 스크린샷 또는 화면 녹화가 첨부되었는지 확인하고 없으면 경고한다.”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/main/java/kr/co/call/callfromai/AppScreen.kt` around lines 72 - 101, AppScreen의 MainBottomBar 레이아웃과 chattingEntry의 ChatRoomNavKey 이동 흐름 변경을 검증할 수 있도록 PR 설명에 해당 화면의 스크린샷 또는 화면 녹화를 첨부하세요.Source: Coding guidelines
68-70: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
remember키에density도 포함하세요.
LocalDensity가 바뀌면 px 값이 같아도 이전 dp 값이 재사용될 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/main/java/kr/co/call/callfromai/AppScreen.kt` around lines 68 - 70, Update the remember key in the bottomBarPadding calculation to include density alongside bottomBarHeightPx, ensuring the dp value is recomputed when LocalDensity changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@app/src/main/java/kr/co/call/callfromai/AppScreen.kt`:
- Around line 72-101: AppScreen의 MainBottomBar 레이아웃과 chattingEntry의
ChatRoomNavKey 이동 흐름 변경을 검증할 수 있도록 PR 설명에 해당 화면의 스크린샷 또는 화면 녹화를 첨부하세요.
- Around line 68-70: Update the remember key in the bottomBarPadding calculation
to include density alongside bottomBarHeightPx, ensuring the dp value is
recomputed when LocalDensity changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: efb3b2be-704c-417d-a611-b8212ba1482f
📒 Files selected for processing (8)
.gitignoreapp/src/main/java/kr/co/call/callfromai/AppScreen.ktcore/data/src/main/java/kr/co/call/data/di/RepositoryModule.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/ChatListItem.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeState.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeableBox.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/screen/ChatRoomScreen.ktfeature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatRoomViewModel.kt
🚧 Files skipped from review as they are similar to previous changes (4)
- feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeState.kt
- feature/chatting/impl/src/main/java/kr/co/call/impl/component/ChatListItem.kt
- feature/chatting/impl/src/main/java/kr/co/call/impl/viewmodel/ChatRoomViewModel.kt
- feature/chatting/impl/src/main/java/kr/co/call/impl/component/SwipeableBox.kt
- .idea 디렉토리 내 개별 파일 설정을 `/.idea/` 전체 디렉토리 무시로 통합 - `/build` 경로를 `**/build/`로 변경하여 모든 하위 모듈의 빌드 결과물이 제외되도록 수정
Gemini AI 코드리뷰안녕하세요! PR 잘 올려주셨습니다. 이번 PR은 채팅 기능의 초기 구조와 UI 컴포넌트를 도입하는 중요한 작업이네요. 전반적으로 Orbit MVI 패턴과 Jetpack Compose의 모범 사례를 잘 따르려고 노력하신 점이 좋습니다. 새로운 기능이 추가될 때마다 체계적으로 접근하는 모습이 인상 깊습니다. 몇 가지 개선점과 질문 사항을 정리했으니, 확인하시고 반영해주시면 감사하겠습니다. 🔍 전반적인 코드 품질 및 변경 사항
🎯 Kotlin 코드 리뷰
🎨 Jetpack Compose 코드 리뷰
💾 Repository/DataSource 레이어 리뷰
🚀 ViewModel 리뷰
💡 기타 제안 사항
전반적으로 이 PR은 채팅 기능의 탄탄한 기반을 잘 다지고 있습니다. 위에 언급된 사항들을 참고하여 더욱 완성도 높은 코드를 만들어 주시면 감사하겠습니다. 수고 많으셨습니다! |
Gemini AI 코드리뷰안녕하세요! 시니어 안드로이드 개발자로서 이 PR을 리뷰하겠습니다. 전반적으로 코드 품질이 매우 좋고, Jetpack Compose, Hilt, Orbit MVI 패턴을 잘 적용하고 계신 것 같습니다. 특히 도메인 레이어와 UI 레이어의 분리, LoadStatus를 사용한 상태 관리 등이 인상 깊습니다. 몇 가지 개선할 점과 질문 사항이 있으니 아래 내용을 참고해주세요. 코드 리뷰 (Code Review)1. Kotlin 코드 리뷰
2. Jetpack Compose 코드 확인
3. Repository/DataSource 레이어 확인
4. ViewModel 확인
5. FCM/SSE/실시간 통신 관련 코드 확인
전반적인 의견 및 개선 제안
전반적으로 매우 좋은 PR이며, 제시된 개선점들은 코드의 견고함과 유지보수성을 더욱 높이는 데 도움이 될 것입니다. 수고하셨습니다! |
📄 작업 내용 요약
📎 Issue 번호
✅ 작업 목록
📝 기타 참고사항
Summary by CodeRabbit
새 기능
개선