diff --git a/feature/src/main/java/com/terning/feature/home/changefilter/ChangeFilterRoute.kt b/feature/src/main/java/com/terning/feature/home/changefilter/ChangeFilterRoute.kt index 649df5d59..ec13a8e4e 100644 --- a/feature/src/main/java/com/terning/feature/home/changefilter/ChangeFilterRoute.kt +++ b/feature/src/main/java/com/terning/feature/home/changefilter/ChangeFilterRoute.kt @@ -48,12 +48,12 @@ fun ChangeFilterRoute( val lifecycleOwner = LocalLifecycleOwner.current val context = LocalContext.current - val filteringState by viewModel.homeFilteringState.collectAsStateWithLifecycle() + val homeState by viewModel.homeState.collectAsStateWithLifecycle() - when (filteringState) { + when (homeState.homeFilteringInfoState) { is UiState.Success -> ChangeFilterScreen( - (filteringState as UiState.Success).data, - navController, + filterData = (homeState.homeFilteringInfoState as UiState.Success).data, + navigateToHome = { navController.popBackStack() }, viewModel, ) @@ -75,7 +75,7 @@ fun ChangeFilterRoute( @Composable fun ChangeFilterScreen( filterData: HomeFilteringInfo, - navController: NavController, + navigateToHome: () -> Unit, viewModel: HomeViewModel, ) { var currentGrade by remember { mutableIntStateOf(filterData.grade ?: -1) } @@ -96,7 +96,7 @@ fun ChangeFilterScreen( topBar = { BackButtonTopAppBar( title = stringResource(id = R.string.change_filter_top_bar_title), - onBackButtonClick = { navController.popBackStack() }, + onBackButtonClick = navigateToHome, modifier = Modifier .shadow(elevation = 2.dp) ) diff --git a/feature/src/main/java/com/terning/feature/home/home/HomeRoute.kt b/feature/src/main/java/com/terning/feature/home/home/HomeRoute.kt index 732f51cb5..cc92f3bdf 100644 --- a/feature/src/main/java/com/terning/feature/home/home/HomeRoute.kt +++ b/feature/src/main/java/com/terning/feature/home/home/HomeRoute.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -23,6 +22,7 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -37,8 +37,8 @@ import com.terning.core.designsystem.component.bottomsheet.SortingBottomSheet import com.terning.core.designsystem.component.button.SortingButton import com.terning.core.designsystem.component.dialog.ScrapCancelDialogContent import com.terning.core.designsystem.component.dialog.TerningBasicDialog +import com.terning.core.designsystem.component.image.TerningImage import com.terning.core.designsystem.component.item.InternItemWithShadow -import com.terning.core.designsystem.component.topappbar.LogoTopAppBar import com.terning.core.designsystem.theme.Black import com.terning.core.designsystem.theme.CalBlue1 import com.terning.core.designsystem.theme.CalBlue2 @@ -78,7 +78,6 @@ fun HomeRoute( navController: NavHostController, viewModel: HomeViewModel = hiltViewModel(), ) { - val systemUiController = rememberSystemUiController() SideEffect { systemUiController.setStatusBarColor( @@ -89,21 +88,13 @@ fun HomeRoute( ) } - val currentSortBy: MutableState = remember { - mutableIntStateOf(0) - } - val lifecycleOwner = LocalLifecycleOwner.current val context = LocalContext.current - val homeTodayState by viewModel.homeTodayState.collectAsStateWithLifecycle() - val homeRecommendInternState by viewModel.homeRecommendInternState.collectAsStateWithLifecycle() - val homeFilteringState by viewModel.homeFilteringState.collectAsStateWithLifecycle() - val homeUserState by viewModel.homeUserState.collectAsStateWithLifecycle() val homeDialogState by viewModel.homeDialogState.collectAsStateWithLifecycle() - val homeTodayInternList: MutableState> = remember { - mutableStateOf(emptyList()) + LaunchedEffect(key1 = true) { + viewModel.getFilteringInfo() } LaunchedEffect(viewModel.homeSideEffect, lifecycleOwner) { @@ -117,186 +108,143 @@ fun HomeRoute( } } - LaunchedEffect(homeFilteringState, currentSortBy.value) { - when (homeFilteringState) { - is UiState.Success -> - with((homeFilteringState as UiState.Success).data) { - viewModel.getRecommendInternsData( - currentSortBy.value, - startYear ?: viewModel.currentYear, - startMonth ?: viewModel.currentMonth - ) - } - - else -> {} - } - } - - LaunchedEffect(homeFilteringState) { - viewModel.getHomeTodayInternList() - } - - LaunchedEffect(key1 = true) { - viewModel.getFilteringInfo() - } - - when (homeTodayState) { - is UiState.Success -> { - homeTodayInternList.value = - (homeTodayState as UiState.Success>).data - } - - is UiState.Loading -> {} - else -> { - homeTodayInternList.value = emptyList() - } - } - - val homeRecommendInternList = when (homeRecommendInternState) { - is UiState.Success -> { - (homeRecommendInternState as UiState.Success>).data - } - - else -> emptyList() - } - - val homeFilteringInfo = when (homeFilteringState) { - is UiState.Success -> (homeFilteringState as UiState.Success).data - else -> HomeFilteringInfo(null, null, viewModel.currentYear, viewModel.currentMonth) - } - - val homeUserName = when (homeUserState) { - is UiState.Success -> (homeUserState as UiState.Success).data - else -> "" - } - HomeScreen( - currentSortBy, - homeUserName = homeUserName, - homeFilteringInfo = homeFilteringInfo, - homeTodayInternList = homeTodayInternList.value, - recommendInternList = homeRecommendInternList, homeDialogState = homeDialogState, onChangeFilterClick = { navController.navigateChangeFilter() }, - navController = navController + navigateToIntern = { navController.navigateIntern(announcementId = it) }, + viewModel = viewModel, ) } @OptIn(ExperimentalFoundationApi::class) @Composable fun HomeScreen( - currentSortBy: MutableState, - homeUserName: String, - homeFilteringInfo: HomeFilteringInfo, - homeTodayInternList: List, - recommendInternList: List, homeDialogState: HomeDialogState, onChangeFilterClick: () -> Unit, - viewModel: HomeViewModel = hiltViewModel(), - navController: NavHostController, + navigateToIntern: (Long) -> Unit, + viewModel: HomeViewModel, ) { + val homeState by viewModel.homeState.collectAsStateWithLifecycle() + + val homeUserName = when (homeState.homeUserNameState) { + is UiState.Success -> (homeState.homeUserNameState as UiState.Success).data + else -> "" + } + + val homeFilteringInfo = when (homeState.homeFilteringInfoState) { + is UiState.Success -> (homeState.homeFilteringInfoState as UiState.Success).data + else -> HomeFilteringInfo(null, null, null, null) + } + + val homeRecommendInternList = when (homeState.homeRecommendInternState) { + is UiState.Success -> (homeState.homeRecommendInternState as UiState.Success>).data + else -> listOf() + } + + val currentSortBy: MutableState = remember { mutableIntStateOf(0) } var sheetState by remember { mutableStateOf(false) } - var scrapId by remember { mutableStateOf(-1) } + var scrapId by remember { mutableIntStateOf(-1) } if (sheetState) { SortingBottomSheet( onDismiss = { sheetState = false + viewModel.getRecommendInternsData( + currentSortBy.value, + homeFilteringInfo.startYear, + homeFilteringInfo.startMonth, + ) }, currentSortBy = currentSortBy.value, newSortBy = currentSortBy ) } - Scaffold( - modifier = Modifier, - topBar = { - LogoTopAppBar() - } - ) { paddingValues -> - Column( + Column( + horizontalAlignment = Alignment.Start, + ) { + TerningImage( + painter = R.drawable.ic_terning_logo_typo, + modifier = Modifier + .padding(start = 24.dp, top = 16.dp, bottom = 16.dp) + ) + + LazyColumn( + contentPadding = PaddingValues( + top = 2.dp, + bottom = 20.dp, + ), + verticalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier - .fillMaxSize() - .padding(top = paddingValues.calculateTopPadding()), + .fillMaxWidth(), ) { - LazyColumn( - contentPadding = PaddingValues( - top = 2.dp, - bottom = 20.dp, - ), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - item { - Column( - modifier = Modifier - .padding(bottom = 16.dp) - ) { - ShowMainTitleWithName(homeUserName) - ShowTodayIntern( - homeTodayInternList = homeTodayInternList, - homeDialogState = homeDialogState, - navigateToDetail = { navController.navigateIntern(announcementId = it) } - ) - } + item { + Column( + modifier = Modifier + .padding(bottom = 16.dp) + ) { + ShowMainTitleWithName(homeUserName) + ShowTodayIntern( + homeTodayInternState = homeState.homeTodayInternState, + homeDialogState = homeDialogState, + navigateToIntern = { navigateToIntern(it) } + ) } - stickyHeader { - Column( + } + stickyHeader { + Column( + modifier = Modifier + .background(White) + ) { + ShowRecommendTitle() + ShowInternFilter(homeFilteringInfo = homeFilteringInfo, onChangeFilterClick) + + HorizontalDivider( + thickness = 4.dp, + color = Grey150, + ) + + Row( modifier = Modifier - .background(White) + .fillMaxWidth(), + horizontalArrangement = Arrangement.End, ) { - ShowRecommendTitle() - ShowInternFilter(homeFilteringInfo = homeFilteringInfo, onChangeFilterClick) - - HorizontalDivider( - thickness = 4.dp, - color = Grey150, + SortingButton( + sortBy = currentSortBy.value, + onCLick = { sheetState = true }, modifier = Modifier - .fillMaxWidth(), + .padding(vertical = 4.dp) ) - - Row( - modifier = Modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.End, - ) { - SortingButton( - sortBy = currentSortBy.value, - onCLick = { sheetState = true }, - modifier = Modifier - .padding(vertical = 4.dp) - ) - Spacer( - modifier = Modifier.padding(9.dp) - ) - } - } - } - - if (recommendInternList.isNotEmpty()) { - items(recommendInternList.size) { index -> - RecommendInternItem( - navController = navController, - intern = recommendInternList[index], - onScrapButtonClicked = { - viewModel.updateScrapDialogVisible(true) - viewModel.updateIsToday(false) - scrapId = index - } + Spacer( + modifier = Modifier.padding(9.dp) ) } } } - if (homeFilteringInfo.grade == null) { - HomeFilteringEmptyIntern( - modifier = Modifier - .padding(horizontal = 24.dp) - .fillMaxSize() - ) - } else if (recommendInternList.isEmpty()) { - HomeRecommendEmptyIntern() + if (homeRecommendInternList.isNotEmpty()) { + items(homeRecommendInternList.size) { index -> + RecommendInternItem( + navigateToIntern = navigateToIntern, + intern = homeRecommendInternList[index], + onScrapButtonClicked = { + viewModel.updateScrapDialogVisible(true) + viewModel.updateIsToday(false) + scrapId = index + } + ) + } } } + if (homeState.homeFilteringInfoState is UiState.Success && homeFilteringInfo.grade == null) { + HomeFilteringEmptyIntern( + modifier = Modifier + .padding(horizontal = 24.dp) + .fillMaxSize() + ) + } else if (homeRecommendInternList.isEmpty()) { + HomeRecommendEmptyIntern() + } } if (homeDialogState.isScrapDialogVisible && !homeDialogState.isToday) { @@ -306,18 +254,18 @@ fun HomeScreen( viewModel.updatePaletteOpen(false) }, content = { - if (recommendInternList[scrapId].scrapId != null) { + if (homeRecommendInternList[scrapId].scrapId != null) { ScrapCancelDialogContent( onClickScrapCancel = { viewModel.updateScrapDialogVisible(false) viewModel.deleteScrap( - recommendInternList[scrapId].scrapId ?: -1, + homeRecommendInternList[scrapId].scrapId ?: -1, ) if (homeDialogState.isScrappedState) { viewModel.getRecommendInternsData( currentSortBy.value, - homeFilteringInfo.startYear ?: viewModel.currentYear, - homeFilteringInfo.startMonth ?: viewModel.currentMonth, + homeFilteringInfo.startYear, + homeFilteringInfo.startMonth, ) viewModel.updateScrapped(false) } @@ -340,7 +288,7 @@ fun HomeScreen( val selectedColorIndex = colorList.indexOf(homeDialogState.selectedColor).takeIf { it >= 0 } ?: 0 - with(recommendInternList[scrapId]) { + with(homeRecommendInternList[scrapId]) { HomeRecommendInternDialog( internInfoList = listOf( stringResource(id = R.string.intern_info_d_day) to deadline, @@ -359,15 +307,15 @@ fun HomeScreen( viewModel.updateScrapDialogVisible(false) } viewModel.postScrap( - recommendInternList[scrapId].internshipAnnouncementId, + homeRecommendInternList[scrapId].internshipAnnouncementId, selectedColorIndex, ) viewModel.updateScrapDialogVisible(false) if (homeDialogState.isScrappedState) { viewModel.getRecommendInternsData( currentSortBy.value, - homeFilteringInfo.startYear ?: viewModel.currentYear, - homeFilteringInfo.startMonth ?: viewModel.currentMonth, + homeFilteringInfo.startYear, + homeFilteringInfo.startMonth, ) viewModel.updateScrapped(false) } @@ -384,17 +332,15 @@ fun HomeScreen( @Composable private fun RecommendInternItem( - navController: NavHostController, intern: HomeRecommendIntern, + navigateToIntern: (Long) -> Unit, onScrapButtonClicked: (Long) -> Unit, ) { InternItemWithShadow( modifier = Modifier .padding(horizontal = 24.dp) .noRippleClickable { - navController.navigateIntern( - announcementId = intern.internshipAnnouncementId - ) + navigateToIntern(intern.internshipAnnouncementId) }, imageUrl = intern.companyImage, title = intern.title, @@ -425,18 +371,25 @@ private fun ShowMainTitleWithName(userName: String) { @Composable private fun ShowTodayIntern( - homeTodayInternList: List, + homeTodayInternState: UiState>, homeDialogState: HomeDialogState, - navigateToDetail: (Long) -> Unit, + navigateToIntern: (Long) -> Unit, ) { - if (homeTodayInternList.isEmpty()) { - HomeTodayEmptyWithImg() - } else { - HomeTodayIntern( - internList = homeTodayInternList, - homeDialogState = homeDialogState, - navigateToDetail = { navigateToDetail(it) }, - ) + when (homeTodayInternState) { + is UiState.Success -> { + if (homeTodayInternState.data.isEmpty()) { + HomeTodayEmptyWithImg() + } else { + HomeTodayIntern( + internList = homeTodayInternState.data, + homeDialogState = homeDialogState, + navigateToIntern = { navigateToIntern(it) }, + ) + } + } + + is UiState.Loading -> HomeTodayEmptyWithImg() + else -> {} } } @@ -471,7 +424,7 @@ private fun ShowInternFilter( grade = stringResource(id = R.string.home_recommend_no_filtering_hyphen), period = stringResource(id = R.string.home_recommend_no_filtering_hyphen), startYear = stringResource(id = R.string.home_recommend_no_filtering_hyphen), - onChangeFilterClick = { onChangeFilterClick() }, + onChangeFilterClick = onChangeFilterClick, ) } else { with(homeFilteringInfo) { @@ -487,7 +440,7 @@ private fun ShowInternFilter( ), startYear = startYear.toString() + stringResource(id = R.string.home_recommend_filtering_startYear) + " " + startMonth.toString() + stringResource(id = R.string.home_recommend_filtering_startMonth), - onChangeFilterClick = { onChangeFilterClick() }, + onChangeFilterClick = onChangeFilterClick, ) } } diff --git a/feature/src/main/java/com/terning/feature/home/home/HomeState.kt b/feature/src/main/java/com/terning/feature/home/home/HomeState.kt new file mode 100644 index 000000000..fdee536c3 --- /dev/null +++ b/feature/src/main/java/com/terning/feature/home/home/HomeState.kt @@ -0,0 +1,15 @@ +package com.terning.feature.home.home + +import com.terning.core.state.UiState +import com.terning.domain.entity.home.HomeFilteringInfo +import com.terning.domain.entity.home.HomeRecommendIntern +import com.terning.domain.entity.home.HomeTodayIntern +import com.terning.feature.home.home.model.SortBy + +data class HomeState( + val sortBy: SortBy = SortBy.EARLIEST, + val homeUserNameState: UiState = UiState.Loading, + val homeFilteringInfoState: UiState = UiState.Loading, + val homeTodayInternState: UiState> = UiState.Loading, + val homeRecommendInternState: UiState> = UiState.Loading, +) diff --git a/feature/src/main/java/com/terning/feature/home/home/HomeViewModel.kt b/feature/src/main/java/com/terning/feature/home/home/HomeViewModel.kt index 5cca99d71..96c958fe1 100644 --- a/feature/src/main/java/com/terning/feature/home/home/HomeViewModel.kt +++ b/feature/src/main/java/com/terning/feature/home/home/HomeViewModel.kt @@ -6,9 +6,6 @@ import androidx.lifecycle.viewModelScope import com.terning.core.designsystem.theme.CalRed import com.terning.core.state.UiState import com.terning.domain.entity.CalendarScrapRequest -import com.terning.domain.entity.home.HomeFilteringInfo -import com.terning.domain.entity.home.HomeRecommendIntern -import com.terning.domain.entity.home.HomeTodayIntern import com.terning.domain.entity.request.ChangeFilteringRequestModel import com.terning.domain.repository.HomeRepository import com.terning.domain.repository.MyPageRepository @@ -35,27 +32,12 @@ class HomeViewModel @Inject constructor( val currentYear = Calendar.getInstance().get(Calendar.YEAR) val currentMonth = Calendar.getInstance().get(Calendar.MONTH) + private val _homeState: MutableStateFlow = MutableStateFlow(HomeState()) + val homeState get() = _homeState.asStateFlow() + private val _homeSideEffect = MutableSharedFlow() val homeSideEffect get() = _homeSideEffect.asSharedFlow() - private val _homeTodayState = - MutableStateFlow>>(UiState.Loading) - val homeTodayState get() = _homeTodayState.asStateFlow() - - private val _homeRecommendInternState = - MutableStateFlow>>(UiState.Loading) - val homeRecommendInternState get() = _homeRecommendInternState.asStateFlow() - - private val _homeFilteringState = - MutableStateFlow>(UiState.Loading) - val homeFilteringState get() = _homeFilteringState.asStateFlow() - - private val _homeSortByState = MutableStateFlow(0) - val homeSortByState get() = _homeSortByState.asStateFlow() - - private val _homeUserState = MutableStateFlow>(UiState.Loading) - val homeUserState get() = _homeUserState.asStateFlow() - private val _homeDialogState: MutableStateFlow = MutableStateFlow(HomeDialogState()) val homeDialogState get() = _homeDialogState.asStateFlow() @@ -65,38 +47,57 @@ class HomeViewModel @Inject constructor( getFilteringInfo() } - fun getRecommendInternsData(sortBy: Int, startYear: Int, startMonth: Int) { - _homeRecommendInternState.value = UiState.Loading + fun getRecommendInternsData(sortBy: Int, startYear: Int?, startMonth: Int?) { viewModelScope.launch { - homeRepository.getRecommendIntern(SortBy.entries[sortBy].sortBy, startYear, startMonth) + homeRepository.getRecommendIntern( + SortBy.entries[sortBy].sortBy, + startYear ?: currentYear, + startMonth ?: currentMonth, + ) .onSuccess { internList -> - _homeRecommendInternState.value = UiState.Success(internList) + _homeState.value = _homeState.value.copy( + homeRecommendInternState = UiState.Success(internList) + ) }.onFailure { exception: Throwable -> - _homeRecommendInternState.value = UiState.Failure(exception.message ?: "") + _homeState.value = _homeState.value.copy( + homeRecommendInternState = UiState.Failure(exception.toString()) + ) _homeSideEffect.emit(HomeSideEffect.ShowToast(R.string.server_failure)) } } } - fun getHomeTodayInternList() { - _homeTodayState.value = UiState.Loading + private fun getHomeTodayInternList() { viewModelScope.launch { homeRepository.getHomeTodayInternList().onSuccess { internList -> - _homeTodayState.value = UiState.Success(internList) + _homeState.value = _homeState.value.copy( + homeTodayInternState = UiState.Success(internList) + ) }.onFailure { exception: Throwable -> - _homeTodayState.value = UiState.Failure(exception.message ?: "") + _homeState.value = _homeState.value.copy( + homeTodayInternState = UiState.Failure(exception.toString()) + ) _homeSideEffect.emit(HomeSideEffect.ShowToast(R.string.server_failure)) } } } fun getFilteringInfo() { - _homeFilteringState.value = UiState.Loading viewModelScope.launch { homeRepository.getFilteringInfo().onSuccess { filteringInfo -> - _homeFilteringState.value = UiState.Success(filteringInfo) + _homeState.value = _homeState.value.copy( + homeFilteringInfoState = UiState.Success(filteringInfo) + ) + getHomeTodayInternList() + getRecommendInternsData( + sortBy = _homeState.value.sortBy.ordinal, + startYear = filteringInfo.startYear, + startMonth = filteringInfo.startMonth, + ) }.onFailure { exception: Throwable -> - _homeFilteringState.value = UiState.Failure(exception.message ?: "") + _homeState.value = _homeState.value.copy( + homeFilteringInfoState = UiState.Failure(exception.toString()) + ) _homeSideEffect.emit(HomeSideEffect.ShowToast(R.string.server_failure)) } } @@ -115,9 +116,12 @@ class HomeViewModel @Inject constructor( private fun getProfile() { viewModelScope.launch { myPageRepository.getProfile().onSuccess { response -> - _homeUserState.value = UiState.Success(response.name) + _homeState.value = _homeState.value.copy( + homeUserNameState = UiState.Success(response.name) + ) }.onFailure { exception: Throwable -> - _homeUserState.value = UiState.Failure(exception.message ?: "") + _homeState.value = + _homeState.value.copy(homeUserNameState = UiState.Failure(exception.toString())) _homeSideEffect.emit(HomeSideEffect.ShowToast(R.string.server_failure)) } } diff --git a/feature/src/main/java/com/terning/feature/home/home/component/HomeTodayIntern.kt b/feature/src/main/java/com/terning/feature/home/home/component/HomeTodayIntern.kt index 0e0e2cc6f..d6c93b782 100644 --- a/feature/src/main/java/com/terning/feature/home/home/component/HomeTodayIntern.kt +++ b/feature/src/main/java/com/terning/feature/home/home/component/HomeTodayIntern.kt @@ -25,7 +25,7 @@ import com.terning.feature.home.home.model.HomeDialogState fun HomeTodayIntern( internList: List, homeDialogState: HomeDialogState, - navigateToDetail: (Long) -> Unit, + navigateToIntern: (Long) -> Unit, homeViewModel: HomeViewModel = hiltViewModel() ) { var selectedIndex by remember { @@ -73,7 +73,7 @@ fun HomeTodayIntern( stringResource(id = R.string.intern_info_start_date) to startYearMonth, ), navigateTo = { - navigateToDetail(internshipAnnouncementId) + navigateToIntern(internshipAnnouncementId) homeViewModel.updateScrapDialogVisible(false) }, homeTodayIntern = internList[selectedIndex], diff --git a/feature/src/main/res/drawable/ic_terning_logo_typo.xml b/feature/src/main/res/drawable/ic_terning_logo_typo.xml new file mode 100644 index 000000000..4e7d72284 --- /dev/null +++ b/feature/src/main/res/drawable/ic_terning_logo_typo.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + +