Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package com.puzzle.data.repository

import com.puzzle.domain.model.configure.ForceUpdate
import com.puzzle.domain.model.configure.MaintenancePeriod
import com.puzzle.domain.repository.ConfigureRepository
import com.puzzle.network.model.configure.GetForceUpdateInfoResponse
import com.puzzle.network.model.configure.GetMaintenancePeriodInfoResponse
import com.puzzle.network.source.configure.ConfigDataSource
import com.puzzle.network.source.configure.ConfigDataSource.Key
import javax.inject.Inject

class ConfigureRepositoryImpl @Inject constructor(
private val configureDataSource: ConfigDataSource,
) : ConfigureRepository {
override suspend fun getUpdateInfo(): ForceUpdate {
return configureDataSource.getReferenceType(
key = Key.getKey(ConfigDataSource.FORCE_UPDATE),
defaultValue = GetForceUpdateInfoResponse(),
).toDomain()
}
override suspend fun getUpdateInfo(): ForceUpdate = configureDataSource.getReferenceType(
key = Key.getKey(ConfigDataSource.FORCE_UPDATE),
defaultValue = GetForceUpdateInfoResponse(),
).toDomain()

override suspend fun getMaintenancePeriod(): MaintenancePeriod = configureDataSource.getReferenceType(
key = Key.getKey(ConfigDataSource.MAINTENANCE_PERIOD),
defaultValue = GetMaintenancePeriodInfoResponse(),
).toDomain()
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ fun PieceDialog(
fun PieceDialogDefaultTop(
title: AnnotatedString,
subText: String,
description: AnnotatedString? = null
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
Expand All @@ -80,13 +81,35 @@ fun PieceDialogDefaultTop(
textAlign = TextAlign.Center,
style = PieceTheme.typography.bodySM,
)

description?.let {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(top = 4.dp)
.fillMaxWidth()
.background(PieceTheme.colors.light3)
.padding(vertical = 12.dp),
) {
Text(
text = it,
color = PieceTheme.colors.dark3,
style = PieceTheme.typography.bodySM,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.background(PieceTheme.colors.light3)
)
}
}
}
}

@Composable
fun PieceDialogDefaultTop(
title: AnnotatedString,
subText: AnnotatedString,
description: AnnotatedString? = null
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
Expand All @@ -106,13 +129,35 @@ fun PieceDialogDefaultTop(
textAlign = TextAlign.Center,
style = PieceTheme.typography.bodySM,
)

description?.let {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(top = 4.dp)
.fillMaxWidth()
.background(PieceTheme.colors.light3)
.padding(vertical = 12.dp),
) {
Text(
text = it,
color = PieceTheme.colors.dark3,
style = PieceTheme.typography.bodySM,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.background(PieceTheme.colors.light3)
)
}
}
}
}

@Composable
fun PieceDialogDefaultTop(
title: String,
subText: AnnotatedString,
description: AnnotatedString? = null
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
Expand All @@ -132,13 +177,35 @@ fun PieceDialogDefaultTop(
textAlign = TextAlign.Center,
style = PieceTheme.typography.bodySM,
)

description?.let {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(top = 4.dp)
.fillMaxWidth()
.background(PieceTheme.colors.light3)
.padding(vertical = 12.dp),
) {
Text(
text = it,
color = PieceTheme.colors.dark3,
style = PieceTheme.typography.bodySM,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.background(PieceTheme.colors.light3)
)
}
}
}
}

@Composable
fun PieceDialogDefaultTop(
title: String,
subText: String,
description: AnnotatedString? = null
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
Expand All @@ -158,6 +225,27 @@ fun PieceDialogDefaultTop(
style = PieceTheme.typography.bodySM,
textAlign = TextAlign.Center,
)

description?.let {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(top = 4.dp)
.fillMaxWidth()
.background(PieceTheme.colors.light3)
.padding(vertical = 12.dp),
) {
Text(
text = it,
color = PieceTheme.colors.dark3,
style = PieceTheme.typography.bodySM,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.background(PieceTheme.colors.light3)
)
}
}
}
}

