diff --git a/app/src/main/java/org/sopt/and/presentation/home/HomeEffect.kt b/app/src/main/java/org/sopt/and/presentation/home/HomeEffect.kt new file mode 100644 index 0000000..53b2332 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/home/HomeEffect.kt @@ -0,0 +1,6 @@ +package org.sopt.and.presentation.home + +sealed interface HomeEffect { + data class ShowError(val message: String) : HomeEffect + data object NavigateToDetail : HomeEffect +} diff --git a/app/src/main/java/org/sopt/and/presentation/home/HomeIntent.kt b/app/src/main/java/org/sopt/and/presentation/home/HomeIntent.kt new file mode 100644 index 0000000..2637ba3 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/home/HomeIntent.kt @@ -0,0 +1,6 @@ +package org.sopt.and.presentation.home + +sealed interface HomeIntent { + data object LoadInitialData : HomeIntent + data object RefreshContent : HomeIntent +} diff --git a/app/src/main/java/org/sopt/and/presentation/home/HomeScreen.kt b/app/src/main/java/org/sopt/and/presentation/home/HomeScreen.kt index 7162c98..077399d 100644 --- a/app/src/main/java/org/sopt/and/presentation/home/HomeScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/home/HomeScreen.kt @@ -1,17 +1,21 @@ package org.sopt.and.presentation.home import android.app.Activity +import android.widget.Toast import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.viewmodel.compose.viewModel import org.sopt.and.presentation.home.component.BannerView import org.sopt.and.presentation.home.component.EditorPicksList @@ -21,12 +25,26 @@ import org.sopt.and.presentation.home.component.Top20List @Composable fun HomeScreen( - homeViewModel: HomeViewModel = viewModel() + viewModel: HomeViewModel = hiltViewModel() ) { + val state by viewModel.state.collectAsState() val context = LocalContext.current - val uiState by homeViewModel.uiState.collectAsState() + // Effect handling + LaunchedEffect(Unit) { + viewModel.effect.collect { effect -> + when (effect) { + is HomeEffect.ShowError -> { + Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show() + } + HomeEffect.NavigateToDetail -> { + // Handle navigation + } + } + } + } + // Back handler BackHandler { (context as? Activity)?.finish() } @@ -38,28 +56,31 @@ fun HomeScreen( ) { HomeTopBar() - LazyColumn( - modifier = Modifier.fillMaxSize() - ) { - item { - BannerView(uiState.bannerImages) - } + if (state.isLoading) { + CircularProgressIndicator( + modifier = Modifier + .fillMaxSize() + .wrapContentSize(), + color = Color.White + ) + } else { + LazyColumn( + modifier = Modifier.fillMaxSize() + ) { + item { + BannerView(state.bannerImages) + } - item { - SectionTitle("믿고 보는 웨이브 에디터 추천작") - EditorPicksList(uiState.editorPicks) - } + item { + SectionTitle("믿고 보는 웨이브 에디터 추천작") + EditorPicksList(state.editorPicks) + } - item { - SectionTitle("오늘의 TOP 20") - Top20List(uiState.top20Items) + item { + SectionTitle("오늘의 TOP 20") + Top20List(state.top20Items) + } } } } } -@Preview -@Composable -private fun HomeScreenPreview() { - HomeScreen() - -} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/home/HomeState.kt b/app/src/main/java/org/sopt/and/presentation/home/HomeState.kt new file mode 100644 index 0000000..440158c --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/home/HomeState.kt @@ -0,0 +1,8 @@ +package org.sopt.and.presentation.home + +data class HomeState( + val bannerImages: List = emptyList(), + val editorPicks: List = emptyList(), + val top20Items: List = emptyList(), + val isLoading: Boolean = false +) diff --git a/app/src/main/java/org/sopt/and/presentation/home/HomeViewModel.kt b/app/src/main/java/org/sopt/and/presentation/home/HomeViewModel.kt index cbbbd3c..1834ac4 100644 --- a/app/src/main/java/org/sopt/and/presentation/home/HomeViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/home/HomeViewModel.kt @@ -1,44 +1,60 @@ package org.sopt.and.presentation.home import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import org.sopt.and.R +import javax.inject.Inject -data class HomeUiState( - val bannerImages: List = listOf(), - val editorPicks: List = listOf(), - val top20Items: List = listOf() -) +@HiltViewModel +class HomeViewModel @Inject constructor() : ViewModel() { + private val _state = MutableStateFlow(HomeState()) + val state = _state.asStateFlow() -class HomeViewModel : ViewModel() { - private val _uiState = MutableStateFlow(HomeUiState()) - val uiState: StateFlow = _uiState.asStateFlow() + private val _effect = Channel() + val effect = _effect.receiveAsFlow() init { - loadBannerImages() - loadEditorPicks() - loadTop20Items() + processIntent(HomeIntent.LoadInitialData) } - private fun loadBannerImages() { - val images = listOf( - R.drawable.banner_image, - R.drawable.banner_image, - R.drawable.banner_image, - R.drawable.banner_image - ) - _uiState.value = _uiState.value.copy(bannerImages = images) + fun processIntent(intent: HomeIntent) { + when (intent) { + HomeIntent.LoadInitialData -> loadInitialData() + HomeIntent.RefreshContent -> refreshContent() + } } - private fun loadEditorPicks() { - val picks = List(5) { "추천작 $it" } - _uiState.value = _uiState.value.copy(editorPicks = picks) + private fun loadInitialData() { + viewModelScope.launch { + _state.update { it.copy(isLoading = true) } + + val images = listOf( + R.drawable.banner_image, + R.drawable.banner_image, + R.drawable.banner_image, + R.drawable.banner_image + ) + val picks = List(5) { "추천작 $it" } + val items = List(20) { "Top $it" } + + _state.update { it.copy( + bannerImages = images, + editorPicks = picks, + top20Items = items, + isLoading = false + ) } + } } - private fun loadTop20Items() { - val items = List(20) { "Top $it" } - _uiState.value = _uiState.value.copy(top20Items = items) + private fun refreshContent() { + loadInitialData() } -} \ No newline at end of file +}