From 4d5cc845344193f920a1e8fb6315288a45c121d7 Mon Sep 17 00:00:00 2001 From: Ray Jang Date: Sun, 28 Jan 2024 16:56:03 +0900 Subject: [PATCH] =?UTF-8?q?[Feature]=20OnBoardingScreen=20UI=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/common/view/ConfirmButton.kt | 4 +- .../login/onboarding/LoginOnBoardingEvent.kt | 4 +- .../login/onboarding/LoginOnBoardingIntent.kt | 4 +- .../login/onboarding/LoginOnBoardingModel.kt | 3 +- .../login/onboarding/LoginOnBoardingScreen.kt | 102 ++++++------------ .../login/onboarding/LoginOnBoardingState.kt | 13 +-- .../onboarding/LoginOnBoardingViewModel.kt | 21 +--- 7 files changed, 41 insertions(+), 110 deletions(-) diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/ConfirmButton.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/ConfirmButton.kt index 6275b2ab..d69e539c 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/ConfirmButton.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/common/view/ConfirmButton.kt @@ -1,6 +1,5 @@ package ac.dnd.bookkeeping.android.presentation.common.view -import ac.dnd.bookkeeping.android.presentation.R import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -12,7 +11,6 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -81,7 +79,7 @@ fun ConfirmButtonPreview2() { @Composable fun ConfirmButtonPreview3() { ConfirmButton( - text = stringResource(R.string.next_button_text), + text = "다음", isMain = true, modifier = Modifier .padding(20.dp) diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingEvent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingEvent.kt index 639a07a1..2a31644a 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingEvent.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingEvent.kt @@ -1,5 +1,3 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.login.onboarding -sealed interface LoginOnBoardingEvent { - data object GoToNextStep : LoginOnBoardingEvent -} +sealed interface LoginOnBoardingEvent diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingIntent.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingIntent.kt index 48e4a4dc..07c09dae 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingIntent.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingIntent.kt @@ -1,5 +1,3 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.login.onboarding -sealed interface LoginOnBoardingIntent { - data object OnClickNextStep : LoginOnBoardingIntent -} +sealed interface LoginOnBoardingIntent diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingModel.kt index 07a62d58..c556af7f 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingModel.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingModel.kt @@ -1,6 +1,5 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.login.onboarding class LoginOnBoardingModel( - val loadingState: LoginOnBoardingState.Loading, - val buttonState: LoginOnBoardingState.Button + val state: LoginOnBoardingState ) diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingScreen.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingScreen.kt index 0811483a..7e5bd14d 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingScreen.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingScreen.kt @@ -5,6 +5,7 @@ import ac.dnd.bookkeeping.android.presentation.common.util.LaunchedEffectWithLif import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.EventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.MutableEventFlow import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.eventObserve +import ac.dnd.bookkeeping.android.presentation.common.view.ConfirmButton import ac.dnd.bookkeeping.android.presentation.ui.main.ApplicationState import ac.dnd.bookkeeping.android.presentation.ui.main.home.HomeConstant import ac.dnd.bookkeeping.android.presentation.ui.main.login.LoginConstant @@ -27,25 +28,21 @@ import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.launch +import kotlinx.coroutines.plus @OptIn(ExperimentalFoundationApi::class) @Composable @@ -56,25 +53,11 @@ fun LoginOnBoardingScreen( intent: (LoginOnBoardingIntent) -> Unit, handler: CoroutineExceptionHandler ) { - var currentSelectedPage by rememberSaveable { mutableIntStateOf(0) } - val pagerState = rememberPagerState(pageCount = { 3 }) - val buttonColorState = animateColorAsState( - targetValue = when (model.buttonState) { - is LoginOnBoardingState.Button.Default -> { - Color(0xFFDD55FF) - } - - is LoginOnBoardingState.Button.Pressed -> { - Color(0xFFD401FE) - } - }, - label = "pressed button color" + val scope = rememberCoroutineScope() + handler + val pagerState = rememberPagerState( + pageCount = { 3 } ) - LaunchedEffect(currentSelectedPage) { - pagerState.animateScrollToPage(currentSelectedPage) - } - fun navigateToHome() { appState.navController.navigate(HomeConstant.ROUTE) { popUpTo(LoginConstant.ROUTE) { @@ -82,7 +65,6 @@ fun LoginOnBoardingScreen( } } } - Box( modifier = Modifier .fillMaxSize() @@ -95,39 +77,34 @@ fun LoginOnBoardingScreen( horizontalAlignment = Alignment.CenterHorizontally ) { HorizontalPager( - state = pagerState + state = pagerState, ) { page -> - when (model.loadingState) { - is LoginOnBoardingState.Loading.Progress -> { - SampleImageComponent(page.toString()) - } - - is LoginOnBoardingState.Loading.Success -> { - - } - - is LoginOnBoardingState.Loading.Failure -> { - - } - } + LoginOnBoardingPage(page.toString()) } + Spacer(Modifier.height(29.18.dp)) + Row( Modifier.wrapContentSize(), horizontalArrangement = Arrangement.spacedBy(14.dp) ) { - repeat(pagerState.pageCount) { iterationIndex -> + repeat(pagerState.pageCount) { index -> + val isSelected = pagerState.currentPage == index + val color by animateColorAsState( - targetValue = - if (pagerState.currentPage == iterationIndex) Color.DarkGray - else Color.LightGray, + targetValue = if (isSelected) Color.DarkGray else Color.LightGray, label = "iteration color" ) + Box( modifier = Modifier .padding(2.dp) .clip(CircleShape) - .clickable { currentSelectedPage = iterationIndex } + .clickable { + scope.launch { + pagerState.animateScrollToPage(index) + } + } .background(color) .size(8.dp) ) @@ -135,41 +112,29 @@ fun LoginOnBoardingScreen( } } - Surface( + ConfirmButton( + text = stringResource(R.string.next_button_text), + isMain = true, modifier = Modifier - .align(Alignment.BottomCenter) - .padding( - horizontal = 20.dp, - vertical = 12.dp - ) .fillMaxWidth() - .clickable { - intent(LoginOnBoardingIntent.OnClickNextStep) - }, - shape = RoundedCornerShape(8.dp), - color = buttonColorState.value + .align(Alignment.BottomCenter) + .padding(25.dp), ) { - Text( - text = stringResource(R.string.next_button_text), - fontSize = 16.sp, - color = Color.White, - textAlign = TextAlign.Center, - modifier = Modifier.padding(14.dp) - ) + navigateToHome() } } LaunchedEffectWithLifecycle(event, handler) { event.eventObserve { event -> - when (event) { - is LoginOnBoardingEvent.GoToNextStep -> navigateToHome() - } + } } } @Composable -fun SampleImageComponent(contentIndex: String) { +private fun LoginOnBoardingPage( + text: String +) { Box( modifier = Modifier .fillMaxWidth() @@ -179,7 +144,7 @@ fun SampleImageComponent(contentIndex: String) { contentAlignment = Alignment.Center ) { Text( - text = contentIndex, + text = text, fontSize = 16.sp, color = Color.Black, ) @@ -192,8 +157,7 @@ fun LoginOnBoardingScreenPreview() { LoginOnBoardingScreen( appState = rememberApplicationState(), model = LoginOnBoardingModel( - loadingState = LoginOnBoardingState.Loading.Progress, - buttonState = LoginOnBoardingState.Button.Default + state = LoginOnBoardingState.Init ), event = MutableEventFlow(), intent = {}, diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingState.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingState.kt index 631a92ae..f6dd51e0 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingState.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingState.kt @@ -1,16 +1,5 @@ package ac.dnd.bookkeeping.android.presentation.ui.main.login.onboarding -import ac.dnd.bookkeeping.android.domain.model.error.ServerException - sealed interface LoginOnBoardingState { - sealed interface Loading { - data object Progress : Loading - data object Success : Loading - data class Failure(val exception: ServerException) : Loading - } - - sealed interface Button { - data object Default : Button - data object Pressed : Button - } + data object Init : LoginOnBoardingState } diff --git a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingViewModel.kt b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingViewModel.kt index 8d628b13..e937c3df 100644 --- a/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingViewModel.kt +++ b/presentation/src/main/kotlin/ac/dnd/bookkeeping/android/presentation/ui/main/login/onboarding/LoginOnBoardingViewModel.kt @@ -6,7 +6,6 @@ import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.Mutab import ac.dnd.bookkeeping.android.presentation.common.util.coroutine.event.asEventFlow import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -17,28 +16,14 @@ class LoginOnBoardingViewModel @Inject constructor( private val savedStateHandle: SavedStateHandle, ) : BaseViewModel() { - private val _loadingState: MutableStateFlow = - MutableStateFlow(LoginOnBoardingState.Loading.Progress) - val loadingState: StateFlow = _loadingState.asStateFlow() - - private val _buttonState: MutableStateFlow = - MutableStateFlow(LoginOnBoardingState.Button.Default) - val buttonState: StateFlow = _buttonState.asStateFlow() + private val _state: MutableStateFlow = + MutableStateFlow(LoginOnBoardingState.Init) + val state: StateFlow = _state.asStateFlow() private val _event: MutableEventFlow = MutableEventFlow() val event: EventFlow = _event.asEventFlow() fun onIntent(intent: LoginOnBoardingIntent) { - when (intent) { - LoginOnBoardingIntent.OnClickNextStep -> goToNextStep() - } - } - private fun goToNextStep() { - launch { - _buttonState.emit(LoginOnBoardingState.Button.Pressed) - delay(100L) - _event.emit(LoginOnBoardingEvent.GoToNextStep) - } } }