Skip to content

feat: 탐색탭 제거 및 서재 아이콘 추가#876

Merged
m6z1 merged 10 commits into
developfrom
feat/875
May 10, 2026
Merged

feat: 탐색탭 제거 및 서재 아이콘 추가#876
m6z1 merged 10 commits into
developfrom
feat/875

Conversation

@m6z1
Copy link
Copy Markdown
Member

@m6z1 m6z1 commented May 1, 2026

📌𝘐𝘴𝘴𝘶𝘦𝘴

📎𝘞𝘰𝘳𝘬 𝘋𝘦𝘴𝘤𝘳𝘪𝘱𝘵𝘪𝘰𝘯

  • 탐색 탭 제거
  • 상세 탐색 평점 범위 슬라이더 추가
  • 서재 아이콘 내 검색으로 이동할 수 있는 아이콘 추가
  • 홈 화면 ㅇㅇㅇ님의 관심글 제거

📷𝘚𝘤𝘳𝘦𝘦𝘯𝘴𝘩𝘰𝘵

Screen_recording_20260501_143559.webm
Screen_recording_20260501_143408.webm

💬𝘛𝘰 𝘙𝘦𝘷𝘪𝘦𝘸𝘦𝘳𝘴

  • xml 정리하고 compose 로 마이그레이션 하다 보니 양이 많네요 호호 ,,,
  • 아직 상세 탐색 평점이 서버가 범위로 api 가 바뀌지 않은 상태라 그 부분 빼고 확인 부탁드려용
    -> 연결 완료

Summary by CodeRabbit

  • 새로운 기능

    • 상세 탐색 화면(정보/키워드 탭), 평점 범위 슬라이더, 선택형 칩, 검색 CTA 추가
    • 상세 탐색 전용 액티비티로 직접 진입 가능
  • 개선 사항

    • 홈에 '상세 검색' 카드 추가 및 바로가기 연결
    • 라이브러리 상단 플러스 아이콘으로 탐색 진입 연결
    • 평점 필터가 범위(최소/최대)로 변경되어 세밀한 필터링 가능
  • 제거

    • 기존 탐색(Explore) 탭과 관련된 UI/레이아웃(관심글·키워드·하위 시트 등) 및 관련 리소스 삭제

m6z1 added 6 commits May 1, 2026 13:42
- 홈 화면 검색바 아래 상세 탐색 진입 카드 추가
- DetailExploreDialogBottomSheet → DetailExploreActivity (Compose)로 전환
- 별점 기준을 단일값 → 0.0~5.0 범위 슬라이더로 변경
- 정보/키워드 탭, 커스텀 RangeSlider, S3 카테고리 이미지 연결
- ExploreFragment, fragment_explore.xml 삭제
- BottomNav 메뉴에서 menu_explore 항목 제거
- ic_main_explore drawable 및 main_ic_explore, explore_title, explore_detail_search_button 문자열 제거
- MainActivity FragmentType.EXPLORE 분기 제거
- 필터 버튼 클릭 시 finish() 처리로 변경하여 이전 DetailExploreActivity 상태를 그대로 사용
- DetailExploreResultDialogBottomSheet/InfoFragment/KeywordFragment 및 dialog/fragment xml 삭제
- DetailExploreResultViewModel에서 편집 관련 state 및 메서드 제거
- fragment_home.xml에서 관심글 섹션(빈/등록/추천) 3개 컨테이너 제거
- HomeFragment/HomeViewModel/HomeUiState에서 userInterestFeeds 관련 상태 및 메서드 제거
- UserInterestFeedAdapter/ViewHolder, item_user_interest_feed.xml 삭제
- FeedRepository.fetchUserInterestFeeds, FeedApi.getUserInterestFeeds, mapper, DTO, Entity 제거 (app + data/feed 모듈)
- 관심글 관련 string 리소스 정리
@m6z1 m6z1 added 🍯 [FEAT] 새로운 기능을 개발합니다. 🏹 궁사 명지 웹소소 공주의 은밀한 사냥생활 labels May 1, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@m6z1 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 23 minutes and 34 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bbd4e8a5-eafe-4bc1-a5cc-689ff94ed10b

📥 Commits

Reviewing files that changed from the base of the PR and between e24a563 and 8343f31.

📒 Files selected for processing (1)
  • app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreViewModel.kt

Walkthrough

