diff --git a/data/src/main/java/com/everymeal/data/datasource/restaurant/RestaurantDataSource.kt b/data/src/main/java/com/everymeal/data/datasource/restaurant/RestaurantDataSource.kt index 246455e0..cb1f4794 100644 --- a/data/src/main/java/com/everymeal/data/datasource/restaurant/RestaurantDataSource.kt +++ b/data/src/main/java/com/everymeal/data/datasource/restaurant/RestaurantDataSource.kt @@ -1,6 +1,7 @@ package com.everymeal.data.datasource.restaurant import androidx.paging.PagingData +import com.everymeal.data.model.restaruant.GetUnivRestaurantResponse import com.everymeal.data.model.restaruant.RestaurantResponse import kotlinx.coroutines.flow.Flow @@ -13,4 +14,11 @@ interface RestaurantDataSource { ): Flow> suspend fun getRestaurantDetail(index: Int): Result + + suspend fun getHomeRestaurant( + campusIdx: Int, + order: String, + group: String? = null, + grade: String? = null, + ): Result } \ No newline at end of file diff --git a/data/src/main/java/com/everymeal/data/datasource/restaurant/RestaurantDataSourceImpl.kt b/data/src/main/java/com/everymeal/data/datasource/restaurant/RestaurantDataSourceImpl.kt index 1656037e..868332b4 100644 --- a/data/src/main/java/com/everymeal/data/datasource/restaurant/RestaurantDataSourceImpl.kt +++ b/data/src/main/java/com/everymeal/data/datasource/restaurant/RestaurantDataSourceImpl.kt @@ -3,6 +3,7 @@ package com.everymeal.data.datasource.restaurant import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData +import com.everymeal.data.model.restaruant.GetUnivRestaurantResponse import com.everymeal.data.model.restaruant.RestaurantResponse import com.everymeal.data.model.unwrapData import com.everymeal.data.service.restaurant.RestaurantApi @@ -35,4 +36,20 @@ class RestaurantDataSourceImpl @Inject constructor( override suspend fun getRestaurantDetail(index: Int): Result { return runCatching { restaurantApi.getRestaurantDetail(index) }.unwrapData() } + + override suspend fun getHomeRestaurant( + campusIdx: Int, + order: String, + group: String?, + grade: String? + ): Result { + return runCatching { restaurantApi.getUnivRestaurant( + campusIdx = campusIdx, + order = order, + group = group, + grade = grade, + offset = 0, + limit = 3) + }.unwrapData() + } } \ No newline at end of file diff --git a/data/src/main/java/com/everymeal/data/model/restaruant/GetUnivRestaurantResponse.kt b/data/src/main/java/com/everymeal/data/model/restaruant/GetUnivRestaurantResponse.kt index f4801fe6..df11aa84 100644 --- a/data/src/main/java/com/everymeal/data/model/restaruant/GetUnivRestaurantResponse.kt +++ b/data/src/main/java/com/everymeal/data/model/restaruant/GetUnivRestaurantResponse.kt @@ -1,5 +1,6 @@ package com.everymeal.data.model.restaruant +import com.everymeal.domain.model.restaurant.GetUnivRestaurantEntity import com.everymeal.domain.model.restaurant.RestaurantDataEntity import kotlinx.serialization.Serializable @@ -64,4 +65,10 @@ fun RestaurantResponse.toEntity(): RestaurantDataEntity { images = this.images, isLiked = this.isLiked ) +} + +fun GetUnivRestaurantResponse.toEntity(): GetUnivRestaurantEntity { + return GetUnivRestaurantEntity( + data = this.content.map { it.toEntity() } + ) } \ No newline at end of file diff --git a/data/src/main/java/com/everymeal/data/repository/restaurant/RestaurantRepositoryImpl.kt b/data/src/main/java/com/everymeal/data/repository/restaurant/RestaurantRepositoryImpl.kt index 606d149e..85c5e436 100644 --- a/data/src/main/java/com/everymeal/data/repository/restaurant/RestaurantRepositoryImpl.kt +++ b/data/src/main/java/com/everymeal/data/repository/restaurant/RestaurantRepositoryImpl.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import androidx.paging.map import com.everymeal.data.model.restaruant.toEntity +import com.everymeal.domain.model.restaurant.GetUnivRestaurantEntity import javax.inject.Inject class RestaurantRepositoryImpl @Inject constructor( @@ -28,4 +29,13 @@ class RestaurantRepositoryImpl @Inject constructor( override suspend fun getRestaurantDetail(index: Int): Result { return restaurantDataSource.getRestaurantDetail(index).map { it.toEntity() } } + + override suspend fun getHomeRestaurant( + campusIdx: Int, + order: String, + group: String?, + grade: String? + ): Result { + return restaurantDataSource.getHomeRestaurant(campusIdx, order, group, grade).map { it.toEntity() } + } } \ No newline at end of file diff --git a/domain/src/main/java/com/everymeal/domain/repository/restaurant/RestaurantRepository.kt b/domain/src/main/java/com/everymeal/domain/repository/restaurant/RestaurantRepository.kt index 627951bd..a91d4504 100644 --- a/domain/src/main/java/com/everymeal/domain/repository/restaurant/RestaurantRepository.kt +++ b/domain/src/main/java/com/everymeal/domain/repository/restaurant/RestaurantRepository.kt @@ -1,6 +1,7 @@ package com.everymeal.domain.repository.restaurant import androidx.paging.PagingData +import com.everymeal.domain.model.restaurant.GetUnivRestaurantEntity import com.everymeal.domain.model.restaurant.RestaurantDataEntity import kotlinx.coroutines.flow.Flow @@ -16,4 +17,11 @@ interface RestaurantRepository { suspend fun getRestaurantDetail( index: Int ) : Result + + suspend fun getHomeRestaurant( + campusIdx: Int, + order: String, + group: String? = null, + grade: String? = null, + ) : Result } \ No newline at end of file diff --git a/domain/src/main/java/com/everymeal/domain/usecase/restaurant/GetHomeRestaurantUseCase.kt b/domain/src/main/java/com/everymeal/domain/usecase/restaurant/GetHomeRestaurantUseCase.kt new file mode 100644 index 00000000..10aa8047 --- /dev/null +++ b/domain/src/main/java/com/everymeal/domain/usecase/restaurant/GetHomeRestaurantUseCase.kt @@ -0,0 +1,18 @@ +package com.everymeal.domain.usecase.restaurant + +import com.everymeal.domain.model.restaurant.GetUnivRestaurantEntity +import com.everymeal.domain.repository.restaurant.RestaurantRepository +import javax.inject.Inject + +class GetHomeRestaurantUseCase @Inject constructor( + private val restaurantRepository: RestaurantRepository +) { + suspend operator fun invoke( + campusIdx: Int, + order: String, + group: String?, + grade: String? + ) : Result { + return restaurantRepository.getHomeRestaurant(campusIdx, order, group, grade) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/everymeal/presentation/components/EveryMealBottomSheetDialog.kt b/presentation/src/main/java/com/everymeal/presentation/components/EveryMealBottomSheetDialog.kt index 732ac1db..12697d63 100644 --- a/presentation/src/main/java/com/everymeal/presentation/components/EveryMealBottomSheetDialog.kt +++ b/presentation/src/main/java/com/everymeal/presentation/components/EveryMealBottomSheetDialog.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.everymeal.presentation.R import com.everymeal.presentation.ui.detail.ReportCategoryType +import com.everymeal.presentation.ui.detail.RestaurantCategoryType import com.everymeal.presentation.ui.home.HomeCategoryList import com.everymeal.presentation.ui.theme.EveryMealTypo import com.everymeal.presentation.ui.theme.EveryMealTypography @@ -165,6 +166,7 @@ fun SortCategoryItem( @OptIn(ExperimentalMaterial3Api::class) @Composable fun EveryMealCategoryRatingBottomSheetDialog( + title : String = "", currentRating: Int, restaurantCategoryType: String, onClick: () -> Unit, @@ -181,26 +183,30 @@ fun EveryMealCategoryRatingBottomSheetDialog( .fillMaxWidth() .padding(horizontal = 20.dp) ) { - Text( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 14.dp), - text = stringResource(R.string.meal_category), - fontSize = 17.sp, - color = Gray900, - fontWeight = FontWeight.SemiBold, - ) - Spacer(modifier = Modifier.padding(4.dp)) - HomeCategoryList( - isBottomSheet = true, - restaurantCategoryType - ) { - onCategoryClick(it) + if(title.RestaurantCategoryType() != RestaurantCategoryType.CAFE + && title.RestaurantCategoryType() != RestaurantCategoryType.DRINK) { + Spacer(modifier = Modifier.padding(4.dp)) + Text( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 14.dp), + text = stringResource(R.string.meal_category), + fontSize = 17.sp, + color = Gray900, + fontWeight = FontWeight.SemiBold, + ) + Spacer(modifier = Modifier.padding(4.dp)) + HomeCategoryList( + isBottomSheet = true, + restaurantCategoryType + ) { + onCategoryClick(it) + } + Spacer(modifier = Modifier.padding(14.dp)) + Divider( + color = Gray200, + ) } - Spacer(modifier = Modifier.padding(14.dp)) - Divider( - color = Gray200, - ) Spacer(modifier = Modifier.padding(4.dp)) Text( modifier = Modifier diff --git a/presentation/src/main/java/com/everymeal/presentation/components/EveryMealRestaurantItem.kt b/presentation/src/main/java/com/everymeal/presentation/components/EveryMealRestaurantItem.kt index ff0415f8..e97addd4 100644 --- a/presentation/src/main/java/com/everymeal/presentation/components/EveryMealRestaurantItem.kt +++ b/presentation/src/main/java/com/everymeal/presentation/components/EveryMealRestaurantItem.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.font.FontWeight @@ -74,6 +75,7 @@ fun RestaurantTitle( ) { Row( modifier = modifier, + verticalAlignment = Alignment.CenterVertically, ) { Text( modifier = Modifier.padding(top = 6.dp), @@ -104,7 +106,9 @@ fun RestaurantLoveCount( onLoveClick: () -> Unit, ) { Column( - modifier = Modifier.clickable( + modifier = Modifier + .padding(top = 6.dp) + .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } ) { onLoveClick() }, @@ -157,23 +161,33 @@ fun RestaurantRating(restaurant: RestaurantDataEntity) { fun RestaurantImage(restaurant: RestaurantDataEntity) { Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.spacedBy(6.dp) ) { +// restaurant.images?.take(3)?.forEachIndexed { index, image -> +// AsyncImage( +// model = image, +// contentDescription = null, +// modifier = Modifier +// .weight(1f) +// .aspectRatio(1f) +// .padding(end = if (index < 2) 6.dp else 0.dp) +// .clip(RoundedCornerShape(8.dp)), +// contentScale = ContentScale.Crop +// ) +// } restaurant.images?.let { when { - restaurant.images?.size == 3 -> { - restaurant.images?.forEachIndexed { index, image -> + restaurant.images?.size!! >= 3 -> { + restaurant.images?.take(3)?.forEachIndexed { index, image -> AsyncImage( + model = image, + contentDescription = null, modifier = Modifier .weight(1f) .aspectRatio(1f) .clip(RoundedCornerShape(8.dp)), - model = image, - contentDescription = null + contentScale = ContentScale.Crop ) - if(index != 2) { - Spacer(modifier = Modifier.padding(end = 6.dp)) - } } } @@ -185,11 +199,12 @@ fun RestaurantImage(restaurant: RestaurantDataEntity) { .aspectRatio(1f) .clip(RoundedCornerShape(8.dp)), model = image, - contentDescription = null + contentDescription = null, + contentScale = ContentScale.Crop ) - if(index != 1) { - Spacer(modifier = Modifier.padding(end = 6.dp)) - } +// if(index != 1) { +// Spacer(modifier = Modifier.padding(end = 6.dp)) +// } } Spacer(modifier = Modifier.weight(1f)) } @@ -205,56 +220,56 @@ fun RestaurantImage(restaurant: RestaurantDataEntity) { ) Spacer(modifier = Modifier .weight(2f) - .padding(end = 6.dp) +// .padding(end = 6.dp) ) } - restaurant.images?.size!! > 3 -> { - AsyncImage( - modifier = Modifier - .weight(1f) - .aspectRatio(1f) - .clip(RoundedCornerShape(8.dp)), - model = restaurant.images!![0], - contentDescription = null - ) - Spacer(modifier = Modifier.padding(end = 6.dp)) - AsyncImage( - modifier = Modifier - .weight(1f) - .aspectRatio(1f) - .clip(RoundedCornerShape(8.dp)), - model = restaurant.images!![0], - contentDescription = null - ) - Spacer(modifier = Modifier.padding(end = 6.dp)) - Box( - modifier = Modifier - .weight(1f) - .aspectRatio(1f) - ) { - AsyncImage( - modifier = Modifier - .aspectRatio(1f) - .fillMaxSize(), - model = restaurant.images!![0], - contentDescription = null - ) - Box( - modifier = Modifier - .matchParentSize() - .clip(RoundedCornerShape(8.dp)) - .background(Color.Black.copy(alpha = 0.2f)), - contentAlignment = Alignment.Center - ) { - Text( - text = "+${restaurant.reviewCount - 2}", - color = Color.White, - fontSize = 14.sp - ) - } - } - } +// restaurant.images?.size!! > 3 -> { +// AsyncImage( +// modifier = Modifier +// .weight(1f) +// .aspectRatio(1f) +// .clip(RoundedCornerShape(8.dp)), +// model = restaurant.images!![0], +// contentDescription = null +// ) +// Spacer(modifier = Modifier.padding(end = 6.dp)) +// AsyncImage( +// modifier = Modifier +// .weight(1f) +// .aspectRatio(1f) +// .clip(RoundedCornerShape(8.dp)), +// model = restaurant.images!![0], +// contentDescription = null +// ) +// Spacer(modifier = Modifier.padding(end = 6.dp)) +// Box( +// modifier = Modifier +// .weight(1f) +// .aspectRatio(1f) +// ) { +// AsyncImage( +// modifier = Modifier +// .aspectRatio(1f) +// .fillMaxSize(), +// model = restaurant.images!![0], +// contentDescription = null +// ) +// Box( +// modifier = Modifier +// .matchParentSize() +// .clip(RoundedCornerShape(8.dp)) +// .background(Color.Black.copy(alpha = 0.2f)), +// contentAlignment = Alignment.Center +// ) { +// Text( +// text = "+${restaurant.reviewCount - 2}", +// color = Color.White, +// fontSize = 14.sp +// ) +// } +// } +// } } } } diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/bottom/BottomNavigation.kt b/presentation/src/main/java/com/everymeal/presentation/ui/bottom/BottomNavigation.kt index 3a54c5ba..340c31cc 100644 --- a/presentation/src/main/java/com/everymeal/presentation/ui/bottom/BottomNavigation.kt +++ b/presentation/src/main/java/com/everymeal/presentation/ui/bottom/BottomNavigation.kt @@ -39,4 +39,5 @@ enum class EveryMealRoute(val route: String) { DETAIL_LIST("detail-list"), DETAIL_RESTAURANT("detail-restaurant"), SCHOOL_AUTH("school-auth"), + WITH_DRAW("with-draw") } diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/detail/DetailListScreen.kt b/presentation/src/main/java/com/everymeal/presentation/ui/detail/DetailListScreen.kt index e670cad4..9387613a 100644 --- a/presentation/src/main/java/com/everymeal/presentation/ui/detail/DetailListScreen.kt +++ b/presentation/src/main/java/com/everymeal/presentation/ui/detail/DetailListScreen.kt @@ -88,6 +88,7 @@ fun DetailListScreen( if(detailListViewState.mealRatingBottomSheetState) { EveryMealCategoryRatingBottomSheetDialog( + title, detailListViewState.rating, detailListViewState.restaurantCategoryType.title(), onClick = { diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeContract.kt b/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeContract.kt index dadf9ac0..9e9e75c2 100644 --- a/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeContract.kt +++ b/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeContract.kt @@ -1,5 +1,7 @@ package com.everymeal.presentation.ui.home +import com.everymeal.domain.model.restaurant.GetUnivRestaurantEntity +import com.everymeal.domain.model.restaurant.RestaurantDataEntity import com.everymeal.presentation.base.LoadState import com.everymeal.presentation.base.ViewEvent import com.everymeal.presentation.base.ViewSideEffect @@ -7,18 +9,22 @@ import com.everymeal.presentation.base.ViewState class HomeContract { data class HomeState( - val uiState: LoadState = LoadState.SUCCESS, + val uiState: LoadState = LoadState.LOADING, val detailListScreenType: DetailListScreenType = DetailListScreenType.RECOMMEND, - val bottomSheetState: Boolean = false + val bottomSheetState: Boolean = false, + val restaurantData: List = emptyList() ) : ViewState sealed class HomeEvent : ViewEvent { + object InitHomeScreen : HomeEvent() data class OnClickDetailList(val detailListScreenType: DetailListScreenType) : HomeEvent() data class BottomSheetStateChange(val bottomSheetState: Boolean) : HomeEvent() + data class OnClickDetailRestaurant(val restaurantId: Int) : HomeEvent() } sealed class HomeEffect : ViewSideEffect { data class NavigateToDetailListScreen(val detailListScreenType: DetailListScreenType) : HomeEffect() + data class NavigateToDetailRestaurant(val restaurantId: Int) : HomeEffect() } } diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeScreen.kt b/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeScreen.kt index 6662bd01..d42b6daf 100644 --- a/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeScreen.kt +++ b/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeScreen.kt @@ -41,7 +41,9 @@ import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import com.everymeal.domain.model.restaurant.RestaurantDataEntity import com.everymeal.presentation.R +import com.everymeal.presentation.base.LoadState import com.everymeal.presentation.components.EveryMealLineButton +import com.everymeal.presentation.components.EveryMealLoadingDialog import com.everymeal.presentation.components.EveryMealMainBottomSheetDialog import com.everymeal.presentation.components.EveryMealRestaurantItem import com.everymeal.presentation.components.EveryMealReviewItem @@ -57,60 +59,24 @@ import com.everymeal.presentation.ui.theme.Paddings fun HomeScreen( homeViewModel : HomeViewModel = hiltViewModel(), onDetailScreenClickType : (String) -> Unit, - onDetailRestaurantClick : () -> Unit, + onDetailRestaurantClick : (String) -> Unit, ) { - val items = listOf( - RestaurantDataEntity( - idx = 386, - name = "히포 브런치하우스", - address = "서울 마포구 연남동 487-34", - phoneNumber = "010-3796-3176", - categoryDetail = "카페", - distance = 1721, - grade = 0.0F, - reviewCount = 5, - recommendedCount = 5, - images = listOf( - "store/ea9be6e1-c5cb-4772-a5a8-15bb89b604a0", - "store/ea9be6e1-c5cb-4772-a5a8-15bb89b604a0", - "store/ea9be6e1-c5cb-4772-a5a8-15bb89b604a0", - "store/ea9be6e1-c5cb-4772-a5a8-15bb89b604a0", - ), - isLiked = false, - ), - RestaurantDataEntity( - idx = 386, - name = "히포 브런치하우스", - address = "서울 마포구 연남동 487-34", - phoneNumber = "010-3796-3176", - categoryDetail = "카페", - distance = 1721, - grade = 0.0F, - reviewCount = 5, - recommendedCount = 5, - images = listOf( - "store/ea9be6e1-c5cb-4772-a5a8-15bb89b604a0", - "store/ea9be6e1-c5cb-4772-a5a8-15bb89b604a0", - "store/ea9be6e1-c5cb-4772-a5a8-15bb89b604a0", - "store/ea9be6e1-c5cb-4772-a5a8-15bb89b604a0", - ), - isLiked = false, - ), - ) - val homeViewState by homeViewModel.viewState.collectAsState() - - if (homeViewState.bottomSheetState) { - EveryMealMainBottomSheetDialog( - title = stringResource(id = R.string.univ_admin_review_title), - content = stringResource(id = R.string.univ_admin_review_content), - onClick = { + LaunchedEffect(Unit) { + homeViewModel.setEvent(HomeContract.HomeEvent.InitHomeScreen) + } - }, - onDismiss = { - homeViewModel.setEvent(HomeContract.HomeEvent.BottomSheetStateChange(false)) + LaunchedEffect(key1 = homeViewModel.effect) { + homeViewModel.effect.collect { effect -> + when (effect) { + is HomeContract.HomeEffect.NavigateToDetailListScreen -> { + onDetailScreenClickType(effect.detailListScreenType.title()) + } + is HomeContract.HomeEffect.NavigateToDetailRestaurant -> { + onDetailRestaurantClick(effect.restaurantId.toString()) + } } - ) + } } val reviewTestItem = listOf( @@ -142,102 +108,117 @@ fun HomeScreen( ), ) - Column( - modifier = Modifier - .fillMaxSize() - .background(color = Color.White), - ) { - HomeTopAppBar() - LazyColumn( - modifier = Modifier - .fillMaxWidth(), - ) { - item { - HomeMainTopLayout { - homeViewModel.setEvent(HomeContract.HomeEvent.BottomSheetStateChange(true)) - } - HomeCategoryList { - homeViewModel.setEvent(HomeContract.HomeEvent.OnClickDetailList(it.DetailListScreenType())) - } - Spacer(modifier = Modifier.padding(10.dp)) + val homeViewState by homeViewModel.viewState.collectAsState() - HomeDivider() - Spacer(modifier = Modifier.padding(10.dp)) + if (homeViewState.bottomSheetState) { + EveryMealMainBottomSheetDialog( + title = stringResource(id = R.string.univ_admin_review_title), + content = stringResource(id = R.string.univ_admin_review_content), + onClick = { - LazyColumnTitle(stringResource(R.string.home_top_good_restaurant)) - Spacer(modifier = Modifier.padding(8.dp)) + }, + onDismiss = { + homeViewModel.setEvent(HomeContract.HomeEvent.BottomSheetStateChange(false)) } - items(items.size) { index -> - val item = items[index] - EveryMealRestaurantItem( - item, - onLoveClick = { + ) + } - }, - onDetailClick = { - onDetailRestaurantClick() + when(homeViewState.uiState) { + LoadState.LOADING -> { + EveryMealLoadingDialog() + } + LoadState.SUCCESS -> { + Column( + modifier = Modifier + .fillMaxSize() + .background(color = Color.White), + ) { + HomeTopAppBar() + LazyColumn( + modifier = Modifier + .fillMaxWidth(), + ) { + item { + HomeMainTopLayout { + homeViewModel.setEvent(HomeContract.HomeEvent.BottomSheetStateChange(true)) + } + HomeCategoryList { + homeViewModel.setEvent(HomeContract.HomeEvent.OnClickDetailList(it.DetailListScreenType())) + } + Spacer(modifier = Modifier.padding(10.dp)) + + HomeDivider() + Spacer(modifier = Modifier.padding(10.dp)) + + LazyColumnTitle(stringResource(R.string.home_top_good_restaurant)) + Spacer(modifier = Modifier.padding(8.dp)) } - ) - Spacer(modifier = Modifier.padding(10.dp)) - if (index != items.size - 1) { - Divider( - modifier = Modifier - .padding(horizontal = 20.dp), - color = Gray300, - ) - } - Spacer(modifier = Modifier.padding(10.dp)) - } - item { - EveryMealLineButton( - text = stringResource(R.string.home_restaurant_button_text), - onClick = { + items(homeViewState.restaurantData.size) { index -> + val item = homeViewState.restaurantData[index] + EveryMealRestaurantItem( + item, + onLoveClick = { - }, - ) - } + }, + onDetailClick = { + homeViewModel.setEvent(HomeContract.HomeEvent.OnClickDetailRestaurant(it)) + } + ) + Spacer(modifier = Modifier.padding(10.dp)) + if (index != homeViewState.restaurantData.size - 1) { + Divider( + modifier = Modifier + .padding(horizontal = 20.dp), + color = Gray300, + ) + } + Spacer(modifier = Modifier.padding(10.dp)) + } + item { + EveryMealLineButton( + text = stringResource(R.string.home_restaurant_button_text), + onClick = { + homeViewModel.setEvent(HomeContract.HomeEvent.OnClickDetailList("추천".DetailListScreenType())) + }, + ) + } - item { - Spacer(modifier = Modifier.padding(2.dp)) - HomeDivider() - Spacer(modifier = Modifier.padding(10.dp)) + item { + Spacer(modifier = Modifier.padding(2.dp)) + HomeDivider() + Spacer(modifier = Modifier.padding(10.dp)) - LazyColumnTitle(stringResource(R.string.home_top_good_review)) - Spacer(modifier = Modifier.padding(8.dp)) - } - items(reviewTestItem.size) { index -> - val item = reviewTestItem[index] - EveryMealReviewItem(item) { + LazyColumnTitle(stringResource(R.string.home_top_good_review)) + Spacer(modifier = Modifier.padding(8.dp)) + } + items(reviewTestItem.size) { index -> + val item = reviewTestItem[index] + EveryMealReviewItem(item) { - } - Spacer(modifier = Modifier.padding(10.dp)) - if (index != reviewTestItem.size - 1) { - Divider( - modifier = Modifier - .padding(horizontal = 20.dp), - color = Gray300, - ) - } - Spacer(modifier = Modifier.padding(10.dp)) - } - item { - EveryMealLineButton( - text = stringResource(R.string.home_restaurant_review_button_text), - onClick = { + } + Spacer(modifier = Modifier.padding(10.dp)) + if (index != reviewTestItem.size - 1) { + Divider( + modifier = Modifier + .padding(horizontal = 20.dp), + color = Gray300, + ) + } + Spacer(modifier = Modifier.padding(10.dp)) + } + item { + EveryMealLineButton( + text = stringResource(R.string.home_restaurant_review_button_text), + onClick = { - }, - ) + }, + ) + } + } } } - } + LoadState.ERROR -> { - LaunchedEffect(key1 = homeViewModel.effect) { - homeViewModel.effect.collect { effect -> - when (effect) { - is HomeContract.HomeEffect.NavigateToDetailListScreen -> { - onDetailScreenClickType(effect.detailListScreenType.title()) - } - } } } } @@ -418,7 +399,8 @@ fun CategoryItem( restaurantCategoryType == stringResource(categoryText) -> Gray100 else -> Color.White }, - RoundedCornerShape(12.dp)) + RoundedCornerShape(12.dp) + ) .padding(horizontal = 17.dp, vertical = 4.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeViewModel.kt b/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeViewModel.kt index ab07f8cb..0c861883 100644 --- a/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeViewModel.kt +++ b/presentation/src/main/java/com/everymeal/presentation/ui/home/HomeViewModel.kt @@ -1,10 +1,17 @@ package com.everymeal.presentation.ui.home +import androidx.lifecycle.viewModelScope +import com.everymeal.domain.usecase.local.GetUniversityIndexUseCase +import com.everymeal.domain.usecase.restaurant.GetHomeRestaurantUseCase +import com.everymeal.domain.usecase.restaurant.GetUnivRestaurantUseCase import com.everymeal.presentation.base.BaseViewModel +import com.everymeal.presentation.base.LoadState import com.everymeal.presentation.ui.home.HomeContract.HomeEffect import com.everymeal.presentation.ui.home.HomeContract.HomeState import com.everymeal.presentation.ui.home.HomeContract.HomeEvent import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch import javax.inject.Inject data class Review( @@ -29,13 +36,17 @@ data class Restaurant( @HiltViewModel class HomeViewModel @Inject constructor( - + private val getHomeRestaurantUseCase: GetHomeRestaurantUseCase, + private val getUniversityIndexUseCase: GetUniversityIndexUseCase ): BaseViewModel( HomeState() ) { override fun handleEvents(event: HomeEvent) { when (event) { + is HomeEvent.InitHomeScreen -> { + getUnivRestaurant() + } is HomeEvent.OnClickDetailList -> { sendEffect({ HomeEffect.NavigateToDetailListScreen(event.detailListScreenType) }) } @@ -46,6 +57,33 @@ class HomeViewModel @Inject constructor( ) } } + is HomeEvent.OnClickDetailRestaurant -> { + sendEffect({ HomeEffect.NavigateToDetailRestaurant(event.restaurantId) }) + } + } + } + + private fun getUnivRestaurant() { + viewModelScope.launch { + getHomeRestaurantUseCase( + campusIdx = getUniversityIndexUseCase().first().toInt(), + order = "name", + group = null, + grade = "1" + ).onSuccess { + updateState { + copy( + uiState = LoadState.SUCCESS, + restaurantData = it.data + ) + } + }.onFailure { + updateState { + copy( + uiState = LoadState.ERROR + ) + } + } } } } \ No newline at end of file diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/main/MainScreen.kt b/presentation/src/main/java/com/everymeal/presentation/ui/main/MainScreen.kt index dec1db19..75f88b56 100644 --- a/presentation/src/main/java/com/everymeal/presentation/ui/main/MainScreen.kt +++ b/presentation/src/main/java/com/everymeal/presentation/ui/main/MainScreen.kt @@ -20,6 +20,7 @@ import com.everymeal.presentation.ui.bottom.navigateBottomNavigationScreen import com.everymeal.presentation.ui.detail.DetailListScreen import com.everymeal.presentation.ui.home.HomeScreen import com.everymeal.presentation.ui.mypage.MyPageScreen +import com.everymeal.presentation.ui.mypage.WithDrawScreen import com.everymeal.presentation.ui.restaurant.DetailRestaurantScreen import com.everymeal.presentation.ui.signup.school.SchoolAuthScreen import com.everymeal.presentation.ui.univfood.UnivFoodScreen @@ -61,8 +62,8 @@ fun MainScreen( onDetailScreenClickType = { detailScreenType -> navController.navigate(EveryMealRoute.DETAIL_LIST.route.plus("/$detailScreenType")) }, - onDetailRestaurantClick = { - navController.navigate(EveryMealRoute.SCHOOL_AUTH.route) + onDetailRestaurantClick = { detailRestaurantIdx -> + navController.navigate(EveryMealRoute.DETAIL_RESTAURANT.route.plus("/$detailRestaurantIdx")) } ) } @@ -73,7 +74,11 @@ fun MainScreen( WhatFoodScreen() } composable(route = EveryMealRoute.MY_PAGE.route) { - MyPageScreen() + MyPageScreen( + withDrawClick = { + navController.navigate(EveryMealRoute.WITH_DRAW.route) + } + ) } composable(route = EveryMealRoute.DETAIL_LIST.route.plus("/{$DETAIL_SCREEN_TYPE}")) { val detailScreenType = it.arguments?.getString(DETAIL_SCREEN_TYPE) ?: "" @@ -87,9 +92,11 @@ fun MainScreen( } composable(route = EveryMealRoute.DETAIL_RESTAURANT.route.plus("/{$DETAIL_RESTAURANT_IDX}")) { val detailRestaurantIdx = it.arguments?.getString(DETAIL_RESTAURANT_IDX) ?: "" - DetailRestaurantScreen(detailRestaurantIdx.toInt()) { - navController.popBackStack() - } + DetailRestaurantScreen( + restaurantId = detailRestaurantIdx.toInt(), + onNetWorkErrorCancelClick = { navController.popBackStack() }, + backButtonClick = { navController.popBackStack() } + ) } composable(route = EveryMealRoute.SCHOOL_AUTH.route) { SchoolAuthScreen( @@ -98,6 +105,9 @@ fun MainScreen( } ) } + composable(route = EveryMealRoute.WITH_DRAW.route) { + WithDrawScreen() + } } } diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageContract.kt b/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageContract.kt new file mode 100644 index 00000000..5d039de3 --- /dev/null +++ b/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageContract.kt @@ -0,0 +1,28 @@ +package com.everymeal.presentation.ui.mypage + +import com.everymeal.presentation.base.LoadState +import com.everymeal.presentation.base.ViewEvent +import com.everymeal.presentation.base.ViewSideEffect +import com.everymeal.presentation.base.ViewState + +class MyPageContract { + data class MyPageState( + val uiState: LoadState = LoadState.LOADING, + ) : ViewState + + sealed class MyPageEvent : ViewEvent { + + } + + sealed class MyPageEffect : ViewSideEffect { + + } +} + +enum class MyPageSetting(val type : String) { + INQUIRY("문의하기"), + SERVICE_TERMS("서비스 약관"), + OPEN_SOURCE_LICENSE("오픈소스 라이센스"), + VERSION_INFO("버전 정보"), + WITHDRAWAL("탈퇴하기") +} \ No newline at end of file diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageScreen.kt b/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageScreen.kt index 3a1640df..973edd00 100644 --- a/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageScreen.kt +++ b/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageScreen.kt @@ -17,9 +17,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarColors -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -30,6 +27,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import com.everymeal.presentation.R import com.everymeal.presentation.ui.theme.EveryMealTypography import com.everymeal.presentation.ui.theme.Gray100 @@ -40,23 +38,17 @@ import com.everymeal.presentation.ui.theme.Gray800 import com.everymeal.presentation.ui.theme.Gray900 -@OptIn(ExperimentalMaterial3Api::class) @Composable fun MyPageScreen( - + viewModel : MyPageViewModel = hiltViewModel(), + withDrawClick : () -> Unit = {} ) { - Scaffold( - topBar = { - TopAppBar( - title = { }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = Color.White, - ) - ) - } - ) { innerPadding -> + Scaffold { innerPadding -> LazyColumn( - modifier = Modifier.padding(innerPadding), + modifier = Modifier + .padding(innerPadding) + .background(Color.White) + .padding(top = 20.dp), ) { item(key = "My Information") { Spacer(modifier = Modifier.padding(8.dp)) @@ -69,7 +61,9 @@ fun MyPageScreen( } item(key = "My Activities") { - MyActivities(Modifier.padding(horizontal = 20.dp)) + MyActivities(Modifier.padding(horizontal = 20.dp)) { + + } Spacer(modifier = Modifier.padding(24.dp)) Divider( color = Gray100, @@ -79,7 +73,25 @@ fun MyPageScreen( } item(key = "My Settings") { - MySettings(Modifier.padding(horizontal = 20.dp)) + MySettings(Modifier.padding(horizontal = 20.dp)) { + when(it) { + MyPageSetting.INQUIRY.type -> { + // TODO + } + MyPageSetting.SERVICE_TERMS.type -> { + // TODO + } + MyPageSetting.OPEN_SOURCE_LICENSE.type -> { + // TODO + } + MyPageSetting.VERSION_INFO.type -> { + // TODO + } + MyPageSetting.WITHDRAWAL.type -> { + withDrawClick() + } + } + } Spacer(modifier = Modifier.padding(24.dp)) } } @@ -145,7 +157,8 @@ fun MyInformation( @Composable fun MyActivities( - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onClick: (String) -> Unit ) { Column ( modifier = modifier @@ -172,7 +185,8 @@ fun MyActivities( @Composable fun MySettings( - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onClick: (String) -> Unit ) { Column ( modifier = modifier @@ -183,25 +197,25 @@ fun MySettings( color = Gray900 ) MyTabMenu( - menuTitle = "문의하기", - onClick = { } + menuTitle = MyPageSetting.INQUIRY.type, + onClick = { onClick(MyPageSetting.INQUIRY.type) } ) MyTabMenu( - menuTitle = "서비스 약관", - onClick = { } + menuTitle = MyPageSetting.SERVICE_TERMS.type, + onClick = { onClick(MyPageSetting.SERVICE_TERMS.type) } ) MyTabMenu( - menuTitle = "오픈소스 라이센스", - onClick = { } + menuTitle = MyPageSetting.OPEN_SOURCE_LICENSE.type, + onClick = { onClick(MyPageSetting.OPEN_SOURCE_LICENSE.type) } ) MyTabMenu( - menuTitle = "오픈소스 라이센스", + menuTitle = MyPageSetting.VERSION_INFO.type, isAppVersion = true, - onClick = { } + onClick = { onClick(MyPageSetting.VERSION_INFO.type) } ) MyTabMenu( - menuTitle = "탈퇴하기", - onClick = { } + menuTitle = MyPageSetting.WITHDRAWAL.type, + onClick = { onClick(MyPageSetting.WITHDRAWAL.type) } ) } } diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageViewModel.kt b/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageViewModel.kt new file mode 100644 index 00000000..6d1a3c4f --- /dev/null +++ b/presentation/src/main/java/com/everymeal/presentation/ui/mypage/MyPageViewModel.kt @@ -0,0 +1,16 @@ +package com.everymeal.presentation.ui.mypage + +import com.everymeal.presentation.base.BaseViewModel +import javax.inject.Inject + +class MyPageViewModel @Inject constructor( + +): BaseViewModel( + MyPageContract.MyPageState() +) { + + override fun handleEvents(event: MyPageContract.MyPageEvent) { + + } +} + diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/mypage/WithDrawScreen.kt b/presentation/src/main/java/com/everymeal/presentation/ui/mypage/WithDrawScreen.kt new file mode 100644 index 00000000..113f70d1 --- /dev/null +++ b/presentation/src/main/java/com/everymeal/presentation/ui/mypage/WithDrawScreen.kt @@ -0,0 +1,8 @@ +package com.everymeal.presentation.ui.mypage + +import androidx.compose.runtime.Composable + +@Composable +fun WithDrawScreen() { + +} \ No newline at end of file diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/restaurant/DetailRestaurantScreen.kt b/presentation/src/main/java/com/everymeal/presentation/ui/restaurant/DetailRestaurantScreen.kt index 1d012fb7..f5490e38 100644 --- a/presentation/src/main/java/com/everymeal/presentation/ui/restaurant/DetailRestaurantScreen.kt +++ b/presentation/src/main/java/com/everymeal/presentation/ui/restaurant/DetailRestaurantScreen.kt @@ -49,6 +49,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel +import coil.compose.AsyncImage +import coil.compose.rememberImagePainter import com.everymeal.domain.model.restaurant.RestaurantDataEntity import com.everymeal.presentation.R import com.everymeal.presentation.base.LoadState @@ -72,7 +74,8 @@ import kotlinx.coroutines.launch fun DetailRestaurantScreen( restaurantId: Int, detailRestaurantViewModel: DetailRestaurantViewModel = hiltViewModel(), - onNetWorkErrorCancelClick: () -> Unit = {} + onNetWorkErrorCancelClick: () -> Unit = {}, + backButtonClick: () -> Unit = {}, ) { val viewState by detailRestaurantViewModel.viewState.collectAsState() @@ -89,7 +92,9 @@ fun DetailRestaurantScreen( LoadState.SUCCESS -> { Scaffold( topBar = { - SaveTopBar(title = "") + SaveTopBar(title = "") { + backButtonClick() + } }, floatingActionButton = { Row( @@ -126,7 +131,9 @@ fun DetailRestaurantScreen( // Todo Test 필요 if(!restaurantInfo.images.isNullOrEmpty()) { item { - DetailRestaurantImage() + DetailRestaurantImage( + restaurantInfo = restaurantInfo + ) } } item { @@ -221,32 +228,47 @@ fun DetailRestaurantScreen( } } +@OptIn(ExperimentalFoundationApi::class) @Composable fun DetailRestaurantImage( - + restaurantInfo : RestaurantDataEntity ) { + val images = restaurantInfo.images ?: listOf() + + val pagerState = rememberPagerState( + initialPage = 0, + initialPageOffsetFraction = 0f + ) { + images.size + } + Box( modifier = Modifier .fillMaxWidth() .aspectRatio(9f / 7f) ) { - Image( - painter = painterResource(id = R.drawable.test_image), - contentDescription = null, - contentScale = ContentScale.Crop, + HorizontalPager( + state = pagerState, modifier = Modifier.fillMaxSize() - ) + ) { page -> + AsyncImage( + model = images[page], + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + } + Text( + text = "${pagerState.currentPage + 1}/${images.size}", modifier = Modifier + .align(Alignment.BottomEnd) .padding(10.dp) .clip(RoundedCornerShape(20.dp)) - .align(Alignment.BottomStart) .background(Color.Black.copy(alpha = 0.6f)) .padding(horizontal = 8.dp, vertical = 2.dp), - text = "1/6", - style = EveryMealTypo.bodySmall, - fontSize = 14.sp, color = Color.White, + fontSize = 14.sp, ) } } @@ -322,6 +344,7 @@ fun DetailRestaurantMainInfo( .weight(1f) .padding(vertical = 12.dp), horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, ) { Image( modifier = Modifier.size(24.dp), @@ -419,7 +442,9 @@ fun DetailRestaurantTabLayout( restaurantInfo = restaurantInfo, modifier = Modifier.padding(horizontal = 20.dp) ) - 1 -> DetailRestaurantTabImage() + 1 -> DetailRestaurantTabImage( + restaurantInfo = restaurantInfo + ) 2 -> DetailRestaurantReview() } } @@ -470,10 +495,10 @@ fun DetailRestaurantTabInfo( Row( verticalAlignment = Alignment.CenterVertically, ) { - Image( - imageVector = ImageVector.vectorResource(R.drawable.icon_link_mono), - contentDescription = "link", - ) +// Image( +// imageVector = ImageVector.vectorResource(R.drawable.icon_link_mono), +// contentDescription = "link", +// ) // Version2 KakaoMap // Text( // modifier = Modifier.padding(start = 4.dp), @@ -488,36 +513,37 @@ fun DetailRestaurantTabInfo( } @Composable -fun DetailRestaurantTabImage() { - // Mock Data - val items = listOf( - R.drawable.food_ex_1, - R.drawable.food_ex_2, - R.drawable.food_ex_3, - R.drawable.food_ex_1, - R.drawable.food_ex_2, - R.drawable.food_ex_3, - R.drawable.food_ex_1, - R.drawable.food_ex_2, - R.drawable.food_ex_3, - ) - +fun DetailRestaurantTabImage( + restaurantInfo : RestaurantDataEntity +) { Column { - for (rowItems in items.chunked(3)) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 3.dp), - ) { - for (item in rowItems) { - Image( - painter = painterResource(id = item), - contentDescription = null, - modifier = Modifier - .padding(end = if (rowItems.indexOf(item) != 2) 3.dp else 0.dp) - .weight(1f) - .aspectRatio(1f) - ) + restaurantInfo.images?.let { images -> + for (rowItems in images.chunked(3)) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 3.dp), + horizontalArrangement = Arrangement.spacedBy(3.dp) + ) { + rowItems.forEach { item -> + AsyncImage( + model = item, + contentDescription = null, + modifier = Modifier + .weight(1f) + .aspectRatio(1f), + contentScale = ContentScale.Crop + ) + } + if (rowItems.size < 3) { + repeat(3 - rowItems.size) { + Box( + modifier = Modifier + .weight(1f) + .aspectRatio(1f) + ) + } + } } } } @@ -534,9 +560,3 @@ fun DetailRestaurantReview() { fun PreviewDetailRestaurantScreen() { DetailRestaurantScreen(0) } - -@Preview -@Composable -fun PreviewDetailRestaurantImage() { - DetailRestaurantImage() -} diff --git a/presentation/src/main/java/com/everymeal/presentation/ui/save/SaveScreen.kt b/presentation/src/main/java/com/everymeal/presentation/ui/save/SaveScreen.kt index 02c44478..ffcf1e41 100644 --- a/presentation/src/main/java/com/everymeal/presentation/ui/save/SaveScreen.kt +++ b/presentation/src/main/java/com/everymeal/presentation/ui/save/SaveScreen.kt @@ -1,6 +1,7 @@ package com.everymeal.presentation.ui.save import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -12,11 +13,13 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -77,6 +80,9 @@ fun SaveTopBar( onBackClick: () -> Unit = {} ) { TopAppBar( + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color.White, + ), title = { Text( text = title,