Conversation
|
Need an answer fast? Review this PR in Change Stack to ask focused questions about the PR or a changed range. Warning Review limit reached
More reviews will be available in 58 minutes and 15 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
Walkthrough최근 검색 목록 조회·삭제 및 인기 키워드 검색 기능이 일반 탐색 화면에 추가되었습니다. API 엔드포인트에서 데이터를 수신하여 엔티티, UI 모델로 변환하고, RecyclerView 어댑터로 렌더링한 후 Activity에서 사용자 상호작용을 처리하는 전체 데이터 흐름이 구현되었습니다. Changes최근 검색 및 인기 키워드 탐색
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/main/java/com/into/websoso/ui/normalExplore/NormalExploreViewModel.kt (1)
122-164:⚠️ Potential issue | 🟠 Major | ⚡ Quick win검색 초기화 후 이전 요청 응답이 UI를 다시 덮어쓸 수 있습니다.
Line 170-178에서 초기화해도, 이미 시작된 Line 131 코루틴이 나중에 성공하면 Line 137-150에서
_uiState가 다시 갱신됩니다. X 버튼으로 초기 상태로 돌아간 직후 이전 검색 결과가 재노출될 수 있습니다.수정 예시
+import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ class NormalExploreViewModel @@ ) : ViewModel() { + private var searchJob: Job? = null @@ fun updateSearchResult(isSearchButtonClick: Boolean) { @@ - viewModelScope.launch { + val requestedWord = searchWord.value.orEmpty() + searchJob?.cancel() + searchJob = viewModelScope.launch { _uiState.value = _uiState.value?.copy(loading = isSearchButtonClick) runCatching { - getNormalExploreResultUseCase(searchWord.value ?: "", isSearchButtonClick) + getNormalExploreResultUseCase(requestedWord, isSearchButtonClick) }.onSuccess { results -> + if (requestedWord != searchWord.value.orEmpty()) return@onSuccess if (results.novels.isNotEmpty()) { @@ fun updateSearchWordEmpty() { + searchJob?.cancel() _searchWord.value = "" savedStateHandle[SEARCH_AUTHOR] = "" _uiState.value = NormalExploreUiState()Also applies to: 170-178
🤖 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/com/into/websoso/ui/normalExplore/NormalExploreViewModel.kt` around lines 122 - 164, The search coroutine started in updateSearchResult can finish after UI was reset and overwrite state; fix by making searches cancellable and ensuring only the latest result updates UI: add a cancellable reference (e.g., a private var currentSearchJob: Job?) in the ViewModel, cancel currentSearchJob before launching the new viewModelScope.launch in updateSearchResult, assign the launched Job to currentSearchJob, and optionally check isActive or a captured request token (nonce) before applying the results to _uiState and before calling fetchRecentSearchesInternal so stale responses cannot overwrite the cleared/initial state.
🧹 Nitpick comments (1)
app/src/main/java/com/into/websoso/ui/normalExplore/NormalExploreViewModel.kt (1)
87-115: ⚡ Quick win최근 검색/키워드 요청 실패가 무음으로 처리됩니다.
Line 94-113, Line 182-200에서
runCatching의onFailure가 없어 실패 시 UI 피드백/재시도 트리거가 없습니다. 최소한 에러 상태 노출(토스트/스낵바 이벤트 또는 로그) 처리를 추가하는 편이 안정적입니다.Also applies to: 180-202
🤖 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/com/into/websoso/ui/normalExplore/NormalExploreViewModel.kt` around lines 87 - 115, The runCatching calls in fetchRecentSearchesInternal (invoked by fetchRecentSearches) and in fetchKeywordSearches swallow failures because they lack onFailure handlers; add onFailure blocks to both so errors are logged and the UI is notified (e.g., emit a single‑event LiveData/StateFlow like _errorEvent or _snackbarEvent with a message and/or set an error state to enable retry), and optionally trigger retry logic; ensure you reference the existing symbols _recentSearches, updateRecentSearchesVisibility, _keywordSearches, updateKeywordSearchesVisibility, and the functions fetchRecentSearchesInternal and fetchKeywordSearches when wiring the error handling.
🤖 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 `@app/src/main/java/com/into/websoso/data/mapper/KeywordMapper.kt`:
- Around line 12-13: The function PopularKeywordsResponseDto.toData currently
places the expression body on the next line causing a ktlint
standard:function-signature violation; fix it by moving the expression to the
same line as the function signature so the declaration reads as a single-line
expression body (e.g., adjust the toData function that returns
List<CategoriesEntity.CategoryEntity.KeywordEntity> so the "= keywords.map {
it.toData() }" is on the same line as the function signature).
In `@app/src/main/java/com/into/websoso/data/repository/KeywordRepository.kt`:
- Around line 15-16: 함수 fetchPopularKeywords의 expression-body 위치가 ktlint의
standard:function-signature 규칙을 위반하므로 빌드가 깨집니다; 함수 시그니처 끝난 직후에 '='와 구현을 같은 라인에
두지 말고 expression body 시작 위치를 규칙에 맞게 조정하여 '='을 시그니처 다음 줄로 옮기고 들여쓰기를 맞춰
keywordApi.getPopularKeywords().toData() 호출이 새 줄에서 올바르게 정렬되도록 수정하세요.
In `@app/src/main/res/layout/item_normal_explore_recent_search.xml`:
- Around line 34-41: The delete ImageView
(iv_normal_explore_recent_search_delete) lacks an accessibility label, so
TalkBack users can't know its purpose; update the layout or adapter to provide a
meaningful contentDescription (e.g., use a string resource like
R.string.accessibility_delete_recent_search) and ensure the view is announced as
actionable in RecentSearchAdapter where ivNormalExploreRecentSearchDelete is
wired (set contentDescription programmatically if dynamic, and confirm it
remains focusable/clickable for accessibility).
---
Outside diff comments:
In
`@app/src/main/java/com/into/websoso/ui/normalExplore/NormalExploreViewModel.kt`:
- Around line 122-164: The search coroutine started in updateSearchResult can
finish after UI was reset and overwrite state; fix by making searches
cancellable and ensuring only the latest result updates UI: add a cancellable
reference (e.g., a private var currentSearchJob: Job?) in the ViewModel, cancel
currentSearchJob before launching the new viewModelScope.launch in
updateSearchResult, assign the launched Job to currentSearchJob, and optionally
check isActive or a captured request token (nonce) before applying the results
to _uiState and before calling fetchRecentSearchesInternal so stale responses
cannot overwrite the cleared/initial state.
---
Nitpick comments:
In
`@app/src/main/java/com/into/websoso/ui/normalExplore/NormalExploreViewModel.kt`:
- Around line 87-115: The runCatching calls in fetchRecentSearchesInternal
(invoked by fetchRecentSearches) and in fetchKeywordSearches swallow failures
because they lack onFailure handlers; add onFailure blocks to both so errors are
logged and the UI is notified (e.g., emit a single‑event LiveData/StateFlow like
_errorEvent or _snackbarEvent with a message and/or set an error state to enable
retry), and optionally trigger retry logic; ensure you reference the existing
symbols _recentSearches, updateRecentSearchesVisibility, _keywordSearches,
updateKeywordSearchesVisibility, and the functions fetchRecentSearchesInternal
and fetchKeywordSearches when wiring the error handling.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 02838cbb-cc90-44a3-9a32-1ea9e7c664bf
📒 Files selected for processing (23)
app/src/main/java/com/into/websoso/data/mapper/KeywordMapper.ktapp/src/main/java/com/into/websoso/data/mapper/NovelMapper.ktapp/src/main/java/com/into/websoso/data/model/RecentSearchesEntity.ktapp/src/main/java/com/into/websoso/data/remote/api/KeywordApi.ktapp/src/main/java/com/into/websoso/data/remote/api/NovelApi.ktapp/src/main/java/com/into/websoso/data/remote/response/PopularKeywordsResponseDto.ktapp/src/main/java/com/into/websoso/data/remote/response/RecentSearchesResponseDto.ktapp/src/main/java/com/into/websoso/data/repository/KeywordRepository.ktapp/src/main/java/com/into/websoso/data/repository/NovelRepository.ktapp/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultViewModel.ktapp/src/main/java/com/into/websoso/ui/mapper/NovelMapper.ktapp/src/main/java/com/into/websoso/ui/normalExplore/NormalExploreActivity.ktapp/src/main/java/com/into/websoso/ui/normalExplore/NormalExploreViewModel.ktapp/src/main/java/com/into/websoso/ui/normalExplore/adapter/GenreSearchAdapter.ktapp/src/main/java/com/into/websoso/ui/normalExplore/adapter/RecentSearchAdapter.ktapp/src/main/java/com/into/websoso/ui/normalExplore/model/GenreSearchModel.ktapp/src/main/java/com/into/websoso/ui/normalExplore/model/NormalExploreModel.ktapp/src/main/res/drawable/bg_normal_explore_recent_search_chip.xmlapp/src/main/res/drawable/ic_normal_explore_recent_search_delete.xmlapp/src/main/res/layout/activity_normal_explore.xmlapp/src/main/res/layout/item_normal_explore_genre_search.xmlapp/src/main/res/layout/item_normal_explore_recent_search.xmlcore/resource/src/main/res/values/strings.xml
| suspend fun fetchPopularKeywords(): List<CategoriesEntity.CategoryEntity.KeywordEntity> = | ||
| keywordApi.getPopularKeywords().toData() |
There was a problem hiding this comment.
추가된 메서드의 시그니처 포맷 때문에 빌드가 깨집니다.
Line 15의 함수 시그니처가 ktlint standard:function-signature 규칙을 위반해 CI가 실패합니다. 동일 규칙에 맞게 expression body 시작 위치를 조정해 주세요.
수정 예시
- suspend fun fetchPopularKeywords(): List<CategoriesEntity.CategoryEntity.KeywordEntity> =
- keywordApi.getPopularKeywords().toData()
+ suspend fun fetchPopularKeywords():
+ List<CategoriesEntity.CategoryEntity.KeywordEntity> = keywordApi.getPopularKeywords().toData()📝 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.
| suspend fun fetchPopularKeywords(): List<CategoriesEntity.CategoryEntity.KeywordEntity> = | |
| keywordApi.getPopularKeywords().toData() | |
| suspend fun fetchPopularKeywords(): | |
| List<CategoriesEntity.CategoryEntity.KeywordEntity> = keywordApi.getPopularKeywords().toData() |
🧰 Tools
🪛 GitHub Actions: Android CI / 0_build.txt
[error] 15-15: ktlint error (standard:function-signature): First line of body expression fits on same line as function signature (standard:function-signature)
🪛 GitHub Actions: Android CI / build
[error] 15-15: ktlint (standard:function-signature): First line of body expression fits on same line as function signature.
🤖 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/com/into/websoso/data/repository/KeywordRepository.kt`
around lines 15 - 16, 함수 fetchPopularKeywords의 expression-body 위치가 ktlint의
standard:function-signature 규칙을 위반하므로 빌드가 깨집니다; 함수 시그니처 끝난 직후에 '='와 구현을 같은 라인에
두지 말고 expression body 시작 위치를 규칙에 맞게 조정하여 '='을 시그니처 다음 줄로 옮기고 들여쓰기를 맞춰
keywordApi.getPopularKeywords().toData() 호출이 새 줄에서 올바르게 정렬되도록 수정하세요.
Source: Pipeline failures
| <ImageView | ||
| android:id="@+id/iv_normal_explore_recent_search_delete" | ||
| android:layout_width="16dp" | ||
| android:layout_height="16dp" | ||
| android:layout_marginStart="6dp" | ||
| android:contentDescription="@null" | ||
| android:padding="4dp" | ||
| android:src="@drawable/ic_normal_explore_recent_search_delete" /> |
There was a problem hiding this comment.
삭제 아이콘의 접근성 레이블이 누락되었습니다.
RecentSearchAdapter에서 ivNormalExploreRecentSearchDelete에 클릭 리스너를 직접 연결하고 있는데, 현재 contentDescription="@null"이라 TalkBack 사용자는 이 버튼의 의미를 알 수 없습니다.
수정 제안
--- a/app/src/main/res/layout/item_normal_explore_recent_search.xml
+++ b/app/src/main/res/layout/item_normal_explore_recent_search.xml
@@
<ImageView
android:id="@+id/iv_normal_explore_recent_search_delete"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginStart="6dp"
- android:contentDescription="`@null`"
+ android:contentDescription="`@string/normal_explore_recent_search_delete`"
android:padding="4dp"
android:src="`@drawable/ic_normal_explore_recent_search_delete`" />--- a/core/resource/src/main/res/values/strings.xml
+++ b/core/resource/src/main/res/values/strings.xml
@@
<string name="normal_explore_recent_search_delete_all">전체삭제</string>
+ <string name="normal_explore_recent_search_delete">최근 검색어 삭제</string>
<string name="normal_explore_genre_search_title">장르별 검색</string>📝 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.
| <ImageView | |
| android:id="@+id/iv_normal_explore_recent_search_delete" | |
| android:layout_width="16dp" | |
| android:layout_height="16dp" | |
| android:layout_marginStart="6dp" | |
| android:contentDescription="@null" | |
| android:padding="4dp" | |
| android:src="@drawable/ic_normal_explore_recent_search_delete" /> | |
| <ImageView | |
| android:id="@+id/iv_normal_explore_recent_search_delete" | |
| android:layout_width="16dp" | |
| android:layout_height="16dp" | |
| android:layout_marginStart="6dp" | |
| android:contentDescription="`@string/normal_explore_recent_search_delete`" | |
| android:padding="4dp" | |
| android:src="`@drawable/ic_normal_explore_recent_search_delete`" /> |
| <ImageView | |
| android:id="@+id/iv_normal_explore_recent_search_delete" | |
| android:layout_width="16dp" | |
| android:layout_height="16dp" | |
| android:layout_marginStart="6dp" | |
| android:contentDescription="@null" | |
| android:padding="4dp" | |
| android:src="@drawable/ic_normal_explore_recent_search_delete" /> | |
| <string name="normal_explore_recent_search_delete_all">전체삭제</string> | |
| <string name="normal_explore_recent_search_delete">최근 검색어 삭제</string> | |
| <string name="normal_explore_genre_search_title">장르별 검색</string> |
🤖 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/res/layout/item_normal_explore_recent_search.xml` around lines
34 - 41, The delete ImageView (iv_normal_explore_recent_search_delete) lacks an
accessibility label, so TalkBack users can't know its purpose; update the layout
or adapter to provide a meaningful contentDescription (e.g., use a string
resource like R.string.accessibility_delete_recent_search) and ensure the view
is announced as actionable in RecentSearchAdapter where
ivNormalExploreRecentSearchDelete is wired (set contentDescription
programmatically if dynamic, and confirm it remains focusable/clickable for
accessibility).
📌𝘐𝘴𝘴𝘶𝘦𝘴
📎𝘞𝘰𝘳𝘬 𝘋𝘦𝘴𝘤𝘳𝘪𝘱𝘵𝘪𝘰𝘯
/keywords/popularAPI를 연결하고, 키워드 칩 클릭 시 선택한 키워드 ID로 상세 탐색 결과 화면에 진입하도록 구현했습니다.📷𝘚𝘤𝘳𝘦𝘦𝘯𝘴𝘩𝘰𝘵
default.mp4
💬𝘛𝘰 𝘙𝘦𝘷𝘪𝘦𝘸𝘦𝘳𝘴
git diff --check,./gradlew ktlintCheck,./gradlew :app:compileDebugKotlin확인했습니다.Summary by CodeRabbit
릴리스 노트