탐색(Explore) 탭 및 user-interest 피드 관련 네트워크·모델·매퍼·레포지토리·프래그먼트/레이아웃을 제거하고, Compose 기반의 DetailExplore Activity/스크린·컴포넌트(RatingRangeSlider, SelectableChip 등), ViewModel/UseCase/리소스 및 홈 진입로(상세검색 카드)로 전환했습니다.

Changes

Detail Explore — 데이터형/API → 도메인 → UI 전환

Layer / File(s) Summary
Data Shape / API (삭제·변경)
.../remote/response/UserInterestFeedsResponseDto.kt, core/network/.../UserInterestFeedsResponseDto.kt, app/src/main/java/.../data/remote/api/FeedApi.kt, app/src/main/res/menu/menu_main_bnv.xml
UserInterestFeeds 관련 DTO(네트워크) 삭제 및 FeedApi의 getUserInterestFeeds() 제거; BottomNav의 menu_explore 항목 삭제.
Data Model / Mapper / Repository (삭제·변경)
.../data/model/UserInterestFeedsEntity.kt, .../data/mapper/FeedMapper.kt, .../data/repository/FeedRepository.kt, data/feed/.../UserInterestFeedsEntity.kt, data/feed/.../FeedMapper.kt
UserInterestFeeds 엔티티·메시지 enum 및 관련 toData() 매퍼 삭제; FeedRepository의 fetchUserInterestFeeds() 제거.
API 변경 — 필터(평점) 쿼리
app/src/main/java/com/into/websoso/data/remote/api/NovelApi.kt
getFilteredNovelResult 쿼리 파라미터: nullable novelRatingnovelRatingStartnovelRatingEnd(Float)로 변경.
Repository / UseCase 반영
app/src/main/java/com/into/websoso/data/repository/NovelRepository.kt, app/src/main/java/com/into/websoso/domain/usecase/GetDetailExploreResultUseCase.kt
NovelRepository와 UseCase가 평점 범위(start/end)를 요구하고 캐시 유효성 검사에 해당 범위를 포함하도록 변경.
ViewModel 변경 (DetailExplore / Result)
app/src/main/java/.../ui/detailExplore/DetailExploreViewModel.kt, .../ui/detailExploreResult/DetailExploreResultViewModel.kt
DetailExploreViewModel: nullable 단일 평점 → selectedRatingMin/selectedRatingMax LiveData로 전환, updateSelectedRatingRange API 추가. DetailExploreResultViewModel: 공개 선택형 LiveData/키워드 노출 제거, 내부 private 필터 상태로 관리하고 UseCase에 범위 전달.
UI — Compose 추가
app/src/main/java/.../ui/detailExplore/DetailExploreActivity.kt, DetailExploreScreen.kt, component/* (DetailExploreAppBar.kt, DetailExploreInfoTab.kt, DetailExploreKeywordTab.kt, DetailExploreCtaButton.kt, RatingRangeSlider.kt, SelectableChip.kt)
Hilt Activity 등록(Manifest 추가), Compose 기반 화면·컴포넌트 추가: 탭, 칩, 평점 범위 슬라이더, CTA, 키워드/정보 탭 구성 및 검색 결과/문의 네비게이션.
UI 진입점·네비게이션 조정
app/src/main/java/.../ui/main/home/HomeFragment.kt, MainActivity.kt, feature/library/*, app/src/main/res/layout/fragment_home.xml
홈에서 관심글 UI 및 Explore Fragment 제거; 홈 레이아웃에 상세검색 카드(cl_home_detail_search) 추가하여 DetailExploreActivity로 연결; FragmentType.EXPLORE 및 관련 메뉴 항목 제거; 라이브러리 상단바 검색 아이콘·플러스 아이콘 연결 변경.
삭제 — 기존 Fragment/BottomSheet/어댑터/레이아웃
app/src/main/java/.../ui/detailExplore/* fragments, .../detailExploreResult/* fragments & bottom sheets, .../keyword/adapter/*, home/adpater/*, res/layout/dialog_detail_explore.xml, fragment_detail_explore_*.xml, fragment_detail_explore_result_*.xml, item_user_interest_feed.xml, fragment_explore.xml
기존 Fragment/BottomSheet 기반 상세 탐색·결과 관련 모든 Fragment·BottomSheet·어댑터·뷰홀더·레이아웃 일괄 삭제.
리소스 정리 / 추가
core/resource/.../drawable/ic_main_explore.xml (삭제), core/resource/.../drawable/ic_plus_novel.xml (추가), core/resource/.../values/strings.xml
탐색 아이콘/문구 삭제, 플러스 아이콘 추가, 홈 레이아웃에서 관심글 영역 제거 및 문자열 리소스 일부 추가/삭제.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Home as HomeFragment
    participant Activity as DetailExploreActivity
    participant VM as DetailExploreViewModel
    participant UseCase as GetDetailExploreResultUseCase
    participant Result as DetailExploreResultActivity
    participant Browser

    User->>Home: 홈의 상세검색 카드 탭
    Home->>Activity: startActivity(DetailExploreActivity)
    User->>Activity: 장르/상태/평점 범위/키워드 선택
    Activity->>VM: updateSelected... (장르/상태/평점범위/키워드)
    User->>Activity: 검색 CTA 클릭
    Activity->>VM: 현재 필터값 요청
    VM->>UseCase: invoke(genres, isCompleted, ratingStart, ratingEnd, keywordIds)
    UseCase->>Activity: ExploreResult 반환
    Activity->>Result: startActivity(DetailExploreResultActivity with filters)
    alt 키워드 문의
      Activity->>Browser: ACTION_VIEW(inquire_link)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Team-WSS/WSS-Android#780: UserInterestFeeds DTO/Entity 및 매퍼/레포지토리 변경을 도입한 PR로, 본 PR의 관심글 관련 제거와 직접적으로 연결됨.
  • Team-WSS/WSS-Android#787: 피드 네트워크/모델 표면 변경(유사 DTO 제거/변경)과 관련된 PR로 코드-level 충돌 가능성 있음.
  • Team-WSS/WSS-Android#728: MainActivity의 프래그먼트 네비게이션/FragmentType 변경과 관련된 PR로 네비게이션 충돌 소지 있음.

Suggested reviewers

  • Sadturtleman
  • s9hn

Poem

🐰 탐색 탭은 살짝 접고 나는 폴짝,
칩과 슬라이더로 길을 닦았네,
홈의 카드 한 번 탭하면 상세 검색으로 훌쩍,
관심글은 작별인사, 코드엔 새 가지가 났네,
당근처럼 반짝이는 플러스 아이콘에 웃음 한 움큼.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항인 탐색 탭 제거와 서재 아이콘 추가를 명확하게 요약하고 있으며, 저자의 관점에서 가장 중요한 변경을 잘 반영하고 있습니다.
Description check ✅ Passed PR 설명이 템플릿의 모든 필수 섹션(Issues, Work Description, Screenshots, To Reviewers)을 포함하고 있으며, 변경사항이 상세하게 나열되어 있고 스크린 녹화 자료도 제공되었습니다.
Linked Issues check ✅ Passed PR의 코드 변경사항들이 링크된 이슈 #875의 요구사항을 충족합니다: 탐색 탭 제거, 평점 범위 슬라이더 추가, 서재 검색 아이콘 추가, 관심글 제거 등 모든 주요 목표가 구현되었습니다.
Out of Scope Changes check ✅ Passed PR의 변경사항들이 이슈 #875의 범위 내에서 이루어졌습니다. 탐색 탭 제거, 상세 탐색 UI 개선, 서재 아이콘 추가, 관심글 제거 등 모두 요구된 목표와 일치합니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/875

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreKeywordTab.kt`:
- Around line 79-105: searchValue is kept only in local remember and you flip
search-mode too early via updateIsSearchKeywordProceeding(true), causing UI
desync; either lift search query and search-mode into the ViewModel or change
handlers so the ViewModel is only updated on actual search/clear actions.
Concretely: stop calling viewModel.updateIsSearchKeywordProceeding(true) on
every onValueChange, instead call viewModel.updateKeyword(searchValue.text) and
viewModel.updateIsSearchKeywordProceeding(true) only from the onSearch handler
(and call viewModel.initSearchKeyword() and
updateIsSearchKeywordProceeding(false) on onClear); update usages around
searchValue, KeywordSearchField, viewModel.updateKeyword(),
viewModel.initSearchKeyword(), and viewModel.updateIsSearchKeywordProceeding()
accordingly so UI state remains authoritative in the ViewModel.

In
`@app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreViewModel.kt`:
- Around line 32-40: The current separate LiveData fields _selectedRatingMin and
_selectedRatingMax cause intermediate inconsistent states when updated; replace
them with a single MutableLiveData holding a rating range object (e.g., data
class RatingRange(val min: Float, val max: Float)) named _selectedRatingRange
and expose selectedRatingRange: LiveData<RatingRange>; update all callers that
set either min or max to post a new RatingRange atomically via a helper like
updateRatingRange(newMin?, newMax?) and change dependent derived LiveData
(selectedRating, isInfoChipSelected, and any other mappings referenced around
selectedRating/selectedRatingMin/selectedRatingMax usages) to map from
selectedRatingRange instead of separate min/max LiveData so observers only see
consistent range updates.

In `@app/src/main/java/com/into/websoso/ui/main/home/HomeViewModel.kt`:
- Around line 159-165: On successful fetchPopularFeeds() handling in the
onSuccess lambda, reset the error flag on _uiState by copying uiState.value and
setting error = false in addition to updating popularFeeds (i.e., update
_uiState.value = uiState.value?.copy(popularFeeds =
popularFeeds.popularFeeds.chunked(3), error = false)), so a prior failure no
longer shows after a successful refresh; modify the onSuccess block around
fetchPopularFeeds() accordingly.

In
`@feature/library/src/main/java/com/into/websoso/feature/library/component/LibraryTopBar.kt`:
- Around line 36-46: The clickable icon in LibraryTopBar (Box/Image using
debouncedClickable and onSearchClick) currently has contentDescription = null
and lacks button semantics; update the Image (or the Box's Modifier) to provide
an accessible label and button semantics by replacing contentDescription = null
with a meaningful string (or stringResource) and adding a semantics modifier
that sets role = Role.Button and an onClick semantic that invokes onSearchClick
(or ensure semantics are merged so the existing debouncedClickable is exposed to
accessibility). This ensures the action is exposed to screen readers while
keeping the existing debouncedClickable behavior.
🪄 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: 6a488562-761a-468c-b4b6-6f8c566a023d

📥 Commits

Reviewing files that changed from the base of the PR and between 297ad54 and eb0bee1.

⛔ Files ignored due to path filters (1)
  • core/resource/src/main/res/drawable-xxhdpi/img_home_detail_search.png is excluded by !**/*.png
