Skip to content

Commit 248a00a

Browse files
committed
[BOOK-274] feat: 설정 화면 내 앱 최신 버전 표기 추가
1 parent 23cf844 commit 248a00a

File tree

9 files changed

+122
-18
lines changed

9 files changed

+122
-18
lines changed

core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/HandleException.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import java.net.UnknownHostException
1919
fun handleException(
2020
exception: Throwable,
2121
onError: (String) -> Unit,
22-
onLoginRequired: () -> Unit,
22+
onLoginRequired: () -> Unit = {},
2323
) {
2424
when {
2525
exception is HttpException && exception.code() == 401 -> {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.ninecraft.booket.core.data.api.repository
2+
3+
interface RemoteConfigRepository {
4+
suspend fun getLatestVersion(): Result<String>
5+
}

core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/di/RepositoryModule.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package com.ninecraft.booket.core.data.impl.di
33
import com.ninecraft.booket.core.data.api.repository.AuthRepository
44
import com.ninecraft.booket.core.data.api.repository.BookRepository
55
import com.ninecraft.booket.core.data.api.repository.RecordRepository
6+
import com.ninecraft.booket.core.data.api.repository.RemoteConfigRepository
67
import com.ninecraft.booket.core.data.api.repository.UserRepository
78
import com.ninecraft.booket.core.data.impl.repository.DefaultAuthRepository
89
import com.ninecraft.booket.core.data.impl.repository.DefaultBookRepository
910
import com.ninecraft.booket.core.data.impl.repository.DefaultRecordRepository
11+
import com.ninecraft.booket.core.data.impl.repository.DefaultRemoteConfigRepository
1012
import com.ninecraft.booket.core.data.impl.repository.DefaultUserRepository
1113
import dagger.Binds
1214
import dagger.Module
@@ -33,4 +35,8 @@ internal abstract class RepositoryModule {
3335
@Binds
3436
@Singleton
3537
abstract fun bindRecordRepository(defaultRecordRepository: DefaultRecordRepository): RecordRepository
38+
39+
@Binds
40+
@Singleton
41+
abstract fun bindRemoteConfigRepository(defaultRemoteConfigRepository: DefaultRemoteConfigRepository): RemoteConfigRepository
3642
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.ninecraft.booket.core.data.impl.repository
2+
3+
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
4+
import com.google.firebase.remoteconfig.get
5+
import com.ninecraft.booket.core.data.api.repository.RemoteConfigRepository
6+
import com.orhanobut.logger.Logger
7+
import kotlinx.coroutines.suspendCancellableCoroutine
8+
import javax.inject.Inject
9+
import kotlin.coroutines.resume
10+
11+
class DefaultRemoteConfigRepository @Inject constructor(
12+
private val remoteConfig: FirebaseRemoteConfig,
13+
) : RemoteConfigRepository {
14+
override suspend fun getLatestVersion(): Result<String> = suspendCancellableCoroutine { continuation ->
15+
remoteConfig.fetchAndActivate().addOnCompleteListener { task ->
16+
if (task.isSuccessful) {
17+
val latestVersion = remoteConfig[KEY_LATEST_VERSION].asString()
18+
Logger.d("LatestVersion: $latestVersion")
19+
continuation.resume(Result.success(latestVersion))
20+
} else {
21+
Logger.e(task.exception, "getLatestVersion failed")
22+
continuation.resume(Result.failure(task.exception ?: Exception("Unknown error")))
23+
}
24+
}
25+
}
26+
27+
companion object {
28+
private const val KEY_LATEST_VERSION = "LatestVersion"
29+
}
30+
}

feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/HandlingSettingsSideEffect.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.skydoves.compose.effects.RememberedEffect
88
@Composable
99
internal fun HandleSettingsSideEffects(
1010
state: SettingsUiState,
11+
eventSink: (SettingsUiEvent) -> Unit,
1112
) {
1213
val context = LocalContext.current
1314

@@ -16,7 +17,12 @@ internal fun HandleSettingsSideEffects(
1617
is SettingsSideEffect.ShowToast -> {
1718
Toast.makeText(context, state.sideEffect.message, Toast.LENGTH_SHORT).show()
1819
}
20+
1921
null -> {}
2022
}
23+
24+
if (state.sideEffect != null) {
25+
eventSink(SettingsUiEvent.InitSideEffect)
26+
}
2127
}
2228
}

feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsPresenter.kt

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package com.ninecraft.booket.feature.settings
22

33
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.LaunchedEffect
45
import androidx.compose.runtime.getValue
56
import androidx.compose.runtime.mutableStateOf
67
import androidx.compose.runtime.rememberCoroutineScope
78
import androidx.compose.runtime.setValue
89
import com.ninecraft.booket.core.common.constants.WebViewConstants
910
import com.ninecraft.booket.core.common.utils.handleException
1011
import com.ninecraft.booket.core.data.api.repository.AuthRepository
12+
import com.ninecraft.booket.core.data.api.repository.RemoteConfigRepository
1113
import com.ninecraft.booket.feature.screens.LoginScreen
1214
import com.ninecraft.booket.feature.screens.OssLicensesScreen
1315
import com.ninecraft.booket.feature.screens.SettingsScreen
@@ -26,19 +28,48 @@ import kotlinx.coroutines.launch
2628
class SettingsPresenter @AssistedInject constructor(
2729
@Assisted val navigator: Navigator,
2830
private val authRepository: AuthRepository,
31+
private val remoteConfigRepository: RemoteConfigRepository,
2932
) : Presenter<SettingsUiState> {
3033

3134
@Composable
3235
override fun present(): SettingsUiState {
3336
val scope = rememberCoroutineScope()
3437
var isLoading by rememberRetained { mutableStateOf(false) }
35-
var sideEffect by rememberRetained { mutableStateOf<SettingsSideEffect?>(null) }
3638
var isLogoutDialogVisible by rememberRetained { mutableStateOf(false) }
3739
var isWithdrawBottomSheetVisible by rememberRetained { mutableStateOf(false) }
3840
var isWithdrawConfirmed by rememberRetained { mutableStateOf(false) }
41+
var latestVersion by rememberRetained { mutableStateOf("") }
42+
var sideEffect by rememberRetained { mutableStateOf<SettingsSideEffect?>(null) }
43+
44+
suspend fun getLatestVersion() {
45+
try {
46+
isLoading = true
47+
remoteConfigRepository.getLatestVersion()
48+
.onSuccess { version ->
49+
latestVersion = version
50+
}
51+
.onFailure { exception ->
52+
val handleErrorMessage = { message: String ->
53+
Logger.e(message)
54+
sideEffect = SettingsSideEffect.ShowToast(message)
55+
}
56+
57+
handleException(
58+
exception = exception,
59+
onError = handleErrorMessage,
60+
)
61+
}
62+
} finally {
63+
isLoading = false
64+
}
65+
}
3966

4067
fun handleEvent(event: SettingsUiEvent) {
4168
when (event) {
69+
is SettingsUiEvent.InitSideEffect -> {
70+
sideEffect = null
71+
}
72+
4273
is SettingsUiEvent.OnBackClick -> {
4374
navigator.pop()
4475
}
@@ -134,11 +165,17 @@ class SettingsPresenter @AssistedInject constructor(
134165
}
135166
}
136167
}
168+
169+
LaunchedEffect(Unit) {
170+
getLatestVersion()
171+
}
172+
137173
return SettingsUiState(
138174
isLoading = isLoading,
139175
isLogoutDialogVisible = isLogoutDialogVisible,
140176
isWithdrawBottomSheetVisible = isWithdrawBottomSheetVisible,
141177
isWithdrawConfirmed = isWithdrawConfirmed,
178+
latestVersion = latestVersion,
142179
sideEffect = sideEffect,
143180
eventSink = ::handleEvent,
144181
)

feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsUi.kt

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ internal fun SettingsUi(
4747
state: SettingsUiState,
4848
modifier: Modifier = Modifier,
4949
) {
50-
HandleSettingsSideEffects(state = state)
50+
HandleSettingsSideEffects(
51+
state = state,
52+
eventSink = state.eventSink,
53+
)
5154

5255
val withDrawSheetState = rememberModalBottomSheetState()
5356
val coroutineScope = rememberCoroutineScope()
@@ -127,6 +130,13 @@ internal fun SettingsUi(
127130
color = ReedTheme.colors.contentBrand,
128131
)
129132
},
133+
description = {
134+
Text(
135+
text = stringResource(R.string.latest_version, state.latestVersion),
136+
color = ReedTheme.colors.contentTertiary,
137+
style = ReedTheme.typography.label1Medium,
138+
)
139+
},
130140
)
131141
ReedDivider(modifier = Modifier.padding(vertical = ReedTheme.spacing.spacing4))
132142
SettingItem(
@@ -199,6 +209,7 @@ private fun SettingItem(
199209
isClickable: Boolean = true,
200210
onItemClick: () -> Unit = {},
201211
action: @Composable () -> Unit = {},
212+
description: @Composable () -> Unit = {},
202213
) {
203214
val combinedModifier = if (isClickable) {
204215
modifier
@@ -208,21 +219,27 @@ private fun SettingItem(
208219
modifier.fillMaxWidth()
209220
}
210221

211-
Row(
212-
modifier = combinedModifier
213-
.padding(
214-
horizontal = ReedTheme.spacing.spacing5,
215-
vertical = ReedTheme.spacing.spacing4,
216-
),
217-
verticalAlignment = Alignment.CenterVertically,
218-
) {
219-
Text(
220-
modifier = Modifier.weight(1f),
221-
text = title,
222-
style = ReedTheme.typography.body1Medium,
223-
color = ReedTheme.colors.contentPrimary,
224-
)
225-
action()
222+
Column {
223+
Row(
224+
modifier = combinedModifier
225+
.padding(
226+
horizontal = ReedTheme.spacing.spacing5,
227+
vertical = ReedTheme.spacing.spacing4,
228+
),
229+
verticalAlignment = Alignment.CenterVertically,
230+
) {
231+
Column(
232+
modifier = Modifier.weight(1f),
233+
) {
234+
Text(
235+
text = title,
236+
style = ReedTheme.typography.body1Medium,
237+
color = ReedTheme.colors.contentPrimary,
238+
)
239+
description()
240+
}
241+
action()
242+
}
226243
}
227244
}
228245

feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsUiState.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ data class SettingsUiState(
1010
val isLogoutDialogVisible: Boolean = false,
1111
val isWithdrawBottomSheetVisible: Boolean = false,
1212
val isWithdrawConfirmed: Boolean = false,
13+
val latestVersion: String = "",
1314
val sideEffect: SettingsSideEffect? = null,
1415
val eventSink: (SettingsUiEvent) -> Unit,
1516
) : CircuitUiState
@@ -23,6 +24,7 @@ sealed interface SettingsSideEffect {
2324
}
2425

2526
sealed interface SettingsUiEvent : CircuitUiEvent {
27+
data object InitSideEffect : SettingsUiEvent
2628
data object OnBackClick : SettingsUiEvent
2729
data object OnPolicyClick : SettingsUiEvent
2830
data object OnTermClick : SettingsUiEvent

feature/settings/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@
1414
<string name="settings_cancel">취소</string>
1515
<string name="settings_withdraw_action">탈퇴하기</string>
1616
<string name="oss_licenses_title">오픈소스 라이선스</string>
17+
<string name="latest_version">최신 버전 %1$s</string>
1718
</resources>

0 commit comments

Comments
 (0)