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
1 change: 1 addition & 0 deletions core/design-system/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@
<string name="onboarding_profile_helper">닉네임 2~8자</string>
<string name="onboarding_profile_button_title">완료</string>
<string name="onboarding_profile_invalid_name_length_toast">2자에서 8자 이내로 닉네임을 입력해주세요.</string>
<string name="onboarding_anniversary_already_registered_toast">짝꿍이 기념일을 먼저 등록했어요</string>
<string name="onboarding_profile_setup_fail">프로필 설정 요청에 실패했습니다.</string>

<string name="onboarding_couple_connect_description">짝꿍과 연결하고\n함께 키피럽 시작하세요</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class OnBoardingViewModel(
private val onBoardingRepository: OnBoardingRepository,
private val notificationRepository: NotificationRepository,
) : BaseViewModel<OnBoardingUiState, OnBoardingIntent, OnBoardingSideEffect>(OnBoardingUiState()) {
private var pollingJob: Job? = null
private var onboardingStatusJob: Job? = null
private var connectCoupleJob: Job? = null

init {
Expand Down Expand Up @@ -70,6 +70,8 @@ class OnBoardingViewModel(
// 디데이 설정 화면
is OnBoardingIntent.SelectDate -> reduceDday(intent.value)
OnBoardingIntent.SubmitDday -> anniversarySetup()
OnBoardingIntent.StartDdayPollingStatus -> startDdayPolling()
OnBoardingIntent.StopDdayPollingStatus -> stopPolling()

is OnBoardingIntent.SubmitMarketingConsent ->
initNotificationSettings(
Expand All @@ -81,31 +83,44 @@ class OnBoardingViewModel(
}

private fun startPolling() {
if (pollingJob?.isActive == true) return
pollingJob =
startStatusPolling { status ->
if (status == OnboardingStatus.COUPLE_CONNECTION) return@startStatusPolling false

emitSideEffect(OnBoardingSideEffect.CoupleConnection.NavigateToNext)
true
}
}

private fun startDdayPolling() {
startStatusPolling { status ->
if (status != OnboardingStatus.COMPLETED) return@startStatusPolling false

showAnniversaryAlreadyRegisteredToast()
emitSideEffect(OnBoardingSideEffect.DdaySetting.NavigateToHome)
true
}
}

private fun startStatusPolling(onStatusFetched: suspend (OnboardingStatus) -> Boolean) {
if (onboardingStatusJob?.isActive == true) return
onboardingStatusJob =
viewModelScope.launch {
/**
* 네트워크 오류 등으로 API 호출이 연속으로 실패한 횟수
* 성공 응답을 받으면 0으로 리셋되며, MAX_POLLING_FAILURE_COUNT에 도달하면 폴링을 중단한다.
* 일시적인 오류에는 폴링을 유지하되, 지속적인 오류 상황에서 무한 루프를 방지하기 위해 사용한다.
* **/
var consecutiveFailureCount = 0

while (isActive) {
delay(POLLING_INTERVAL_MS)
when (val result = onBoardingRepository.fetchOnboardingStatus()) {
is AppResult.Success -> {
consecutiveFailureCount = 0
if (result.data != OnboardingStatus.COUPLE_CONNECTION) {
stopPolling()
emitSideEffect(OnBoardingSideEffect.CoupleConnection.NavigateToNext)
if (onStatusFetched(result.data)) {
onboardingStatusJob = null
break
}
}

is AppResult.Error -> {
if (++consecutiveFailureCount >= MAX_POLLING_FAILURE_COUNT) {
stopPolling()
onboardingStatusJob = null
break
}
}
Expand All @@ -115,8 +130,8 @@ class OnBoardingViewModel(
}

private fun stopPolling() {
pollingJob?.cancel()
pollingJob = null
onboardingStatusJob?.cancel()
onboardingStatusJob = null
}

private fun reduceInviteCode(value: String) {
Expand Down Expand Up @@ -198,21 +213,29 @@ class OnBoardingViewModel(
launchResult(
block = { onBoardingRepository.fetchOnboardingStatus() },
onSuccess = { onboardingStatus ->
val sideEffect =
when (onboardingStatus) {
OnboardingStatus.ANNIVERSARY_SETUP ->
OnBoardingSideEffect.ProfileSetting.NavigateToNext
when (onboardingStatus) {
OnboardingStatus.ANNIVERSARY_SETUP ->
tryEmitSideEffect(OnBoardingSideEffect.ProfileSetting.NavigateToNext)

OnboardingStatus.COMPLETED ->
OnBoardingSideEffect.ProfileSetting.NavigateToHome
OnboardingStatus.COMPLETED ->
onProfileAnniversaryAlreadyRegistered()

else -> return@launchResult
}
tryEmitSideEffect(sideEffect)
else -> return@launchResult
}
},
)
}

private fun onProfileAnniversaryAlreadyRegistered() {
tryEmitSideEffect(
OnBoardingSideEffect.ShowToast(
message = R.string.onboarding_anniversary_already_registered_toast,
type = ToastType.DEFAULT,
),
)
tryEmitSideEffect(OnBoardingSideEffect.ProfileSetting.NavigateToHome)
}

private fun reduceDday(value: LocalDate) {
reduce {
copy(
Expand Down Expand Up @@ -255,6 +278,10 @@ class OnBoardingViewModel(
emitSideEffect(OnBoardingSideEffect.ShowToast(message, type))
}

private suspend fun showAnniversaryAlreadyRegisteredToast() {
showToast(R.string.onboarding_anniversary_already_registered_toast, ToastType.DEFAULT)
}

companion object {
private const val ALREADY_USED_INVITE_CODE_MESSAGE = "이미 사용된 초대 코드입니다."
private const val INVALID_INVITE_CODE_MESSAGE = "유효하지 않은 초대 코드입니다."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ sealed interface OnBoardingIntent : Intent {
data object StartPollingStatus : OnBoardingIntent

data object StopPollingStatus : OnBoardingIntent

data object StartDdayPollingStatus : OnBoardingIntent

data object StopDdayPollingStatus : OnBoardingIntent
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand Down Expand Up @@ -54,6 +55,13 @@ fun DdayRoute(
val currentContext by rememberUpdatedState(context)
var showCalendarBottomSheet by remember { mutableStateOf(false) }

DisposableEffect(Unit) {
viewModel.dispatch(OnBoardingIntent.StartDdayPollingStatus)
onDispose {
viewModel.dispatch(OnBoardingIntent.StopDdayPollingStatus)
}
}

ObserveAsEvents(viewModel.sideEffect) { sideEffect ->
when (sideEffect) {
OnBoardingSideEffect.DdaySetting.NavigateToHome -> navigateToHome()
Expand Down