📒 Files selected for processing (52)
  • app/src/main/AndroidManifest.xml
  • app/src/main/java/com/into/websoso/data/mapper/FeedMapper.kt
  • app/src/main/java/com/into/websoso/data/model/UserInterestFeedsEntity.kt
  • app/src/main/java/com/into/websoso/data/remote/api/FeedApi.kt
  • app/src/main/java/com/into/websoso/data/remote/response/UserInterestFeedsResponseDto.kt
  • app/src/main/java/com/into/websoso/data/repository/FeedRepository.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreActivity.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreDialogBottomSheet.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreScreen.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreViewModel.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreAppBar.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreCtaButton.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreInfoTab.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreKeywordTab.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/component/RatingRangeSlider.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/component/SelectableChip.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/info/DetailExploreInfoFragment.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/info/model/Rating.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/keyword/DetailExploreClickListener.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/keyword/DetailExploreKeywordFragment.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/keyword/adapter/DetailExploreKeywordAdapter.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/keyword/adapter/DetailExploreKeywordViewHolder.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultActivity.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultDialogBottomSheet.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultInfoFragment.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultKeywordFragment.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultViewModel.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/model/DetailExploreResultUiState.kt
  • app/src/main/java/com/into/websoso/ui/main/MainActivity.kt
  • app/src/main/java/com/into/websoso/ui/main/explore/ExploreFragment.kt
  • app/src/main/java/com/into/websoso/ui/main/home/HomeFragment.kt
  • app/src/main/java/com/into/websoso/ui/main/home/HomeViewModel.kt
  • app/src/main/java/com/into/websoso/ui/main/home/adpater/UserInterestFeedAdapter.kt
  • app/src/main/java/com/into/websoso/ui/main/home/adpater/UserInterestFeedViewHolder.kt
  • app/src/main/java/com/into/websoso/ui/main/home/model/HomeUiState.kt
  • app/src/main/res/layout/dialog_detail_explore.xml
  • app/src/main/res/layout/fragment_detail_explore_info.xml
  • app/src/main/res/layout/fragment_detail_explore_keyword.xml
  • app/src/main/res/layout/fragment_detail_explore_result_info.xml
  • app/src/main/res/layout/fragment_detail_explore_result_keyword.xml
  • app/src/main/res/layout/fragment_explore.xml
  • app/src/main/res/layout/fragment_home.xml
  • app/src/main/res/layout/item_user_interest_feed.xml
  • app/src/main/res/menu/menu_main_bnv.xml
  • core/network/src/main/java/com/into/websoso/core/network/datasource/feed/model/response/UserInterestFeedsResponseDto.kt
  • core/resource/src/main/res/drawable/ic_main_explore.xml
  • core/resource/src/main/res/drawable/ic_plus_novel.xml
  • core/resource/src/main/res/values/strings.xml
  • data/feed/src/main/java/com/into/websoso/data/feed/mapper/FeedMapper.kt
  • data/feed/src/main/java/com/into/websoso/data/feed/model/UserInterestFeedsEntity.kt
  • feature/library/src/main/java/com/into/websoso/feature/library/LibraryScreen.kt
  • feature/library/src/main/java/com/into/websoso/feature/library/component/LibraryTopBar.kt
