diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 235114872..75332c1b4 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -192,6 +192,7 @@ 닉네임 2~8자 완료 2자에서 8자 이내로 닉네임을 입력해주세요. + 짝꿍이 기념일을 먼저 등록했어요 프로필 설정 요청에 실패했습니다. 짝꿍과 연결하고\n함께 키피럽 시작하세요 diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/twix/onboarding/OnBoardingViewModel.kt index c27b82fc7..497735b6c 100644 --- a/feature/onboarding/src/main/java/com/twix/onboarding/OnBoardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/twix/onboarding/OnBoardingViewModel.kt @@ -23,7 +23,7 @@ class OnBoardingViewModel( private val onBoardingRepository: OnBoardingRepository, private val notificationRepository: NotificationRepository, ) : BaseViewModel(OnBoardingUiState()) { - private var pollingJob: Job? = null + private var onboardingStatusJob: Job? = null private var connectCoupleJob: Job? = null init { @@ -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( @@ -81,14 +83,28 @@ 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) { @@ -96,16 +112,15 @@ class OnBoardingViewModel( 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 } } @@ -115,8 +130,8 @@ class OnBoardingViewModel( } private fun stopPolling() { - pollingJob?.cancel() - pollingJob = null + onboardingStatusJob?.cancel() + onboardingStatusJob = null } private fun reduceInviteCode(value: String) { @@ -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( @@ -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 = "유효하지 않은 초대 코드입니다." diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/contract/OnBoardingIntent.kt b/feature/onboarding/src/main/java/com/twix/onboarding/contract/OnBoardingIntent.kt index 72fcb4aa8..debbc4251 100644 --- a/feature/onboarding/src/main/java/com/twix/onboarding/contract/OnBoardingIntent.kt +++ b/feature/onboarding/src/main/java/com/twix/onboarding/contract/OnBoardingIntent.kt @@ -35,4 +35,8 @@ sealed interface OnBoardingIntent : Intent { data object StartPollingStatus : OnBoardingIntent data object StopPollingStatus : OnBoardingIntent + + data object StartDdayPollingStatus : OnBoardingIntent + + data object StopDdayPollingStatus : OnBoardingIntent } diff --git a/feature/onboarding/src/main/java/com/twix/onboarding/dday/DdayRoute.kt b/feature/onboarding/src/main/java/com/twix/onboarding/dday/DdayRoute.kt index 50c9c139a..af0e01c3c 100644 --- a/feature/onboarding/src/main/java/com/twix/onboarding/dday/DdayRoute.kt +++ b/feature/onboarding/src/main/java/com/twix/onboarding/dday/DdayRoute.kt @@ -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 @@ -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()