-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 탐색화면 Compose 마이그레이션 #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
- 검색 결과 목록을 `ExploreSearchResultList` 컴포저블로 분리했습니다. - 검색어 유무에 따라 검색창과 로고의 위치가 애니메이션과 함께 동적으로 변경되도록 수정했습니다. - 검색 실행 시 키보드가 자동으로 숨겨지도록 구현했습니다. - 화면 전체적인 레이아웃과 패딩을 조정하여 UI를 개선했습니다.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the
✨ Finishing touches🧪 Generate unit tests (beta)
Tip 🧪 Unit Test Generation v2 is now available!We have significantly improved our unit test generation capabilities. To enable: Add this to your reviews:
finishing_touches:
unit_tests:
enabled: trueTry it out by using the Have feedback? Share your thoughts on our Discord thread! 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In
`@app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreBackHeader.kt`:
- Around line 34-37: The Icon composable in ExploreBackHeader.kt uses a
hardcoded contentDescription "뒤로가기"; replace it with a localized string by
adding <string name="explore_back">뒤로가기</string> to strings.xml and calling
stringResource(R.string.explore_back) for the Icon's contentDescription (locate
the Icon call using ImageVector.vectorResource and R.drawable.ic_arrow_back).
In
`@app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreSearchBar.kt`:
- Around line 100-103: The KeyboardActions onSearch currently only hides the
keyboard (keyboardController?.hide()) and never triggers the component's search
callback; update the onSearch handler in ExploreSearchBar.kt (the
KeyboardActions onSearch block) to first hide the keyboard and then invoke the
component's search submission function (e.g., call the existing
onSearch/onSearchSubmit/submitSearch lambda or method that performs the search,
passing the current query if required) so the IME search action actually runs
the search logic; also ensure the KeyboardOptions imeAction is set to Search if
not already.
In
`@app/src/main/java/com/daedan/festabook/presentation/explore/ExploreViewModel.kt`:
- Around line 44-50: checkFestivalId() performs synchronous SharedPreferences
I/O by calling exploreRepository.getFestivalId() on the main thread; wrap the
call in viewModelScope.launch(iODispatcher) (same pattern as SplashViewModel) so
getFestivalId() runs on the IO dispatcher and then update _uiState (via
_uiState.update { ... }) from that coroutine, avoiding blocking the main thread.
🧹 Nitpick comments (3)
app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreScreen.kt (2)
81-114:modifier파라미터가 사용되지 않고 있습니다.
modifier파라미터가 선언되었지만Scaffold에 적용되지 않아 외부에서 전달한 modifier가 무시됩니다.♻️ 수정 제안
`@Composable` fun ExploreSearchScreen( query: String, searchState: SearchUiState, onQueryChange: (String) -> Unit, onUniversitySelect: (SearchResultUiModel) -> Unit, onBackClick: () -> Unit, modifier: Modifier = Modifier, ) { Scaffold( + modifier = modifier, containerColor = Color.White, topBar = {
116-134:modifier파라미터가 사용되지 않고 있습니다.
ExploreSearchScreen과 동일하게modifier파라미터가Scaffold에 적용되어야 합니다.♻️ 수정 제안
`@Composable` fun ExploreLandingScreen( query: String, searchState: SearchUiState, onQueryChange: (String) -> Unit, onUniversitySelect: (SearchResultUiModel) -> Unit, modifier: Modifier = Modifier, ) { // ... Scaffold( + modifier = modifier, containerColor = Color.White, ) { innerPadding ->app/src/main/java/com/daedan/festabook/presentation/explore/ExploreViewModel.kt (1)
84-89:viewModelScope.launch가 불필요합니다.
tryEmit은 non-suspending 함수이므로 코루틴 내에서 실행할 필요가 없습니다.♻️ 수정 제안
fun onUniversitySelected(university: SearchResultUiModel) { exploreRepository.saveFestivalId(university.festivalId) - viewModelScope.launch { - _sideEffect.tryEmit(ExploreSideEffect.NavigateToMain(university)) - } + _sideEffect.tryEmit(ExploreSideEffect.NavigateToMain(university)) }
| Icon( | ||
| imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back), | ||
| contentDescription = "뒤로가기", | ||
| tint = Color.Unspecified, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, find the ExploreBackHeader.kt file
find . -name "ExploreBackHeader.kt" -type fRepository: festabook/android
Length of output: 153
🏁 Script executed:
# Check if strings.xml exists and look for existing localization entries
find . -name "strings.xml" -type f | head -20Repository: festabook/android
Length of output: 98
🏁 Script executed:
# Look for any existing "explore_back" or similar entries in strings.xml files
rg "explore_back|뒤로가기" --type xml -B 2 -A 2Repository: festabook/android
Length of output: 454
🏁 Script executed:
# Read the ExploreBackHeader.kt file to verify the hardcoded string
cat -n ./app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreBackHeader.kt | head -50Repository: festabook/android
Length of output: 1862
🏁 Script executed:
# Check if stringResource is already imported or available in the file
rg "import.*stringResource|stringResource" ./app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreBackHeader.ktRepository: festabook/android
Length of output: 43
🏁 Script executed:
# Check the current imports in ExploreBackHeader.kt
head -30 ./app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreBackHeader.ktRepository: festabook/android
Length of output: 1067
🏁 Script executed:
# Check strings.xml to see the exact structure and what entries exist
cat -n ./app/src/main/res/values/strings.xml | head -100Repository: festabook/android
Length of output: 5157
하드코딩된 문자열을 stringResource로 현지화하세요.
현재 "뒤로가기"가 하드코딩되어 있어 다국어 대응이 불가능합니다. stringResource를 사용하여 리소스로 관리해야 합니다.
🔧 제안 수정
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.res.stringResource
@@
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back),
- contentDescription = "뒤로가기",
+ contentDescription = stringResource(R.string.explore_back),
tint = Color.Unspecified,
)app/src/main/res/values/strings.xml의 "탐색 화면" 섹션에 다음 항목을 추가하세요:
<string name="explore_back">뒤로가기</string>🤖 Prompt for AI Agents
In
`@app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreBackHeader.kt`
around lines 34 - 37, The Icon composable in ExploreBackHeader.kt uses a
hardcoded contentDescription "뒤로가기"; replace it with a localized string by
adding <string name="explore_back">뒤로가기</string> to strings.xml and calling
stringResource(R.string.explore_back) for the Icon's contentDescription (locate
the Icon call using ImageVector.vectorResource and R.drawable.ic_arrow_back).
| KeyboardActions( | ||
| onSearch = { | ||
| keyboardController?.hide() | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find . -name "ExploreSearchBar.kt" -type fRepository: festabook/android
Length of output: 152
🏁 Script executed:
head -150 app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreSearchBar.ktRepository: festabook/android
Length of output: 4557
🏁 Script executed:
sed -n '95,110p' app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreSearchBar.ktRepository: festabook/android
Length of output: 439
🏁 Script executed:
# Search for the onSearch parameter definition to understand its signature
rg "onSearch\s*[:=]" app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreSearchBar.kt -A 2Repository: festabook/android
Length of output: 339
🏁 Script executed:
# Look for the function signature and parameter definitions
rg "fun ExploreSearchBar|@Composable" app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreSearchBar.kt -A 5Repository: festabook/android
Length of output: 624
IME 검색 시 onSearch 콜백이 호출되지 않습니다.
KeyboardActions의 onSearch 핸들러가 키보드 숨김만 수행하고 실제 검색 함수를 호출하지 않아 검색 기능이 작동하지 않습니다.
제안 수정
keyboardActions =
KeyboardActions(
onSearch = {
+ onSearch(query)
keyboardController?.hide()
},
),📝 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.
| KeyboardActions( | |
| onSearch = { | |
| keyboardController?.hide() | |
| }, | |
| KeyboardActions( | |
| onSearch = { | |
| onSearch(query) | |
| keyboardController?.hide() | |
| }, |
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/daedan/festabook/presentation/explore/component/ExploreSearchBar.kt`
around lines 100 - 103, The KeyboardActions onSearch currently only hides the
keyboard (keyboardController?.hide()) and never triggers the component's search
callback; update the onSearch handler in ExploreSearchBar.kt (the
KeyboardActions onSearch block) to first hide the keyboard and then invoke the
component's search submission function (e.g., call the existing
onSearch/onSearchSubmit/submitSearch lambda or method that performs the search,
passing the current query if required) so the IME search action actually runs
the search logic; also ensure the KeyboardOptions imeAction is set to Search if
not already.
| private fun checkFestivalId() { | ||
| val festivalId = exploreRepository.getFestivalId() | ||
| Timber.d("festival ID : $festivalId") | ||
| if (festivalId != null) { | ||
| _uiState.update { it.copy(hasFestivalId = true) } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Find ExploreRepository and getFestivalId implementation
find . -type f -name "*.kt" | xargs grep -l "fun getFestivalId"Repository: festabook/android
Length of output: 406
🏁 Script executed:
# Look for ExploreRepository file
fd -e "\.kt$" ExploreRepositoryRepository: festabook/android
Length of output: 43
🏁 Script executed:
# Check ExploreViewModel.kt to see the current implementation
fd -e "\.kt$" ExploreViewModelRepository: festabook/android
Length of output: 43
🏁 Script executed:
# Search for SplashViewModel to compare patterns
fd -e "\.kt$" SplashViewModelRepository: festabook/android
Length of output: 43
🏁 Script executed:
# Find all ViewModel files
find . -type f -name "*ViewModel.kt"Repository: festabook/android
Length of output: 790
🏁 Script executed:
# Look at ExploreRepositoryImpl to understand getFestivalId
cat ./app/src/main/java/com/daedan/festabook/data/repository/ExploreRepositoryImpl.ktRepository: festabook/android
Length of output: 1398
🏁 Script executed:
# Check the ExploreRepository interface
cat ./app/src/main/java/com/daedan/festabook/domain/repository/ExploreRepository.ktRepository: festabook/android
Length of output: 331
🏁 Script executed:
# Search for ExploreViewModel in the codebase
rg "class ExploreViewModel" -A 50Repository: festabook/android
Length of output: 11420
🏁 Script executed:
# Check FestivalLocalDataSource implementation
cat ./app/src/main/java/com/daedan/festabook/data/datasource/local/FestivalLocalDataSourceImpl.ktRepository: festabook/android
Length of output: 1360
🏁 Script executed:
# Check the interface
cat ./app/src/main/java/com/daedan/festabook/data/datasource/local/FestivalLocalDataSource.ktRepository: festabook/android
Length of output: 259
🏁 Script executed:
# Check SplashViewModel to see the claimed pattern
cat ./app/src/main/java/com/daedan/festabook/presentation/splash/SplashViewModel.ktRepository: festabook/android
Length of output: 1996
checkFestivalId()가 메인 스레드에서 동기적으로 SharedPreferences I/O를 수행합니다.
getFestivalId()는 SharedPreferences.getLong()을 호출하는 동기 작업입니다. SplashViewModel에서는 동일한 작업을 viewModelScope.launch(iODispatcher)로 감싸서 실행합니다. 메인 스레드 블로킹을 피하려면 이 패턴을 따르세요.
🛡️ 수정 제안
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+
class ExploreViewModel(
private val exploreRepository: ExploreRepository,
+ private val iODispatcher: CoroutineDispatcher = Dispatchers.IO,
) : ViewModel() {
private fun checkFestivalId() {
+ viewModelScope.launch(iODispatcher) {
val festivalId = exploreRepository.getFestivalId()
Timber.d("festival ID : $festivalId")
if (festivalId != null) {
_uiState.update { it.copy(hasFestivalId = true) }
}
+ }
}
}📝 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.
| private fun checkFestivalId() { | |
| val festivalId = exploreRepository.getFestivalId() | |
| Timber.d("festival ID : $festivalId") | |
| if (festivalId != null) { | |
| _uiState.update { it.copy(hasFestivalId = true) } | |
| } | |
| } | |
| private fun checkFestivalId() { | |
| viewModelScope.launch { | |
| val festivalId = withContext(iODispatcher) { | |
| exploreRepository.getFestivalId() | |
| } | |
| Timber.d("festival ID : $festivalId") | |
| if (festivalId != null) { | |
| _uiState.update { it.copy(hasFestivalId = true) } | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In
`@app/src/main/java/com/daedan/festabook/presentation/explore/ExploreViewModel.kt`
around lines 44 - 50, checkFestivalId() performs synchronous SharedPreferences
I/O by calling exploreRepository.getFestivalId() on the main thread; wrap the
call in viewModelScope.launch(iODispatcher) (same pattern as SplashViewModel) so
getFestivalId() runs on the IO dispatcher and then update _uiState (via
_uiState.update { ... }) from that coroutine, avoiding blocking the main thread.
#️⃣ 이슈 번호
🛠️ 작업 내용
sideEffect라는 이름으로 분리했습니다. 한 번만 발생해야하는 이벤트이기 때문에 sideEffect 객체와SharedFlow(replay = 0, extraBufferCapacity = 1)를 사용해 작동하도록 구현했습니다.🙇🏻 중점 리뷰 요청
📸 이미지 첨부 (Optional)
Screen_recording_20260131_021301.mp4
Summary by CodeRabbit
릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings.