diff --git a/app/src/main/java/com/spoony/spoony/data/di/RepositoryModule.kt b/app/src/main/java/com/spoony/spoony/data/di/RepositoryModule.kt index e268d869..e1fe4b7f 100644 --- a/app/src/main/java/com/spoony/spoony/data/di/RepositoryModule.kt +++ b/app/src/main/java/com/spoony/spoony/data/di/RepositoryModule.kt @@ -1,8 +1,10 @@ package com.spoony.spoony.data.di import com.spoony.spoony.data.repositoryimpl.DummyRepositoryImpl +import com.spoony.spoony.data.repositoryimpl.ExploreRepositoryImpl import com.spoony.spoony.data.repositoryimpl.PostRepositoryImpl import com.spoony.spoony.domain.repository.DummyRepository +import com.spoony.spoony.domain.repository.ExploreRepository import com.spoony.spoony.domain.repository.PostRepository import dagger.Binds import dagger.Module @@ -17,6 +19,10 @@ abstract class RepositoryModule { @Singleton abstract fun bindDummyRepository(dummyRepositoryImpl: DummyRepositoryImpl): DummyRepository + @Binds + @Singleton + abstract fun bindExploreRepository(exploreRepositoryImpl: ExploreRepositoryImpl): ExploreRepository + @Binds @Singleton abstract fun bindPostRepository(postRepositoryImpl: PostRepositoryImpl): PostRepository diff --git a/app/src/main/java/com/spoony/spoony/data/repositoryimpl/ExploreRepositoryImpl.kt b/app/src/main/java/com/spoony/spoony/data/repositoryimpl/ExploreRepositoryImpl.kt new file mode 100644 index 00000000..56f84a23 --- /dev/null +++ b/app/src/main/java/com/spoony/spoony/data/repositoryimpl/ExploreRepositoryImpl.kt @@ -0,0 +1,152 @@ +package com.spoony.spoony.data.repositoryimpl + +import com.spoony.spoony.domain.entity.CategoryEntity +import com.spoony.spoony.domain.entity.FeedEntity +import com.spoony.spoony.domain.repository.ExploreRepository +import javax.inject.Inject + +class ExploreRepositoryImpl @Inject constructor() : ExploreRepository { + override suspend fun getCategoryList(): Result> = Result.success( + listOf( + CategoryEntity( + categoryId = 1, + categoryName = "전체", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/all_white.png", + unSelectedIconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/all_black.png" + ), + CategoryEntity( + categoryId = 2, + categoryName = "로컬 수저", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/local_white.png", + unSelectedIconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/all_black.png" + ), + CategoryEntity( + categoryId = 3, + categoryName = "한식", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/korean_white.png", + unSelectedIconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/korean_black.png" + ), + CategoryEntity( + categoryId = 4, + categoryName = "일식", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/japanese_white.png", + unSelectedIconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/japanese_black.png" + ), + CategoryEntity( + categoryId = 5, + categoryName = "중식", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/chinese_white.png", + unSelectedIconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/chinese_black.png" + ), + CategoryEntity( + categoryId = 6, + categoryName = "양식", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/american_white.png", + unSelectedIconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/american_black.png" + ), + CategoryEntity( + categoryId = 7, + categoryName = "퓨전/세계요리", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/world_white.png", + unSelectedIconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/world_black.png" + ), + CategoryEntity( + categoryId = 8, + categoryName = "카페", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/cafe_white.png", + unSelectedIconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/cafe_black.png" + ), + CategoryEntity( + categoryId = 9, + categoryName = "주류", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/drink_white.png", + unSelectedIconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/drink_black.png" + ) + ) + ) + + override suspend fun getFeedList( + userId: Int, + categoryId: Int, + locationQuery: String, + sortBy: String + ): Result> = Result.success( + listOf( + FeedEntity( + userId = 3, + userName = "두더지", + userRegion = "용산구", + postId = 3, + title = "스타벅스 삼성역섬유센터R점 리뷰", + categoryInfo = CategoryEntity( + categoryId = 2, + categoryName = "카페", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/cafe_color.png", + textColor = "FFE4E5", + backgroundColor = "FF7E84" + ), + zzimCount = 1 + ), + FeedEntity( + userId = 3, + userName = "두더지", + userRegion = "용산구", + postId = 4, + title = "스타벅스 삼성역섬유센터R점 리뷰", + categoryInfo = CategoryEntity( + categoryId = 2, + categoryName = "카페", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/cafe_color.png", + textColor = "FFE4E5", + backgroundColor = "FF7E84" + ), + zzimCount = 1 + ), + FeedEntity( + userId = 3, + userName = "두더지", + userRegion = "용산구", + postId = 5, + title = "스타벅스 삼성역섬유센터R점 리뷰", + categoryInfo = CategoryEntity( + categoryId = 2, + categoryName = "카페", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/cafe_color.png", + textColor = "FFE4E5", + backgroundColor = "FF7E84" + ), + zzimCount = 1 + ), + FeedEntity( + userId = 3, + userName = "두더지", + userRegion = "용산구", + postId = 6, + title = "스타벅스 삼성역섬유센터R점 리뷰", + categoryInfo = CategoryEntity( + categoryId = 2, + categoryName = "카페", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/cafe_color.png", + textColor = "FFE4E5", + backgroundColor = "FF7E84" + ), + zzimCount = 1 + ), + FeedEntity( + userId = 3, + userName = "두더지", + userRegion = "용산구", + postId = 7, + title = "스타벅스 삼성역섬유센터R점 리뷰", + categoryInfo = CategoryEntity( + categoryId = 2, + categoryName = "카페", + iconUrl = "https://spoony-storage.s3.ap-northeast-2.amazonaws.com/category/icons/cafe_color.png", + textColor = "FFE4E5", + backgroundColor = "FF7E84" + ), + zzimCount = 1 + ) + ) + ) +} diff --git a/app/src/main/java/com/spoony/spoony/domain/entity/FeedEntity.kt b/app/src/main/java/com/spoony/spoony/domain/entity/FeedEntity.kt new file mode 100644 index 00000000..2cc6d745 --- /dev/null +++ b/app/src/main/java/com/spoony/spoony/domain/entity/FeedEntity.kt @@ -0,0 +1,11 @@ +package com.spoony.spoony.domain.entity + +data class FeedEntity( + val userId: Int, + val userName: String, + val userRegion: String, + val postId: Int, + val title: String, + val categoryInfo: CategoryEntity, + val zzimCount: Int +) diff --git a/app/src/main/java/com/spoony/spoony/domain/repository/ExploreRepository.kt b/app/src/main/java/com/spoony/spoony/domain/repository/ExploreRepository.kt new file mode 100644 index 00000000..4054eceb --- /dev/null +++ b/app/src/main/java/com/spoony/spoony/domain/repository/ExploreRepository.kt @@ -0,0 +1,15 @@ +package com.spoony.spoony.domain.repository + +import com.spoony.spoony.domain.entity.CategoryEntity +import com.spoony.spoony.domain.entity.FeedEntity + +interface ExploreRepository { + suspend fun getCategoryList(): Result> + + suspend fun getFeedList( + userId: Int, + categoryId: Int, + locationQuery: String, + sortBy: String + ): Result> +} diff --git a/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreScreen.kt b/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreScreen.kt index 2a9733fc..59e9115c 100644 --- a/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreScreen.kt +++ b/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreScreen.kt @@ -1,6 +1,5 @@ package com.spoony.spoony.presentation.explore -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -49,6 +48,7 @@ import kotlinx.collections.immutable.persistentListOf @Composable fun ExploreRoute( paddingValues: PaddingValues, + navigateToPlaceDetail: (Int, Int) -> Unit, viewModel: ExploreViewModel = hiltViewModel() ) { val state = viewModel.state.collectAsStateWithLifecycle() @@ -74,14 +74,13 @@ fun ExploreRoute( feedList = feedList, onLocationSortingButtonClick = viewModel::updateSelectedCity, onSortingButtonClick = viewModel::updateSelectedSortingOption, - onFeedItemClick = {}, + onFeedItemClick = { navigateToPlaceDetail(it, 1) }, onRegisterButtonClick = {}, updateSelectedCategory = viewModel::updateSelectedCategory ) } } -@OptIn(ExperimentalFoundationApi::class) @Composable private fun ExploreScreen( paddingValues: PaddingValues, @@ -143,6 +142,7 @@ private fun ExploreScreen( unSelectedIconUrl = category.unSelectedIconUrl ?: "", selectedIconUrl = category.iconUrl, isSelected = selectedCategoryId == category.categoryId, + isGradient = true, onClick = { updateSelectedCategory(category.categoryId) } ) } diff --git a/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreState.kt b/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreState.kt index 0b128d96..26caa0de 100644 --- a/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreState.kt +++ b/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreState.kt @@ -9,7 +9,7 @@ import kotlinx.collections.immutable.ImmutableList data class ExploreState( val spoonCount: UiState = UiState.Loading, val selectedCity: String = "마포구", - val selectedCategoryId: Int = 0, + val selectedCategoryId: Int = 1, val selectedSortingOption: SortingOption = SortingOption.LATEST, val categoryList: UiState> = UiState.Loading, val feedList: UiState> = UiState.Loading diff --git a/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreViewModel.kt b/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreViewModel.kt index 861d78d8..c9a5faf6 100644 --- a/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreViewModel.kt +++ b/app/src/main/java/com/spoony/spoony/presentation/explore/ExploreViewModel.kt @@ -1,19 +1,90 @@ package com.spoony.spoony.presentation.explore import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.spoony.spoony.core.state.UiState +import com.spoony.spoony.domain.repository.ExploreRepository +import com.spoony.spoony.presentation.explore.model.toModel import com.spoony.spoony.presentation.explore.type.SortingOption import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch @HiltViewModel -class ExploreViewModel @Inject constructor() : ViewModel() { +class ExploreViewModel @Inject constructor( + private val exploreRepository: ExploreRepository +) : ViewModel() { private var _state: MutableStateFlow = MutableStateFlow(ExploreState()) val state: StateFlow get() = _state + init { + getCategoryList() + getFeedList( + userId = 1, + categoryId = 2, + locationQuery = "강남", + sortBy = "latest" + ) + } + + private fun getCategoryList() { + viewModelScope.launch { + exploreRepository.getCategoryList() + .onSuccess { response -> + _state.update { + it.copy( + categoryList = UiState.Success(response.toImmutableList()) + ) + } + } + .onFailure { + _state.update { + it.copy( + categoryList = UiState.Failure("카테고리 목록 조회 실패") + ) + } + } + } + } + + fun getFeedList( + userId: Int, + categoryId: Int, + locationQuery: String, + sortBy: String + ) { + viewModelScope.launch { + exploreRepository.getFeedList( + userId = userId, + categoryId = categoryId, + locationQuery = locationQuery, + sortBy = sortBy + ).onSuccess { response -> + _state.update { + it.copy( + feedList = UiState.Success( + response.map { feed -> + feed.toModel() + }.toImmutableList() + ) + ) + } + } + .onFailure { + _state.update { + it.copy( + feedList = UiState.Failure("피드 목록 조회 실패") + ) + } + } + } + } + fun updateSelectedSortingOption(sortingOption: SortingOption) { _state.update { it.copy( diff --git a/app/src/main/java/com/spoony/spoony/presentation/explore/model/FeedModel.kt b/app/src/main/java/com/spoony/spoony/presentation/explore/model/FeedModel.kt index aa984e74..148f5f33 100644 --- a/app/src/main/java/com/spoony/spoony/presentation/explore/model/FeedModel.kt +++ b/app/src/main/java/com/spoony/spoony/presentation/explore/model/FeedModel.kt @@ -1,6 +1,7 @@ package com.spoony.spoony.presentation.explore.model import com.spoony.spoony.domain.entity.CategoryEntity +import com.spoony.spoony.domain.entity.FeedEntity data class FeedModel( val feedId: Int, @@ -11,3 +12,13 @@ data class FeedModel( val categoryEntity: CategoryEntity, val addMapCount: Int ) + +fun FeedEntity.toModel(): FeedModel = FeedModel( + feedId = this.postId, + userId = this.userId, + username = this.userName, + userRegion = this.userRegion, + title = this.title, + categoryEntity = this.categoryInfo, + addMapCount = this.zzimCount +) diff --git a/app/src/main/java/com/spoony/spoony/presentation/explore/navigation/ExploreNavigation.kt b/app/src/main/java/com/spoony/spoony/presentation/explore/navigation/ExploreNavigation.kt index 5d72619f..ef9be411 100644 --- a/app/src/main/java/com/spoony/spoony/presentation/explore/navigation/ExploreNavigation.kt +++ b/app/src/main/java/com/spoony/spoony/presentation/explore/navigation/ExploreNavigation.kt @@ -3,10 +3,12 @@ package com.spoony.spoony.presentation.explore.navigation import androidx.compose.foundation.layout.PaddingValues import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.spoony.spoony.core.navigation.MainTabRoute import com.spoony.spoony.presentation.explore.ExploreRoute +import com.spoony.spoony.presentation.placeDetail.navigation.navigateToPlaceDetail import kotlinx.serialization.Serializable fun NavController.navigateToExplore( @@ -16,11 +18,13 @@ fun NavController.navigateToExplore( } fun NavGraphBuilder.exploreNavGraph( - paddingValues: PaddingValues + paddingValues: PaddingValues, + navHostController: NavHostController ) { composable { ExploreRoute( - paddingValues = paddingValues + paddingValues = paddingValues, + navigateToPlaceDetail = navHostController::navigateToPlaceDetail ) } } diff --git a/app/src/main/java/com/spoony/spoony/presentation/main/MainScreen.kt b/app/src/main/java/com/spoony/spoony/presentation/main/MainScreen.kt index 3a36450d..e4b4d959 100644 --- a/app/src/main/java/com/spoony/spoony/presentation/main/MainScreen.kt +++ b/app/src/main/java/com/spoony/spoony/presentation/main/MainScreen.kt @@ -45,7 +45,8 @@ fun MainScreen( mapNavGraph() exploreNavGraph( - paddingValues = innerPadding + paddingValues = innerPadding, + navHostController = navigator.navController ) registerNavGraph(