From 0ba0a2244922bd2eb3ef9e7b3525abfdc4384008 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Thu, 21 May 2026 19:27:38 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=98=A8=EB=B3=B4=EB=94=A9?= =?UTF-8?q?=20=EA=B8=B0=EB=85=90=EC=9D=BC=20=EB=93=B1=EB=A1=9D=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/values/strings.xml | 1 + .../twix/onboarding/OnBoardingViewModel.kt | 73 +++++++++++++------ .../onboarding/contract/OnBoardingIntent.kt | 4 + .../com/twix/onboarding/dday/DdayRoute.kt | 8 ++ 4 files changed, 63 insertions(+), 23 deletions(-) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 23511487..75332c1b 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 c27b82fc..497735b6 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 72fcb4aa..debbc425 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 50c9c139..af0e01c3 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()