From 2154455ceffaabf28df6e45f16db6201e5e1c65b Mon Sep 17 00:00:00 2001 From: JIEUNI Date: Wed, 11 Dec 2024 17:40:22 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[mod]=20#13=20PagerState=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/and/presentation/ui/home/HomeRoute.kt | 13 ++++++++++--- .../sopt/and/presentation/ui/home/HomeScreen.kt | 15 ++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeRoute.kt b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeRoute.kt index de0392a..ba916b5 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeRoute.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeRoute.kt @@ -2,9 +2,11 @@ package org.sopt.and.presentation.ui.home import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -15,19 +17,24 @@ fun HomeRoute( homeViewModel: HomeViewModel = hiltViewModel() ) { val homeState by homeViewModel.uiState.collectAsStateWithLifecycle() + val pagerState = rememberPagerState(pageCount = { homeState.bannerImgList.size }) LaunchedEffect(Unit) { homeViewModel.setHomeImgList() } + LaunchedEffect(pagerState) { + snapshotFlow { pagerState.currentPage }.collect { page -> + homeViewModel.setCurrentBannerPage(page) + } + } + HomeScreen( modifier = Modifier .padding(paddingValues), bannerImgList = homeState.bannerImgList, numPages = homeState.bannerImgList.size.toString(), - onCurrentPageChanged = { page -> - homeViewModel.setCurrentBannerPage(page) - }, + pagerState = pagerState, editorRecommendedImgList = homeState.editorRecommendedList, todayTopRankingImgList = homeState.todayTopRankingList ) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeScreen.kt index ae561e0..04c84cc 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeScreen.kt @@ -16,13 +16,13 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -33,8 +33,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.sopt.and.R -import org.sopt.and.presentation.ui.home.component.TextWithNavigateButton import org.sopt.and.presentation.ui.home.component.HomeAsyncImage +import org.sopt.and.presentation.ui.home.component.TextWithNavigateButton import org.sopt.and.ui.theme.ANDANDROIDTheme import org.sopt.and.ui.theme.Gray100 import org.sopt.and.ui.theme.GrayBlack @@ -44,10 +44,10 @@ import org.sopt.and.ui.theme.White fun HomeScreen( bannerImgList: List, numPages: String, - onCurrentPageChanged: (Int) -> Unit, editorRecommendedImgList: List, todayTopRankingImgList: List, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + pagerState: PagerState = rememberPagerState(pageCount = { 0 }), ) { Column( modifier = modifier @@ -55,15 +55,9 @@ fun HomeScreen( .background(GrayBlack) .verticalScroll(rememberScrollState()) ) { - val pagerState = rememberPagerState(pageCount = { bannerImgList.size }) val horizontalContentPadding = ((LocalConfiguration.current).screenWidthDp * (1F - 0.85F) / 2).dp - // TODO PagerState 자체를 인자로 받고 아래 LaunchedEffect를 HomeRoute로 옮기기 - LaunchedEffect(pagerState.currentPage) { - onCurrentPageChanged(pagerState.currentPage) - } - HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = horizontalContentPadding), @@ -214,7 +208,6 @@ fun HomePreview() { HomeScreen( bannerImgList = listOf(""), numPages = "6", - onCurrentPageChanged = { }, editorRecommendedImgList = listOf(""), todayTopRankingImgList = listOf("") ) From ea9eda5025197c792adf4783d067f057d826fb33 Mon Sep 17 00:00:00 2001 From: JIEUNI Date: Fri, 13 Dec 2024 15:42:50 +0900 Subject: [PATCH 2/4] =?UTF-8?q?[mod]=20#13=20BaseViewModel=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/auth/login/LoginViewModel.kt | 16 ++++++++-------- .../ui/auth/register/RegisterViewModel.kt | 14 +++++++------- .../java/org/sopt/and/util/base/BaseViewModel.kt | 10 ++++++---- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/login/LoginViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/login/LoginViewModel.kt index 7847c4f..8e17cce 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/login/LoginViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/login/LoginViewModel.kt @@ -19,21 +19,21 @@ class LoginViewModel @Inject constructor( } override fun handleEffect(effect: LoginContract.LoginEffect) { - TODO("Not yet implemented") + // TODO("Not yet implemented") } override suspend fun handleEvent(event: LoginContract.LoginEvent) { when (event) { is LoginContract.LoginEvent.UsernameChanged -> { - setState(currentUiState.copy(username = event.username)) + setState{ copy(username = event.username) } } is LoginContract.LoginEvent.PasswordChanged -> { - setState(currentUiState.copy(password = event.password)) + setState{ copy(password = event.password) } } is LoginContract.LoginEvent.PasswordVisibilityChanged -> { - setState(currentUiState.copy(showPassword = !currentUiState.showPassword)) + setState{ copy(showPassword = !currentUiState.showPassword) } } is LoginContract.LoginEvent.OnLoginBtnClicked -> { @@ -44,13 +44,13 @@ class LoginViewModel @Inject constructor( ) ).onSuccess { user -> Timber.d("[로그인] 성공 -> $user") - setEffect(LoginContract.LoginEffect.ShowSuccessSnackBar(successMessage = event.successMessage)) + setEffect{ LoginContract.LoginEffect.ShowSuccessSnackBar(successMessage = event.successMessage) } setAccessToken(user.accessToken) - setState(currentUiState.copy(loginStatus = LoginContract.LoginStatus.Success)) + setState{ copy(loginStatus = LoginContract.LoginStatus.Success) } }.onFailure { Timber.d("[로그인] 실패 -> $it") - setEffect(LoginContract.LoginEffect.ShowFailSnackBar(failMessage = event.failMessage)) - setState(currentUiState.copy(loginStatus = LoginContract.LoginStatus.Fail)) + setEffect{LoginContract.LoginEffect.ShowFailSnackBar(failMessage = event.failMessage)} + setState{ copy(loginStatus = LoginContract.LoginStatus.Fail) } } } } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/register/RegisterViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/register/RegisterViewModel.kt index 0009804..8ad82c6 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/register/RegisterViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/register/RegisterViewModel.kt @@ -26,22 +26,22 @@ class RegisterViewModel @Inject constructor( override suspend fun handleEvent(event: RegisterContract.RegisterEvent) { when (event) { is RegisterContract.RegisterEvent.UsernameChanged -> { - setState(currentUiState.copy(username = event.username)) + setState { copy(username = event.username) } Timber.tag("[회원가입]").d("이름 변경 :${event.username}") } is RegisterContract.RegisterEvent.PasswordChanged -> { - setState(currentUiState.copy(password = event.password)) + setState { copy(password = event.password) } Timber.tag("[회원가입]").d("비밀번호 변경 : ${event.password}") } is RegisterContract.RegisterEvent.HobbyChanged -> { - setState(currentUiState.copy(hobby = event.hobby)) + setState { copy(hobby = event.hobby) } Timber.tag("[회원가입]").d("취미 변경 : ${event.hobby}") } is RegisterContract.RegisterEvent.PasswordVisibilityChanged -> { - setState(currentUiState.copy(showPassword = !currentUiState.showPassword)) + setState { copy(showPassword = !currentUiState.showPassword) } Timber.tag("[회원가입]").d("showPassword 변경 : ${currentUiState.showPassword}") } @@ -54,10 +54,10 @@ class RegisterViewModel @Inject constructor( userHobby = currentUiState.hobby ) ) - setState(currentUiState.copy(registerStatus = RegisterContract.RegisterStatus.Success)) + setState { copy(registerStatus = RegisterContract.RegisterStatus.Success) } } else { - setEffect(RegisterContract.RegisterEffect.ShowToast(message = event.message)) - setState(currentUiState.copy(registerStatus = RegisterContract.RegisterStatus.Fail)) + setEffect { RegisterContract.RegisterEffect.ShowToast(message = event.message) } + setState { copy(registerStatus = RegisterContract.RegisterStatus.Fail) } } } } diff --git a/app/src/main/java/org/sopt/and/util/base/BaseViewModel.kt b/app/src/main/java/org/sopt/and/util/base/BaseViewModel.kt index 1528ff7..c401578 100644 --- a/app/src/main/java/org/sopt/and/util/base/BaseViewModel.kt +++ b/app/src/main/java/org/sopt/and/util/base/BaseViewModel.kt @@ -29,16 +29,18 @@ abstract class BaseViewModel State) { + val newState = currentUiState.reduce() + _uiState.value = newState } fun setEvent(event: Event) { viewModelScope.launch { _uiEvent.emit(event) } } - fun setEffect(effect: Effect) { - viewModelScope.launch { _uiEffect.send(effect) } + fun setEffect(builder: () -> Effect) { + val effectValue = builder() + viewModelScope.launch { _uiEffect.send(effectValue) } } private fun subscribeToEvents() { From 5914346379c6b5e92610e3177a5fed599f6ea3ef Mon Sep 17 00:00:00 2001 From: JIEUNI Date: Fri, 13 Dec 2024 15:44:31 +0900 Subject: [PATCH 3/4] =?UTF-8?q?[mod]=20#13=20HomeScreen=EC=97=90=20loadSta?= =?UTF-8?q?te=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../and/presentation/ui/home/HomeContract.kt | 26 +++++++++----- .../and/presentation/ui/home/HomeRoute.kt | 36 ++++++++++--------- .../and/presentation/ui/home/HomeScreen.kt | 6 ++-- .../and/presentation/ui/home/HomeViewModel.kt | 23 ++++++------ 4 files changed, 48 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeContract.kt b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeContract.kt index 4549094..f47645b 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeContract.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeContract.kt @@ -1,6 +1,6 @@ package org.sopt.and.presentation.ui.home -import okhttp3.internal.immutableListOf +import androidx.compose.foundation.pager.PagerState import org.sopt.and.util.base.UiEffect import org.sopt.and.util.base.UiEvent import org.sopt.and.util.base.UiState @@ -10,18 +10,26 @@ class HomeContract { } - enum class HomeStatus { - Loading, Success, Fail - } - data class HomeState( - val bannerImgList: List = immutableListOf(), + val homeInitialState: HomeUiState = HomeUiState.Idle, val currentBannerPage: Int = 0, - val editorRecommendedList: List = immutableListOf(), - val todayTopRankingList: List = immutableListOf(), - val homeStatus: HomeStatus = HomeStatus.Loading + val pagerState: PagerState = PagerState(pageCount = { currentBannerPage }) ) : UiState + sealed class HomeUiState { + data class Success( + val bannerImgList: List, + val editorRecommendedList: List, + val todayTopRankingList: List + ) : HomeUiState() + + data object Loading : HomeUiState() + + data class Error(val message: String? = null) : HomeUiState() + + data object Idle : HomeUiState() + } + sealed class HomeSideEffect : UiEffect { } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeRoute.kt b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeRoute.kt index ba916b5..4f40d9c 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeRoute.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeRoute.kt @@ -6,7 +6,6 @@ import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -17,25 +16,28 @@ fun HomeRoute( homeViewModel: HomeViewModel = hiltViewModel() ) { val homeState by homeViewModel.uiState.collectAsStateWithLifecycle() - val pagerState = rememberPagerState(pageCount = { homeState.bannerImgList.size }) + val homeUiState: HomeContract.HomeUiState = homeState.homeInitialState - LaunchedEffect(Unit) { - homeViewModel.setHomeImgList() + LaunchedEffect(homeUiState) { + if(homeState.homeInitialState == HomeContract.HomeUiState.Idle) { + homeViewModel.setHomeImgList() + } } - LaunchedEffect(pagerState) { - snapshotFlow { pagerState.currentPage }.collect { page -> - homeViewModel.setCurrentBannerPage(page) + when (homeUiState) { + is HomeContract.HomeUiState.Success -> { + val pagerState = rememberPagerState(pageCount = { homeUiState.bannerImgList.size }) + + HomeScreen( + modifier = Modifier + .padding(paddingValues), + bannerImgList = homeUiState.bannerImgList, + pagerState = pagerState, + editorRecommendedImgList = homeUiState.editorRecommendedList, + todayTopRankingImgList = homeUiState.todayTopRankingList + ) } - } - HomeScreen( - modifier = Modifier - .padding(paddingValues), - bannerImgList = homeState.bannerImgList, - numPages = homeState.bannerImgList.size.toString(), - pagerState = pagerState, - editorRecommendedImgList = homeState.editorRecommendedList, - todayTopRankingImgList = homeState.todayTopRankingList - ) + else -> Unit + } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeScreen.kt index 04c84cc..33727f9 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeScreen.kt @@ -43,7 +43,6 @@ import org.sopt.and.ui.theme.White @Composable fun HomeScreen( bannerImgList: List, - numPages: String, editorRecommendedImgList: List, todayTopRankingImgList: List, modifier: Modifier = Modifier, @@ -65,7 +64,7 @@ fun HomeScreen( HomeBannerItem( bannerImg = bannerImgList[page], currentPage = (page + 1).toString(), - numPages = numPages + numPages = pagerState.pageCount.toString() ) } @@ -207,9 +206,8 @@ fun HomePreview() { ANDANDROIDTheme { HomeScreen( bannerImgList = listOf(""), - numPages = "6", editorRecommendedImgList = listOf(""), - todayTopRankingImgList = listOf("") + todayTopRankingImgList = listOf(""), ) } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeViewModel.kt index e1978e5..11865d0 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeViewModel.kt @@ -13,11 +13,11 @@ class HomeViewModel @Inject constructor() : } override fun handleEffect(effect: HomeContract.HomeSideEffect) { - TODO("Not yet implemented") + // TODO } override suspend fun handleEvent(event: HomeContract.HomeEvent) { - TODO("Not yet implemented") + // TODO } private val mockBannerItem = listOf( @@ -53,18 +53,15 @@ class HomeViewModel @Inject constructor() : "https://image.wavve.com/v1/thumbnails/480_720_20_80/meta/image/202410/1728613201561457273.webp" ) - fun setCurrentBannerPage(page: Int) { - setState(currentUiState.copy(currentBannerPage = page)) - } - fun setHomeImgList() { - setState( - currentUiState.copy( - homeStatus = HomeContract.HomeStatus.Success, - bannerImgList = mockBannerItem, - editorRecommendedList = mockEditorRecommendedItem, - todayTopRankingList = mockTodayTopRankingItem + setState { + copy( + homeInitialState = HomeContract.HomeUiState.Success( + bannerImgList = mockBannerItem, + editorRecommendedList = mockEditorRecommendedItem, + todayTopRankingList = mockTodayTopRankingItem + ) ) - ) + } } } \ No newline at end of file From 2bae0d969af43a4bf8c73e6793be086061be48b2 Mon Sep 17 00:00:00 2001 From: JIEUNI Date: Fri, 13 Dec 2024 16:09:26 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[del]=20#13=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20state=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/sopt/and/presentation/ui/home/HomeContract.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeContract.kt b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeContract.kt index f47645b..e2acb5a 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/home/HomeContract.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/home/HomeContract.kt @@ -12,8 +12,7 @@ class HomeContract { data class HomeState( val homeInitialState: HomeUiState = HomeUiState.Idle, - val currentBannerPage: Int = 0, - val pagerState: PagerState = PagerState(pageCount = { currentBannerPage }) + val pagerState: PagerState = PagerState(pageCount = { 0 }) ) : UiState sealed class HomeUiState {