💤 Files with no reviewable changes (32)
  • app/src/main/res/layout/dialog_detail_explore.xml
  • app/src/main/java/com/into/websoso/data/remote/api/FeedApi.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/info/model/Rating.kt
  • app/src/main/java/com/into/websoso/data/model/UserInterestFeedsEntity.kt
  • app/src/main/res/menu/menu_main_bnv.xml
  • app/src/main/res/layout/fragment_detail_explore_info.xml
  • core/network/src/main/java/com/into/websoso/core/network/datasource/feed/model/response/UserInterestFeedsResponseDto.kt
  • core/resource/src/main/res/drawable/ic_main_explore.xml
  • app/src/main/java/com/into/websoso/ui/detailExplore/keyword/DetailExploreClickListener.kt
  • app/src/main/java/com/into/websoso/ui/main/explore/ExploreFragment.kt
  • app/src/main/java/com/into/websoso/data/repository/FeedRepository.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/keyword/DetailExploreKeywordFragment.kt
  • app/src/main/java/com/into/websoso/data/mapper/FeedMapper.kt
  • data/feed/src/main/java/com/into/websoso/data/feed/model/UserInterestFeedsEntity.kt
  • app/src/main/java/com/into/websoso/ui/main/home/adpater/UserInterestFeedAdapter.kt
  • app/src/main/res/layout/item_user_interest_feed.xml
  • app/src/main/res/layout/fragment_detail_explore_result_info.xml
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultDialogBottomSheet.kt
  • app/src/main/java/com/into/websoso/ui/main/MainActivity.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultInfoFragment.kt
  • app/src/main/java/com/into/websoso/data/remote/response/UserInterestFeedsResponseDto.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/model/DetailExploreResultUiState.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/keyword/adapter/DetailExploreKeywordAdapter.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultKeywordFragment.kt
  • app/src/main/java/com/into/websoso/ui/main/home/adpater/UserInterestFeedViewHolder.kt
  • data/feed/src/main/java/com/into/websoso/data/feed/mapper/FeedMapper.kt
  • app/src/main/res/layout/fragment_explore.xml
  • app/src/main/java/com/into/websoso/ui/detailExplore/keyword/adapter/DetailExploreKeywordViewHolder.kt
  • app/src/main/res/layout/fragment_detail_explore_result_keyword.xml
  • app/src/main/res/layout/fragment_detail_explore_keyword.xml
  • app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreDialogBottomSheet.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/info/DetailExploreInfoFragment.kt

