Skip to content

Commit

Permalink
Merge pull request #40 from Nexters/feature/fix-onbaording-flow
Browse files Browse the repository at this point in the history
OnBoardingPageUiState ๊ตฌํ˜„, ์˜จ๋ณด๋”ฉ api ๊ณ„์† ํ˜ธ์ถœํ•˜๋Š” ์ด์Šˆ ์ˆ˜์ •
  • Loading branch information
DwEnn authored Sep 9, 2023
2 parents 9e8870e + e3de655 commit 8d0de55
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.LottieConstants
Expand All @@ -34,58 +34,45 @@ import com.keyme.presentation.onboarding.guide.Guide03Screen
import com.keyme.presentation.onboarding.guide.Guide04Screen
import com.keyme.presentation.onboarding.nickname.NicknameScreen
import com.keyme.presentation.onboarding.signin.SignInScreen
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

@Composable
fun OnboardingRoute(
viewModel: OnboardingViewModel = hiltViewModel(),
navigateToOnboardingKeymeTest: (testId: Int) -> Unit,
navigateToMyDaily: () -> Unit,
) {
val onBoardingPageUiState by viewModel.onBoardingPageUiState.collectAsStateWithLifecycle()

OnboardingScreen(
onBoardingPageUiState = onBoardingPageUiState,
navigateToOnboardingKeymeTest = navigateToOnboardingKeymeTest,
navigateToMyDaily = navigateToMyDaily,
signInWithKeyme = viewModel::signInWithKeyme,
onPageIndexChanged = viewModel::onPageIndexChange,
)
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun OnboardingScreen(
viewModel: OnboardingViewModel = hiltViewModel(),
onBoardingPageUiState: OnBoardingPageUiState,
navigateToOnboardingKeymeTest: (testId: Int) -> Unit,
navigateToMyDaily: () -> Unit,
signInWithKeyme: (String) -> Unit,
onPageIndexChanged: (Int) -> Unit,
) {
val coroutineScope = rememberCoroutineScope()
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.animation_signin_background))

val pagerState = rememberPagerState(initialPage = 0)
val onboardingSteps = listOf(
OnboardingStepsEnum.KAKAO_SIGN_IN,
OnboardingStepsEnum.NICKNAME,
OnboardingStepsEnum.GUIDE_01,
OnboardingStepsEnum.GUIDE_02,
OnboardingStepsEnum.GUIDE_03,
OnboardingStepsEnum.GUIDE_04,
)