Expand Down Expand Up @@ -346,7 +434,8 @@ fun PreviewPieceDialogDefault() {
dialogTop = {
PieceDialogDefaultTop(
title = AnnotatedString("Default Title"),
subText = "This is a default subtitle"
subText = "This is a default subtitle",
description = AnnotatedString("This is a default description")
)
},
dialogBottom = {
Expand Down
7 changes: 7 additions & 0 deletions core/designsystem/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@
<string name="update_title">Piece가 새로운 버전으로\n업데이트되었어요!</string>
<string name="update_subtext">여러분의 의견을 반영하여 사용성을 개선했습니다.\n지금 바로 업데이트해 보세요!</string>

<!--Maintenance-->
<string name="maintenance_title">Piece가 잠시 쉬어가요!</string>
<string name="maintenance_subtext">대규모 업데이트 작업을 진행하고 있어요.\n새롭게 출시될 기능들을 기대해주세요!</string>
<string name="maintenance_description_prefix">일시 중단 시간:</string>
<string name="maintenance_label">닫기</string>


<!--Alarm-->
<string name="alarm_profile_approved_title">가입이 승인되었어요!</string>
<string name="alarm_profile_approved_content">매일 밤 10시, 설레는 인연을 만나보세요✨</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.puzzle.domain.model.configure

data class MaintenancePeriod (
val maintenancePeriod: String
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.puzzle.domain.repository

import com.puzzle.domain.model.configure.ForceUpdate
import com.puzzle.domain.model.configure.MaintenancePeriod

interface ConfigureRepository {
suspend fun getUpdateInfo(): ForceUpdate
suspend fun getMaintenancePeriod(): MaintenancePeriod
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.puzzle.network.model.configure

import com.puzzle.domain.model.configure.MaintenancePeriod
import kotlinx.serialization.Serializable

@Serializable
data class GetMaintenancePeriodInfoResponse(
val maintenancePeriod: String = ""
) {
fun toDomain() = MaintenancePeriod(
maintenancePeriod = maintenancePeriod
)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package com.puzzle.network.source.configure

import android.util.Log
import com.google.firebase.remoteconfig.ConfigUpdate
import com.google.firebase.remoteconfig.ConfigUpdateListener
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.firebase.remoteconfig.FirebaseRemoteConfigException
import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue
import com.google.firebase.remoteconfig.get
import com.google.firebase.remoteconfig.remoteConfigSettings
import com.puzzle.network.BuildConfig
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.serialization.json.Json
Expand All @@ -16,6 +21,22 @@ class ConfigDataSource @Inject constructor(
private val remoteConfig: FirebaseRemoteConfig,
val json: Json,
) {

init {
val configSettings = remoteConfigSettings {
minimumFetchIntervalInSeconds = 0
}
remoteConfig.setConfigSettingsAsync(configSettings)
remoteConfig.addOnConfigUpdateListener(object : ConfigUpdateListener {
override fun onUpdate(configUpdate: ConfigUpdate) {
remoteConfig.activate()
}
override fun onError(error: FirebaseRemoteConfigException) {
Log.e("ConfigDataSource", "RemoteConfig Update Error: ${error.message}", error)
}
})
}

suspend inline fun <reified T> getReferenceType(key: String, defaultValue: T): T {
return getReferenceType<T>(key) ?: defaultValue
}
Expand Down Expand Up @@ -44,6 +65,7 @@ class ConfigDataSource @Inject constructor(

companion object Key {
const val FORCE_UPDATE = "force_update"
const val MAINTENANCE_PERIOD = "maintenance_period"
fun getKey(key: String): String = "${key}_AN_${BuildConfig.BUILD_TYPE}"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class MainActivity : ComponentActivity() {
CompositionLocalProvider(LocalAnalyticsHelper provides analyticsHelper) {
val scope = rememberCoroutineScope()
val forceUpdate by viewModel.forceUpdate.collectAsStateWithLifecycle()
val maintenancePeriod by viewModel.maintenancePeriod.collectAsStateWithLifecycle()
val userRole by viewModel.userRole.collectAsStateWithLifecycle()
val snackBarHostState = remember { SnackbarHostState() }
val appState = rememberPieceAppState(networkMonitor = networkMonitor)
Expand All @@ -104,6 +105,7 @@ class MainActivity : ComponentActivity() {
PieceApp(
appState = appState,
forceUpdate = forceUpdate,
maintenancePeriod = maintenancePeriod,
snackBarHostState = snackBarHostState,
navigateToBottomNaviDestination = { bottomBarDestination ->
if (isPendingUser(bottomBarDestination, userRole)) {
Expand Down Expand Up @@ -150,6 +152,13 @@ class MainActivity : ComponentActivity() {
handleNotificationIntent(intent)
}

override fun onResume() {
super.onResume()
lifecycleScope.launch {
viewModel.refreshAppStatus()
}
}

private fun handleNotificationIntent(intent: Intent?) {
intent?.getStringExtra(NOTIFCATION_ID)?.let { id ->
viewModel.readNotification(id.toIntOrNull() ?: return@let)
Expand Down
33 changes: 23 additions & 10 deletions presentation/src/main/java/com/puzzle/presentation/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.puzzle.common.event.PieceEvent
import com.puzzle.common.suspendRunCatching
import com.puzzle.common.ui.SnackBarState
import com.puzzle.domain.model.configure.ForceUpdate
import com.puzzle.domain.model.configure.MaintenancePeriod
import com.puzzle.domain.model.error.ErrorHelper
import com.puzzle.domain.model.error.HttpResponseException
import com.puzzle.domain.model.error.HttpResponseStatus
Expand Down Expand Up @@ -50,6 +51,9 @@ class MainViewModel @Inject constructor(
private val _forceUpdate = MutableStateFlow<ForceUpdate?>(null)
val forceUpdate = _forceUpdate.asStateFlow()

private val _maintenancePeriod = MutableStateFlow<MaintenancePeriod?>(null)
val maintenancePeriod = _maintenancePeriod.asStateFlow()

val userRole = userRepository.getUserRole()
.stateIn(
scope = viewModelScope,
Expand Down Expand Up @@ -89,23 +93,32 @@ class MainViewModel @Inject constructor(
}

internal suspend fun initConfigure() = coroutineScope {
val forceUpdateJob = launch { checkMinVersion() }
val loadTermsJob = launch { loadTerms() }
val loadValuePicksJob = launch { loadValuePicks() }
val loadValueTalksJob = launch { loadValueTalks() }

forceUpdateJob.join()
loadTermsJob.join()
loadValuePicksJob.join()
loadValueTalksJob.join()
launch { checkMinVersion() }
launch { checkMaintenancePeriod() }
launch { loadTerms() }
launch { loadValuePicks() }
launch { loadValueTalks() }
}

internal suspend fun refreshAppStatus() = coroutineScope {
launch { checkMinVersion() }
launch { checkMaintenancePeriod() }
}

private suspend fun checkMinVersion() {
suspendRunCatching {
configureRepository.getUpdateInfo()
}.onSuccess {
_forceUpdate.value = it
}.onFailure { errorHelper.sendError(it) }
}.onFailure { errorHelper.recordError(it) }
}

private suspend fun checkMaintenancePeriod() {
suspendRunCatching {
configureRepository.getMaintenancePeriod()
}.onSuccess {
_maintenancePeriod.value = it
}.onFailure { errorHelper.recordError(it) }
}

private suspend fun loadTerms() {
Expand Down
Loading