-
Notifications
You must be signed in to change notification settings - Fork 0
[Fix/#129] 설정 화면 버전 표시문제 수정 #158
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
Conversation
… 버튼에 플레이스토어 이동 로직 연결
# Conflicts: # presentation/build.gradle.kts # presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt # presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt
…ine 사용, 업데이트 여부 판단 조건 수정
Walkthrough설정 화면 버전 표시 및 Play Store 업데이트 흐름을 추가하고, 버전 제공자 DI 도입과 빌드 관례로 애플리케이션 ID를 중앙화했습니다. MVI에서 Orbit으로 설정 화면 아키텍처를 전환했습니다. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45-60 minutes
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (7)
build-logic/convention/src/main/java/com/threegap/bitnagil/convention/extension/ApplicationId.kt (2)
8-15: applicationId 하드코딩 대신 설정/카탈로그로의 위임을 고려해 보세요.현재
"com.threegap.bitnagil"이 빌드‑로직 안에 고정 문자열로 들어가 있어, 앱 ID 변경(스테이징/QA용 별도 applicationId 등)이 필요할 때마다 플러그인 코드를 수정해야 합니다.
gradle.properties, version catalog(libs.versions.toml), 혹은 별도 convention 설정 객체로부터 값을 주입받도록 바꾸면, 환경/플레이버 추가 시 더 유연하게 대응할 수 있습니다.
17-26: 모든 라이브러리 모듈에서 BuildConfig를 강제로 활성화하는 영향 확인 필요.
LibraryExtension.buildFeatures { buildConfig = true }를 공통으로 적용하면, 기존에 의도적으로 BuildConfig 생성을 끄고 있던 라이브러리도 전부 BuildConfig가 생성되도록 동작이 바뀝니다.
전 모듈에서BuildConfig.APPLICATION_ID를 쓰겠다는 명확한 정책이라면 괜찮지만, 일부 모듈만 필요하다면:
- opt‑out(예: 커스텀 플러그인 옵션)이나,
buildConfig가 이미 true인 경우에만 필드만 추가하는 형태같은 방식을 한 번 검토해 보셔도 좋겠습니다.
build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidLibraryPlugin.kt (1)
5-24: LibraryExtension 블록 안에서의 이중 lookup을 줄일 수 있습니다.
extensions.configure<LibraryExtension> { ... configureApplicationId() }에서configureApplicationId()가 다시extensions.findByType<LibraryExtension>()를 호출해 같은 익스텐션을 재탐색합니다.
성능상 큰 문제는 없지만, 아래처럼 시그니처를 분리해 바로this를 넘기면 의도가 더 명확해집니다.// 예시 internal fun Project.configureApplicationId() { extensions.findByType<LibraryExtension>()?.let { it.configureApplicationId() } } internal fun LibraryExtension.configureApplicationId() { val applicationId = "com.threegap.bitnagil" defaultConfig { buildConfigField("String", "APPLICATION_ID", "\"$applicationId\"") } buildFeatures { buildConfig = true } }build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidApplicationPlugin.kt (1)
5-31: ApplicationExtension에 대해서도 configureApplicationId 재사용 방식을 정리할 여지가 있습니다.여기서도
extensions.configure<ApplicationExtension> { ... configureApplicationId() }호출 시, 내부에서 다시extensions.findByType<ApplicationExtension>()를 수행하는 구조입니다.
동작에는 문제가 없지만, Library 쪽과 마찬가지로ApplicationExtension용 오버로드를 분리해this에 직접 적용하면, 확장 함수의 책임 범위가 더 명확해지고 추후 유지보수 시 추적이 쉬워질 것 같습니다.presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt (1)
3-4: Enum 상수 네이밍 일관성을 개선해주세요.Enum 상수들의 네이밍 컨벤션이 일관되지 않습니다. Kotlin에서는 enum 상수에 PascalCase를 사용하는 것이 관례입니다.
Latest,NONE: PascalCaseNEED_UPDATE: SCREAMING_SNAKE_CASE다음 diff를 적용하여 일관된 네이밍을 사용하세요:
enum class UpdateAvailableState { - Latest, NEED_UPDATE, NONE + Latest, NeedUpdate, None }또는 모두 PascalCase로:
enum class UpdateAvailableState { - Latest, NEED_UPDATE, NONE + Latest, NeedsUpdate, None }presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt (1)
43-59: 예외 로깅을 고려해보세요 (선택사항).예외를 의도적으로 무시하여 폴백 로직을 구현한 것은 적절하지만, 디버깅을 위해 예외를 로깅하는 것을 고려해볼 수 있습니다.
private fun tryOpenPlayStore(activity: ComponentActivity): Boolean = try { val intent = createPlayStoreIntent() activity.startActivity(intent) true } catch (e: ActivityNotFoundException) { + // Log.d("PlayStoreUtils", "Play Store app not found, will try browser") false } private fun tryOpenWebBrowser(activity: ComponentActivity): Boolean = try { val intent = createWebIntent() activity.startActivity(intent) true } catch (e: Exception) { + // Log.e("PlayStoreUtils", "Failed to open Play Store or browser", e) false }presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt (1)
45-53:intent블록 내에서는state를 직접 사용할 수 있습니다.
container.stateFlow.value.loading대신state.loading을 사용하면 더 간결하고 Orbit 패턴에 일관됩니다.fun logout() = intent { - if (container.stateFlow.value.loading) return@intent + if (state.loading) return@intent reduce { state.copy( logoutConfirmDialogVisible = false, loading = true, ) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
app/build.gradle.kts(0 hunks)app/src/main/java/com/threegap/bitnagil/di/presentation/VersionNameProviderModule.kt(1 hunks)build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidApplicationPlugin.kt(2 hunks)build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidLibraryPlugin.kt(2 hunks)build-logic/convention/src/main/java/com/threegap/bitnagil/convention/extension/ApplicationId.kt(1 hunks)gradle/libs.versions.toml(3 hunks)presentation/build.gradle.kts(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/AndroidApplicationVersionNameProvider.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/VersionNameProvider.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt(7 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingIntent.kt(0 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/splash/SplashScreen.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/splash/util/PlayStoreUtils.kt(0 hunks)
💤 Files with no reviewable changes (3)
- app/build.gradle.kts
- presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingIntent.kt
- presentation/src/main/java/com/threegap/bitnagil/presentation/splash/util/PlayStoreUtils.kt
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-23T13:31:46.809Z
Learnt from: l5x5l
Repo: YAPP-Github/Bitnagil-Android PR: 41
File: presentation/src/main/java/com/threegap/bitnagil/presentation/mypage/model/MyPageState.kt:6-14
Timestamp: 2025-07-23T13:31:46.809Z
Learning: In the Bitnagil Android project, MviState interface extends Parcelable, so any class implementing MviState automatically implements Parcelable. Therefore, Parcelize annotation works correctly without explicitly adding Parcelable implementation.
Applied to files:
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt
📚 Learning: 2025-08-13T09:06:19.028Z
Learnt from: wjdrjs00
Repo: YAPP-Github/Bitnagil-Android PR: 101
File: presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt:61-67
Timestamp: 2025-08-13T09:06:19.028Z
Learning: In Android ViewModels, when logout success triggers navigation to a different screen (like NavigateToLogin), the current ViewModel's lifecycle ends, so loading states don't need to be explicitly reset in the success case since the ViewModel will be destroyed.
Applied to files:
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.ktpresentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt
🧬 Code graph analysis (3)
build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidApplicationPlugin.kt (1)
build-logic/convention/src/main/java/com/threegap/bitnagil/convention/extension/ApplicationId.kt (1)
configureApplicationId(8-27)
build-logic/convention/src/main/java/com/threegap/bitnagil/convention/AndroidLibraryPlugin.kt (1)
build-logic/convention/src/main/java/com/threegap/bitnagil/convention/extension/ApplicationId.kt (1)
configureApplicationId(8-27)
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt (2)
updateAvailable(86-96)openAppInPlayStore(30-41)
🪛 detekt (1.23.8)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt
[warning] 48-48: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 57-57: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (14)
presentation/build.gradle.kts (1)
24-24: LGTM!app-update 라이브러리 의존성이 올바르게 추가되었습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/splash/SplashScreen.kt (1)
28-28: LGTM!Play Store 유틸리티를 공통 모듈로 이동하여 재사용성을 개선했습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/VersionNameProvider.kt (1)
1-5: LGTM!버전 이름 제공을 위한 깔끔한 추상화입니다. DI와 테스트 용이성을 개선합니다.
app/src/main/java/com/threegap/bitnagil/di/presentation/VersionNameProviderModule.kt (1)
11-17: LGTM!VersionNameProvider를 위한 표준 Hilt 바인딩 모듈입니다. Singleton 스코프가 적절하게 적용되었습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt (3)
86-96: LGTM!업데이트 가능 여부를 비동기로 확인하는 Composable 함수입니다. LaunchedEffect를 사용하여 초기 로딩 상태(NONE)에서 실제 상태로 전환하는 패턴이 적절합니다.
98-119: LGTM!
suspendCancellableCoroutine을 올바르게 사용하여 Play Core의 콜백 기반 API를 코루틴으로 변환했습니다.
continuation.isActive체크로 취소된 코루틴 재개를 방지- 실패 시
NONE반환으로 안전한 폴백 제공- 업데이트 가능 여부와 허용된 업데이트 타입을 모두 확인하는 로직이 적절함
26-26: No issue — BuildConfig.APPLICATION_ID is properly configured and available in the presentation library module.The convention plugin (
configureApplicationId()) explicitly enablesbuildConfigfor all library modules and exposes theAPPLICATION_IDconstant viabuildConfigField. This is the correct and standard approach used across the project.Likely an incorrect or invalid review comment.
gradle/libs.versions.toml (1)
51-52: Verify Play Services version for CVE-2022-2390 compatibility.Version 2.1.0 is the latest Play Core app-update release and contains no known unpatched vulnerabilities. However, the broader Play Services ecosystem has CVE-2022-2390 (mutable PendingIntent), which was fixed in Google Play Services v18.0.2. Ensure that the app's Play Services dependency is also updated to v18.0.2 or later and the app is rebuilt to mitigate this transitive vulnerability.
presentation/src/main/java/com/threegap/bitnagil/presentation/common/version/AndroidApplicationVersionNameProvider.kt (1)
6-14: LGTM!
VersionNameProvider인터페이스 구현이 깔끔하고, DI를 위한@Inject생성자 사용이 적절합니다. BuildConfig에서 버전 정보를 조합하는 방식이 명확합니다.presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt (2)
50-60: LGTM!Orbit MVI 패턴으로의 마이그레이션이 적절합니다.
collectAsState()와collectSideEffect()사용이 올바르며, 사이드 이펙트 처리 로직이 명확합니다.
143-168: 업데이트 상태별 UI 처리가 적절합니다.
UpdateAvailableState에 따른 분기 처리가 명확하고, 각 상태에 맞는 스타일링이 잘 적용되어 있습니다.NONE상태에서 아무것도 표시하지 않는 것은 업데이트 가능 여부를 확인하지 못한 경우에 대한 적절한 처리입니다.presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt (3)
69-89: 알람 토글 로직에 대한 확인이 필요합니다.현재
toggleServiceAlarm과togglePushAlarm은 UI 상태만 변경하고, delay 후 실제 저장 로직이 없습니다. 이것이 의도된 것인지 확인 부탁드립니다.
28-31: LGTM!Orbit 컨테이너 초기화가 적절합니다.
VersionNameProvider를 통해 초기 버전 정보를 설정하고,savedStateHandle을 사용하여 상태 복원을 지원합니다.
91-93: LGTM!
navigateToWithdrawal함수가 간결하게 구현되어 있습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/model/mvi/SettingState.kt
Show resolved
Hide resolved
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt
Show resolved
Hide resolved
wjdrjs00
left a 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.
고생하셨습니다~!
나중에 core:common 모듈을 생성해도 좋을거 같네욤~
...on/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt
Outdated
Show resolved
Hide resolved
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: 0
♻️ Duplicate comments (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt (1)
155-166: “업데이트” 배지의 터치 영역을 넓히려면 Modifier 순서를 바꾸는 것이 좋습니다. (기존 코멘트와 동일)현재
UpdateAvailableState.NEED_UPDATE분기에서 Modifier 순서는:modifier = Modifier .background(...) .clickableWithoutRipple(onClick = onClickUpdate) .padding(horizontal = 10.dp, vertical = 5.dp)이렇게 되면 패딩 영역은 클릭 영역에 포함되지 않아, 사용자가 텍스트/배지의 가장자리를 탭했을 때 터치가 먹지 않을 수 있습니다. 패딩까지 포함해서 넉넉한 터치 타겟을 주려면
.padding을.clickableWithoutRipple앞에 두는 편이 좋습니다:- UpdateAvailableState.NEED_UPDATE -> Text( + UpdateAvailableState.NEED_UPDATE -> Text( text = "업데이트", color = BitnagilTheme.colors.orange500, style = BitnagilTheme.typography.button2, modifier = Modifier .background( color = BitnagilTheme.colors.orange50, shape = RoundedCornerShape(8.dp), ) - .clickableWithoutRipple(onClick = onClickUpdate) - .padding(horizontal = 10.dp, vertical = 5.dp), + .padding(horizontal = 10.dp, vertical = 5.dp) + .clickableWithoutRipple(onClick = onClickUpdate), )이 변경으로 시각적 결과는 유지하면서 실제 터치 영역은 더 넉넉해집니다.
🧹 Nitpick comments (4)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt (3)
26-28: 패키지 이름은Context.packageName기반으로 만들면 모듈/variant 변경에 더 안전합니다.현재는
BuildConfig.APPLICATION_ID를 상수로 사용하고 있어 모듈 구조 변경이나 applicationId variant(예: flavor)에 따라 실제 런타임 패키지명과 어긋날 여지가 있습니다.createPlayStoreIntent/createWebIntent쪽으로packageName을 인자로 넘기고,activity.packageName또는context.packageName을 사용하면 이런 위험을 줄일 수 있습니다.-private const val PACKAGE_NAME = BuildConfig.APPLICATION_ID +// 필요시 제거하고, 호출부에서 activity.packageName/context.packageName 전달 -private fun createPlayStoreIntent(): Intent = +private fun createPlayStoreIntent(packageName: String): Intent = Intent( Intent.ACTION_VIEW, - "market://details?id=$PACKAGE_NAME".toUri(), + "market://details?id=$packageName".toUri(), ).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) setPackage(GOOGLE_PLAY_PACKAGE) } -private fun createWebIntent(): Intent = +private fun createWebIntent(packageName: String): Intent = Intent( Intent.ACTION_VIEW, - "https://play.google.com/store/apps/details?id=$PACKAGE_NAME".toUri(), + "https://play.google.com/store/apps/details?id=$packageName".toUri(), ).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } -private fun tryOpenPlayStore(activity: ComponentActivity): Boolean = +private fun tryOpenPlayStore(activity: ComponentActivity): Boolean = try { - val intent = createPlayStoreIntent() + val intent = createPlayStoreIntent(activity.packageName) activity.startActivity(intent) true } catch (e: ActivityNotFoundException) { false } -private fun tryOpenWebBrowser(activity: ComponentActivity): Boolean = +private fun tryOpenWebBrowser(activity: ComponentActivity): Boolean = try { - val intent = createWebIntent() + val intent = createWebIntent(activity.packageName) activity.startActivity(intent) true } catch (e: Exception) { false }
30-41:exitProcess(0)기반 강제 종료는 기본값을 보수적으로 두는 편이 좋습니다.
finishAppWithDelay에서finishAffinity()후exitProcess(0)까지 호출하고 있어, 다른 호출부에서 기본값(shouldFinishApp = true)을 그대로 사용할 경우 사용자가 뒤로가기로 앱으로 돌아올 수 없는 “강제 종료” UX가 됩니다.현재 Setting 화면에서는
shouldFinishApp = false로 안전하게 쓰이고 있지만, 실수 방지를 위해:
- 기본값을
false로 바꾸거나,- 정말 강제 종료가 필요한 경우에만 사용하는 별도 함수(예:
openAppInPlayStoreAndExit(...))를 두는 방식을 고려해볼 수 있습니다.-fun openAppInPlayStore( - activity: ComponentActivity?, - shouldFinishApp: Boolean = true, -) { +fun openAppInPlayStore( + activity: ComponentActivity?, + shouldFinishApp: Boolean = false, +) { activity?.let { val isSuccess = tryOpenPlayStore(it) || tryOpenWebBrowser(it) if (isSuccess && shouldFinishApp) { finishAppWithDelay(it) } } }강제 종료가 제품 기획상 꼭 필요한 시나리오라면, 해당 호출부에서만
shouldFinishApp = true를 명시하는 식으로 의도를 드러내 주면 더 읽기 편할 것 같습니다.Also applies to: 78-84
86-96: 업데이트 판정 조건(UPDATE_AVAILABLE +isUpdateTypeAllowed)이 요구사항과 맞는지 한 번 더 확인이 필요합니다.현재 로직은:
updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE이고AppUpdateType.IMMEDIATE또는AppUpdateType.FLEXIBLE중 하나라도isUpdateTypeAllowed(...) == true일 때만
UpdateAvailableState.NEED_UPDATE를 반환하고, 그 외 성공 케이스는 모두LATEST, 실패는NONE으로 처리합니다.즉, Play Core 입장에서 “업데이트는 있지만 현재 타입(IMMEDIATE/FLEXIBLE)으로는 진행할 수 없는 상태”가 될 경우, UI에는
"최신"으로 표기됩니다. Issue 설명 상 요구사항은 “현 버전이 낮을 경우 ‘업데이트’ 태그 표시”라서, 이런 edge case에서 실제로는 업데이트가 있음에도"최신"으로 보일 수 있습니다.만약 “업데이트만 존재하면 무조건 ‘업데이트’ 뱃지”가 더 맞는 기획이라면,
isAllowed체크를 제거하고UPDATE_AVAILABLE여부만 보는 게 더 직관적일 수 있습니다:val isAvailable = appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE val state = if (isAvailable) { UpdateAvailableState.NEED_UPDATE } else { UpdateAvailableState.LATEST }반대로, 지금처럼 “실제로 사용할 수 있는 업데이트 타입이 없으면 ‘업데이트’ 뱃지를 숨긴다”는 UX가 의도라면 현재 구현도 괜찮습니다. 어느 쪽이 기획에 더 맞는지 한 번만 다시 확인해 주세요.
또한 실패 시
UpdateAvailableState.NONE으로 떨어뜨려 뱃지를 아예 숨기는 패턴은 PR 설명의 세 번째 스크린샷(업데이트 가능 여부를 불러오지 못한 경우)과 잘 맞는 구조로 보입니다.Also applies to: 98-119
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt (1)
53-54: 업데이트 상태 조회를 Composable에서 직접 하는 현재 구조는 충분히 괜찮지만, 필요시 ViewModel로 올리는 것도 고려해볼 수 있습니다.지금은:
SettingScreenContainer에서val updateAvailableState = updateAvailable()로 Compose 레벨에서 app-update 상태를 한 번 조회하고,SettingScreen에updateAvailableState를 내려 UI만 담당하도록 분리되어 있습니다.설정 화면처럼 진입 빈도가 상대적으로 낮고, 상태를 한 번만 알면 되는 경우에는 이 정도 수준의 Compose-side 호출로도 충분히 실용적입니다.
다만:
- 다른 화면에서도 같은 정보를 재사용해야 하거나,
- 설정 화면을 자주 왕복하면서 매번 Play Core를 다시 두드리는 것이 부담스럽다고 판단되면,
updateAvailableState를 ViewModel 상태로 끌어올려(예: init 블록에서 suspend 함수 한 번 호출) 여러 Composable에서 공유하는 구조도 고려해볼 수 있습니다. 지금 코드 구조라면 나중에 옮기기도 쉬운 편이라 “지금은 그대로, 필요해지면 ViewModel로 승격” 정도로 생각해 두시면 될 것 같습니다.Also applies to: 90-91, 143-168
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt(1 hunks)presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/UpdateAvailableState.kt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-13T09:06:19.028Z
Learnt from: wjdrjs00
Repo: YAPP-Github/Bitnagil-Android PR: 101
File: presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingViewModel.kt:61-67
Timestamp: 2025-08-13T09:06:19.028Z
Learning: In Android ViewModels, when logout success triggers navigation to a different screen (like NavigateToLogin), the current ViewModel's lifecycle ends, so loading states don't need to be explicitly reset in the success case since the ViewModel will be destroyed.
Applied to files:
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt
🧬 Code graph analysis (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt (1)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt (2)
updateAvailable(86-96)openAppInPlayStore(30-41)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (3)
presentation/src/main/java/com/threegap/bitnagil/presentation/common/playstore/PlayStoreUtils.kt (1)
1-119: 공통 Play Store 유틸로의 이동 및 app-update 연동 구조는 전반적으로 깔끔합니다.
- Splash/설정 양쪽에서 재사용할 수 있도록
common.playstore로 분리한 점,- Play Store → Web 브라우저 fallback,
- Compose
updateAvailable()에서suspendCancellableCoroutine로 app-update Task를 한 번 감싼 구조모두 역할이 분리되어 있고 호출부 입장에서 쓰기 쉬운 API로 정리되어 있습니다.
presentation/src/main/java/com/threegap/bitnagil/presentation/setting/SettingScreen.kt (2)
50-55: SettingScreenContainer에서 Orbit + PlayStoreUtils 연동 구조는 자연스럽습니다.
viewModel.collectAsState()/collectSideEffect로 Orbit 컨테이너를 직접 사용하는 구조가 깔끔하고,LocalContext.current as? ComponentActivity로 activity를 nullable로 가져와openAppInPlayStore(activity = activity, shouldFinishApp = false)에 넘기는 것도 Preview·테스트 환경에서 안전하게 no-op이 되도록 잘 처리된 것 같습니다.onClickWithdrawal→viewModel::navigateToWithdrawal→SettingSideEffect.NavigateToWithdrawal→navigateToWithdrawal()흐름도 일관적입니다.이 부분은 현재 형태 그대로 유지해도 무방해 보입니다.
Also applies to: 73-85
202-223: 프리뷰에updateAvailableState까지 포함해 새 파라미터가 잘 반영되어 있습니다.
SettingScreenPreview에서updateAvailableState = UpdateAvailableState.LATEST를 명시해 새 시그니처를 반영한 점이 좋습니다. 실제 UI에서의 최신/업데이트/미표시 세 상태를 각각 확인해보고 싶다면, 필요 시 프리뷰를 2–3개로 나눠 상태별로 추가하는 것도 선택지입니다.
[ PR Content ]
Related issue
Screenshot 📸
업데이트 가능/최신 버전/업데이트 가능 여부를 불러오지 못한 경우Work Description
To Reviewers 📢
Summary by CodeRabbit
새 기능
리팩터
기타
✏️ Tip: You can customize this high-level summary in your review settings.