LaunchedEffect(key1 = Unit) {
viewModel.userAuthState
.collectLatest {
when {
it?.accessToken == null -> pagerState.scrollToPage(OnboardingStepsEnum.KAKAO_SIGN_IN.ordinal)
it.nickname.isNullOrBlank() -> pagerState.scrollToPage(OnboardingStepsEnum.NICKNAME.ordinal)
it.onboardingTestResultId == null -> pagerState.scrollToPage(OnboardingStepsEnum.GUIDE_01.ordinal)
else -> navigateToMyDaily.invoke()
}
}
LaunchedEffect(key1 = onBoardingPageUiState) {
pagerState.scrollToPage(onBoardingPageUiState.currentPage.ordinal)
}

when (pagerState.currentPage) {
0 -> BackHandler(enabled = true) { /*TODO*/ }
else -> BackHandler(enabled = true) {
coroutineScope.launch {
pagerState.scrollToPage(pagerState.currentPage - 1)
}
onPageIndexChanged(pagerState.currentPage - 1)
}
}

Expand All @@ -107,53 +94,48 @@ fun OnboardingScreen(
)

HorizontalPager(
pageCount = onboardingSteps.size,
pageCount = onBoardingPageUiState.pageSize,
state = pagerState,
userScrollEnabled = false,
) {
val isVisible = it == pagerState.currentPage

when (onboardingSteps[it]) {
OnboardingStepsEnum.KAKAO_SIGN_IN -> SignInScreen(
signInWithKeyme = viewModel::signInWithKeyme,
)
when (OnboardingStepsEnum.onboardingSteps[it]) {
OnboardingStepsEnum.KAKAO_SIGN_IN -> SignInScreen(signInWithKeyme)

OnboardingStepsEnum.NICKNAME -> NicknameScreen(
isVisible = isVisible,
onBackClick = {
coroutineScope.launch {
pagerState.scrollToPage(pagerState.currentPage - 1)
}
onPageIndexChanged(pagerState.currentPage - 1)
},
)

OnboardingStepsEnum.GUIDE_01 -> Guide01Screen(
isVisible = isVisible,
getOnboardingKeymeTestId = viewModel::getOnboardingKeymeTest,
onClickNextButton = {
coroutineScope.launch {
pagerState.scrollToPage(pagerState.currentPage + 1)
}
onPageIndexChanged(pagerState.currentPage + 1)
},
)

OnboardingStepsEnum.GUIDE_02 -> Guide02Screen(
isVisible = isVisible,
onClickNextButton = {
coroutineScope.launch {
pagerState.scrollToPage(pagerState.currentPage + 1)
}
onPageIndexChanged(pagerState.currentPage + 1)
},
)

OnboardingStepsEnum.GUIDE_03 -> Guide03Screen(
isVisible = isVisible,
onClickNextButton = {
coroutineScope.launch {
pagerState.scrollToPage(pagerState.currentPage + 1)
}
onPageIndexChanged(pagerState.currentPage + 1)
},
)

OnboardingStepsEnum.GUIDE_04 -> Guide04Screen(
isVisible = isVisible,
navigateToOnboardingKeymeTest = navigateToOnboardingKeymeTest,
)

OnboardingStepsEnum.MY_DAILY -> navigateToMyDaily.invoke()
}
}
Expand All @@ -165,6 +147,7 @@ fun fadingAnimateFloatAsState(isAnimationFinished: Boolean): State<Float> {
return animateFloatAsState(
targetValue = if (isAnimationFinished) 1f else 0f,
animationSpec = tween(durationMillis = 500),
label = "",
)
}

Expand All @@ -173,6 +156,7 @@ fun colorAnimateFloatAsState(isVisible: Boolean): State<Color> {
return animateColorAsState(
targetValue = if (isVisible) keyme_black else black_alpha_60,
animationSpec = tween(durationMillis = 1000),
label = "",
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,16 @@ package com.keyme.presentation.onboarding

enum class OnboardingStepsEnum {
KAKAO_SIGN_IN, NICKNAME, GUIDE_01, GUIDE_02, GUIDE_03, GUIDE_04, MY_DAILY,
;

companion object {
val onboardingSteps = listOf(
KAKAO_SIGN_IN,
NICKNAME,
GUIDE_01,
GUIDE_02,
GUIDE_03,
GUIDE_04,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import timber.log.Timber
Expand All @@ -42,9 +46,13 @@ class OnboardingViewModel @Inject constructor(
private val getOnboardingKeymeTestUseCase: GetOnboardingKeymeTestUseCase,
) : BaseViewModel() {

val userAuthState: StateFlow<UserAuth?> =
private val userAuthState: StateFlow<UserAuth?> =
getUserAuthUseCase.getUserAuth().stateIn(viewModelScope, SharingStarted.Lazily, null)

private val _onBoardingPageUiState =
MutableStateFlow(OnBoardingPageUiState(OnboardingStepsEnum.KAKAO_SIGN_IN))
val onBoardingPageUiState = _onBoardingPageUiState.asStateFlow()

private val _remoteOnboardingState = MutableStateFlow(OnboardingStepsEnum.KAKAO_SIGN_IN.ordinal)
val remoteOnboardingState: StateFlow<Int> = _remoteOnboardingState.asStateFlow()

Expand All @@ -58,6 +66,23 @@ class OnboardingViewModel @Inject constructor(
private val _onboardingKeymeTestState = MutableStateFlow<Test?>(null)
val onboardingKeymeTestState: StateFlow<Test?> = _onboardingKeymeTestState.asStateFlow()

init {
userAuthState.map {
when {
it?.accessToken == null -> OnboardingStepsEnum.KAKAO_SIGN_IN
it.nickname.isNullOrBlank() -> OnboardingStepsEnum.NICKNAME
it.onboardingTestResultId == null -> {
getOnboardingKeymeTest()
OnboardingStepsEnum.GUIDE_01
}

else -> null
}
}.filterNotNull().onEach {
_onBoardingPageUiState.value = OnBoardingPageUiState(it)
}.launchIn(baseViewModelScope)
}

fun signInWithKeyme(
token: String,
) {
Expand Down Expand Up @@ -91,9 +116,11 @@ class OnboardingViewModel @Inject constructor(
fun uploadProfileImage(
imageString: String,
) {
apiCall(apiRequest = {
uploadProfileImageUseCase.invoke(imageString = imageString)
},) {
apiCall(
apiRequest = {
uploadProfileImageUseCase.invoke(imageString = imageString)
},
) {
_uploadProfileImageState.emit(it)
}
}
Expand All @@ -105,13 +132,15 @@ class OnboardingViewModel @Inject constructor(
) {
// todo ํ”„๋กœํ•„ ์‚ฌ์ง„ ์„ ํƒ
// if (uploadProfileImageState.value == null) return
apiCall(apiRequest = {
updateMemberUseCase.invoke(
nickname = nickname,
profileImage = originalUrl,
profileThumbnail = thumbnailUrl,
)
},) {
apiCall(
apiRequest = {
updateMemberUseCase.invoke(
nickname = nickname,
profileImage = originalUrl,
profileThumbnail = thumbnailUrl,
)
},
) {
insertUserAuthUseCase.invoke(
UserAuth(
id = it.id,
Expand All @@ -124,20 +153,26 @@ class OnboardingViewModel @Inject constructor(
),
)
FcmUtil.getToken()?.let { token ->
insertPushTokenUseCase.invoke(token)
.onSuccess {
setPushTokenSavedStateUseCase.invoke(true)
}
.onFailure {
setPushTokenSavedStateUseCase.invoke(false)
}
insertPushTokenUseCase.invoke(token).onSuccess {
setPushTokenSavedStateUseCase.invoke(true)
}.onFailure {
setPushTokenSavedStateUseCase.invoke(false)
}
}
}
}

fun getOnboardingKeymeTest() {
private fun getOnboardingKeymeTest() {
apiCall(apiRequest = { getOnboardingKeymeTestUseCase.invoke() }) {
_onboardingKeymeTestState.emit(it)
}
}

fun onPageIndexChange(index: Int) {
_onBoardingPageUiState.value = OnBoardingPageUiState(OnboardingStepsEnum.values()[index])
}
}

data class OnBoardingPageUiState(val currentPage: OnboardingStepsEnum) {
val pageSize: Int = OnboardingStepsEnum.values().size
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,13 @@ import com.keyme.presentation.onboarding.fadingAnimateFloatAsState
@Composable
fun Guide01Screen(
isVisible: Boolean,
getOnboardingKeymeTestId: () -> Unit,
onClickNextButton: () -> Unit,
) {
val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(R.raw.animation_guide_01))
val progress by animateLottieCompositionAsState(composition)
var isAnimationFinished by remember { mutableStateOf(false) }
isAnimationFinished = progress == 1.0f

getOnboardingKeymeTestId.invoke()

Box(
modifier = Modifier
.fillMaxSize()
Expand Down Expand Up @@ -92,7 +89,6 @@ fun Guide01Screen(
fun Guide01ScreenPreview() {
Guide01Screen(
isVisible = true,
getOnboardingKeymeTestId = {},
onClickNextButton = {},
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.keyme.presentation.onboarding.guide

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
Expand All @@ -10,6 +11,7 @@ import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
Expand Down Expand Up @@ -51,13 +53,18 @@ fun Guide04Screen(
.fillMaxWidth()
.wrapContentSize()
.align(Alignment.Center)
.clickable {
if (progress == 1.0f &&
onboardingKeymeTest.value?.testId != null &&
onboardingKeymeTest.value?.testId != 0
) {
navigateToOnboardingKeymeTest(onboardingKeymeTest.value!!.testId)
}
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
) {
// NOTE : ํด๋ฆญ enable ๊นŒ์ง€ ๋„ˆ๋ฌด ์˜ค๋ž˜ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ ๊ฐ™์•„์„œ disable ํ•ด๋‘ 
// if (progress == 1.0f &&
// onboardingKeymeTest.value?.testId != null &&
// onboardingKeymeTest.value?.testId != 0
// ) {
// navigateToOnboardingKeymeTest(onboardingKeymeTest.value!!.testId)
// }
navigateToOnboardingKeymeTest(onboardingKeymeTest.value!!.testId)
},
iterations = LottieConstants.IterateForever,
contentScale = ContentScale.Crop,
Expand Down

0 comments on commit 8d0de55

Please sign in to comment.