Comment on lines +79 to +105
var searchValue by remember { mutableStateOf(TextFieldValue("")) }

val selectedKeywords = remember(uiState.categories) {
uiState.categories.flatMap { it.keywords }.filter { it.isSelected }
}

Column(modifier = modifier.fillMaxWidth()) {
KeywordSearchField(
value = searchValue,
onValueChange = { value ->
searchValue = value
if (value.text.isEmpty()) {
viewModel.initSearchKeyword()
} else {
viewModel.updateIsSearchKeywordProceeding(true)
}
},
onSearch = {
if (searchValue.text.isEmpty()) {
viewModel.initSearchKeyword()
} else {
viewModel.updateKeyword(searchValue.text)
}
},
onClear = {
searchValue = TextFieldValue("")
viewModel.initSearchKeyword()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

검색 입력과 검색모드를 로컬 상태로 두면 UI가 쉽게 어긋납니다.

searchValueremember에만 있고, updateIsSearchKeywordProceeding(true)를 타이핑 직후 호출해서 실제 검색 전에 결과 모드로 들어갑니다. 이 상태로 탭을 바꾸거나 reset하면 입력창은 비는데 이전 검색 결과가 남을 수 있습니다. 쿼리와 검색모드를 ViewModel로 올리거나, 최소한 검색 제출 시점까지는 결과 모드로 전환하지 않도록 조정해 주세요.

Also applies to: 116-136

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/into/websoso/ui/detailExplore/component/DetailExploreKeywordTab.kt`
around lines 79 - 105, searchValue is kept only in local remember and you flip
search-mode too early via updateIsSearchKeywordProceeding(true), causing UI
desync; either lift search query and search-mode into the ViewModel or change
handlers so the ViewModel is only updated on actual search/clear actions.
Concretely: stop calling viewModel.updateIsSearchKeywordProceeding(true) on
every onValueChange, instead call viewModel.updateKeyword(searchValue.text) and
viewModel.updateIsSearchKeywordProceeding(true) only from the onSearch handler
(and call viewModel.initSearchKeyword() and
updateIsSearchKeywordProceeding(false) on onClear); update usages around
searchValue, KeywordSearchField, viewModel.updateKeyword(),
viewModel.initSearchKeyword(), and viewModel.updateIsSearchKeywordProceeding()
accordingly so UI state remains authoritative in the ViewModel.

Comment thread app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreViewModel.kt Outdated
Comment on lines +159 to +165
feedRepository.fetchPopularFeeds()
}.onSuccess { popularFeeds ->
_uiState.value = uiState.value?.copy(
popularFeeds = popularFeeds.popularFeeds.chunked(3),
isInterestNovel = isUserInterestedInNovels(userInterestFeeds.message),
userInterestFeeds = userInterestFeeds.userInterestFeeds,
)
}.onFailure {
_uiState.value = uiState.value?.copy(
error = true,
)
_uiState.value = uiState.value?.copy(error = true)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

갱신 성공 시 에러 플래그를 초기화하세요.

fetchPopularFeeds()가 성공해도 error를 다시 false로 돌리지 않아서, 이전 실패 후 새로고침이 성공하면 에러 UI가 계속 남을 수 있습니다.

간단한 수정안
                 }.onSuccess { popularFeeds ->
                     _uiState.value = uiState.value?.copy(
                         popularFeeds = popularFeeds.popularFeeds.chunked(3),
+                        error = false,
                     )
📝 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.

Suggested change
feedRepository.fetchPopularFeeds()
}.onSuccess { popularFeeds ->
_uiState.value = uiState.value?.copy(
popularFeeds = popularFeeds.popularFeeds.chunked(3),
isInterestNovel = isUserInterestedInNovels(userInterestFeeds.message),
userInterestFeeds = userInterestFeeds.userInterestFeeds,
)
}.onFailure {
_uiState.value = uiState.value?.copy(
error = true,
)
_uiState.value = uiState.value?.copy(error = true)
feedRepository.fetchPopularFeeds()
}.onSuccess { popularFeeds ->
_uiState.value = uiState.value?.copy(
popularFeeds = popularFeeds.popularFeeds.chunked(3),
error = false,
)
}.onFailure {
_uiState.value = uiState.value?.copy(error = true)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/into/websoso/ui/main/home/HomeViewModel.kt` around
lines 159 - 165, On successful fetchPopularFeeds() handling in the onSuccess
lambda, reset the error flag on _uiState by copying uiState.value and setting
error = false in addition to updating popularFeeds (i.e., update _uiState.value
= uiState.value?.copy(popularFeeds = popularFeeds.popularFeeds.chunked(3), error
= false)), so a prior failure no longer shows after a successful refresh; modify
the onSuccess block around fetchPopularFeeds() accordingly.

Comment on lines +36 to +46
Box(
modifier = Modifier
.debouncedClickable(onClick = onSearchClick)
.padding(horizontal = 20.dp, vertical = 8.dp),
) {
Image(
imageVector = ImageVector.vectorResource(id = ic_plus_novel),
contentDescription = null,
modifier = Modifier.size(24.dp),
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

액션 버튼에 접근성 라벨과 버튼 semantics를 추가해 주세요.

지금은 클릭 가능한 아이콘인데 contentDescription = null 이라 스크린리더에서 기능이 드러나지 않습니다. 상단바의 유일한 행동 버튼이므로 접근성상 바로 수정하는 게 좋습니다.

♿ 제안 수정안
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.res.vectorResource
 import androidx.compose.ui.unit.dp
@@
         Box(
             modifier = Modifier
                 .debouncedClickable(onClick = onSearchClick)
+                .semantics { role = Role.Button }
                 .padding(horizontal = 20.dp, vertical = 8.dp),
         ) {
             Image(
                 imageVector = ImageVector.vectorResource(id = ic_plus_novel),
-                contentDescription = null,
+                contentDescription = "서재에서 검색",
                 modifier = Modifier.size(24.dp),
             )
         }
📝 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.

Suggested change
Box(
modifier = Modifier
.debouncedClickable(onClick = onSearchClick)
.padding(horizontal = 20.dp, vertical = 8.dp),
) {
Image(
imageVector = ImageVector.vectorResource(id = ic_plus_novel),
contentDescription = null,
modifier = Modifier.size(24.dp),
)
}
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
Box(
modifier = Modifier
.debouncedClickable(onClick = onSearchClick)
.semantics { role = Role.Button }
.padding(horizontal = 20.dp, vertical = 8.dp),
) {
Image(
imageVector = ImageVector.vectorResource(id = ic_plus_novel),
contentDescription = "서재에서 검색",
modifier = Modifier.size(24.dp),
)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/library/src/main/java/com/into/websoso/feature/library/component/LibraryTopBar.kt`
around lines 36 - 46, The clickable icon in LibraryTopBar (Box/Image using
debouncedClickable and onSearchClick) currently has contentDescription = null
and lacks button semantics; update the Image (or the Box's Modifier) to provide
an accessible label and button semantics by replacing contentDescription = null
with a meaningful string (or stringResource) and adding a semantics modifier
that sets role = Role.Button and an onClick semantic that invokes onSearchClick
(or ensure semantics are merged so the existing debouncedClickable is exposed to
accessibility). This ensures the action is exposed to screen readers while
keeping the existing debouncedClickable behavior.

Copy link
Copy Markdown
Contributor

@Sadturtleman Sadturtleman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다!

m6z1 and others added 2 commits May 8, 2026 19:10
- final-newline: 10개 파일에 누락된 개행 추가
- blank-line-between-when-conditions: DetailExploreScreen when 조건 사이 빈 줄 추가
- function-signature: updateSelectedRatingRange 파라미터 멀티라인 포맷
- import-ordering: DetailExploreAppBar, DetailExploreKeywordTab import 순서 수정
- no-blank-line-in-list: DetailExploreInfoTab StatusSection 호출 내 불필요한 빈 줄 제거
- if-else-wrapping/multiline-if-else: RatingRangeSlider if-else 중괄호 및 개행 추가
- backing-property-naming: DetailExploreResultViewModel 미노출 backing property 이름 변경

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/into/websoso/domain/usecase/GetDetailExploreResultUseCase.kt (1)

29-33: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

상세 탐색 캐시 재사용 시 resultCount가 잘못 계산됩니다.

Line 31이 cachedNormalExploreResult.size를 사용하고 있어, 상세 탐색 캐시(cachedDetailExploreResult)를 반환할 때 결과 개수가 틀어집니다.

수정 제안
-                    resultCount = novelRepository.cachedNormalExploreResult.size.toLong(),
+                    resultCount = novelRepository.cachedDetailExploreResult.size.toLong(),
🤖 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/domain/usecase/GetDetailExploreResultUseCase.kt`
around lines 29 - 33, The resultCount is calculated from the wrong cache when
reusing detailed explore cache in GetDetailExploreResultUseCase: change the
value used for resultCount from novelRepository.cachedNormalExploreResult.size
to novelRepository.cachedDetailExploreResult.size when returning
ExploreResultEntity while isSearchButtonClick && isCacheValid(...) so the
returned resultCount matches the cachedDetailExploreResult and preserve other
fields like isLoadable and novels (cachedDetailExploreIsLoadable and
cachedDetailExploreResult).
🤖 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/domain/usecase/GetDetailExploreResultUseCase.kt`:
- Around line 72-76: 현재 null인 필드 비교에서 genres?.equals(previousGenres) 및
keywordIds?.equals(previousKeywordIds)처럼 수신자가 null일 때 항상 false가 되어 캐시 재사용이
실패합니다; GetDetailExploreResultUseCase 안의 해당 조건 블록을 찾아 nullable 안전 동치 비교로 바꿔주세요
(예: genres == previousGenres 및 keywordIds == previousKeywordIds 또는 이전값을 왼쪽에 두고
안전한 비교(previousGenres == genres)처럼), 나머지 불리언/범위 비교(novelRatingStart/End,
isCompleted)는 그대로 유지하여 동일한 검색 조건일 때 캐시가 올바르게 재사용되도록 수정하세요.

---

Outside diff comments:
In
`@app/src/main/java/com/into/websoso/domain/usecase/GetDetailExploreResultUseCase.kt`:
- Around line 29-33: The resultCount is calculated from the wrong cache when
reusing detailed explore cache in GetDetailExploreResultUseCase: change the
value used for resultCount from novelRepository.cachedNormalExploreResult.size
to novelRepository.cachedDetailExploreResult.size when returning
ExploreResultEntity while isSearchButtonClick && isCacheValid(...) so the
returned resultCount matches the cachedDetailExploreResult and preserve other
fields like isLoadable and novels (cachedDetailExploreIsLoadable and
cachedDetailExploreResult).
🪄 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: dc9f0ba7-37ef-4bef-99ad-59bdbdf8e60e

📥 Commits

Reviewing files that changed from the base of the PR and between 3e48a28 and ac39853.

📒 Files selected for processing (6)
  • app/src/main/java/com/into/websoso/data/remote/api/NovelApi.kt
  • app/src/main/java/com/into/websoso/data/repository/NovelRepository.kt
  • app/src/main/java/com/into/websoso/domain/usecase/GetDetailExploreResultUseCase.kt
  • app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreActivity.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultViewModel.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/model/DetailExploreFilteredModel.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreActivity.kt
  • app/src/main/java/com/into/websoso/ui/detailExploreResult/DetailExploreResultViewModel.kt

Comment on lines 72 to 76
genres?.equals(previousGenres) == true &&
isCompleted == previousIsCompleted &&
novelRating == previousNovelRating &&
novelRatingStart == previousNovelRatingStart &&
novelRatingEnd == previousNovelRatingEnd &&
keywordIds?.equals(previousKeywordIds) == true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

null 필터 조건에서 캐시 유효성 검사가 항상 실패합니다.

Line 72, Line 76은 현재 값이 null일 때 무조건 false가 되어, 동일 검색 조건이어도 캐시를 재사용하지 못합니다.

수정 제안
-            genres?.equals(previousGenres) == true &&
+            genres == previousGenres &&
                 isCompleted == previousIsCompleted &&
                 novelRatingStart == previousNovelRatingStart &&
                 novelRatingEnd == previousNovelRatingEnd &&
-                keywordIds?.equals(previousKeywordIds) == true
+                keywordIds == previousKeywordIds
🤖 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/domain/usecase/GetDetailExploreResultUseCase.kt`
around lines 72 - 76, 현재 null인 필드 비교에서 genres?.equals(previousGenres) 및
keywordIds?.equals(previousKeywordIds)처럼 수신자가 null일 때 항상 false가 되어 캐시 재사용이
실패합니다; GetDetailExploreResultUseCase 안의 해당 조건 블록을 찾아 nullable 안전 동치 비교로 바꿔주세요
(예: genres == previousGenres 및 keywordIds == previousKeywordIds 또는 이전값을 왼쪽에 두고
안전한 비교(previousGenres == genres)처럼), 나머지 불리언/범위 비교(novelRatingStart/End,
isCompleted)는 그대로 유지하여 동일한 검색 조건일 때 캐시가 올바르게 재사용되도록 수정하세요.

Copy link
Copy Markdown
Contributor

@devfeijoa devfeijoa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고 많으셨습니다 ! 역시 뭉치 👍🏻

Comment thread app/src/main/java/com/into/websoso/ui/detailExplore/DetailExploreViewModel.kt Outdated
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

새 아이콘 너무 귀여워용

@m6z1 m6z1 merged commit 2dffd30 into develop May 10, 2026
2 checks passed
@m6z1 m6z1 deleted the feat/875 branch May 10, 2026 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍯 [FEAT] 새로운 기능을 개발합니다. 🏹 궁사 명지 웹소소 공주의 은밀한 사냥생활

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 탐색탭 제거 및 서재 아이콘 추가

3 participants