diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/data/remote/responseDTO/FollowInfoDataDTO.kt b/app/src/main/java/com/d_vide/D_VIDE/app/data/remote/responseDTO/FollowInfoDataDTO.kt index 90ac5dd5..d84475dd 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/data/remote/responseDTO/FollowInfoDataDTO.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/data/remote/responseDTO/FollowInfoDataDTO.kt @@ -2,7 +2,7 @@ package com.d_vide.D_VIDE.app.data.remote.responseDTO data class FollowInfoDataDTO ( val userId: Long, - val profileImageUrl: String, + val profileImgUrl: String, val nickname: String, val followId: Long ) \ No newline at end of file diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/GetRecruitings.kt b/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/GetRecruitings.kt index b449d688..8b1f3ef9 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/GetRecruitings.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/GetRecruitings.kt @@ -18,7 +18,7 @@ class GetRecruitings @Inject constructor( latitude: Double, longitude: Double, category: Category, - offset: Int + offset: Int = 0 ): Flow> = flow { try { diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetMyReviews.kt b/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetMyReviews.kt index f3c79ae5..efdc761b 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetMyReviews.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetMyReviews.kt @@ -14,7 +14,7 @@ import javax.inject.Inject class GetMyReviews @Inject constructor( private val repository: ReviewRepository, ) { - operator fun invoke(first: Int): Flow> = flow { + operator fun invoke(first: Int = 0): Flow> = flow { try { emit(Resource.Loading()) diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetReviews.kt b/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetReviews.kt index 7c25fa0b..4544a848 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetReviews.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetReviews.kt @@ -16,7 +16,7 @@ class GetReviews @Inject constructor( operator fun invoke( longitude: Double, latitude: Double, - first: Int + first: Int = 1 ): Flow> = flow { try { diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetStoreReview.kt b/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetStoreReview.kt index 0ca55901..18248694 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetStoreReview.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/domain/use_case/Review/GetStoreReview.kt @@ -15,7 +15,7 @@ import javax.inject.Inject class GetStoreReview @Inject constructor( private val repository: ReviewRepository, ) { - operator fun invoke(first: Int, storeName: String): Flow> = flow { + operator fun invoke(first: Int = 0, storeName: String): Flow> = flow { try { emit(Resource.Loading()) diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/navigation/NavGraph.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/navigation/NavGraph.kt index f982b8a3..e56aa82c 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/navigation/NavGraph.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/navigation/NavGraph.kt @@ -236,7 +236,8 @@ private fun NavGraphBuilder.MyReviewNavGraph( navController = navController, onReviewSelected = { id -> onReviewClick(id, from) }, onTagClick = { id -> onTagClick(id, from) }, - onRecruitingClick = { id -> onRecruitingClick(id, from) } + onRecruitingClick = { id -> onRecruitingClick(id, from) }, + upPress = upPress ) } diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/FollowState.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/FollowState.kt index 4ed0c167..ef529a3a 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/FollowState.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/FollowState.kt @@ -7,5 +7,8 @@ data class FollowState( val isLoading: Boolean = false, val follows: List = emptyList(), val otherFollows: List = emptyList(), - val error: String = "" + val error: String = "", + val offset: Int = 0, + val endReached: Boolean = false, + val pagingLoading : Boolean = false, ) \ No newline at end of file diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/FollowViewModel.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/FollowViewModel.kt index 1cdb731b..28f68d24 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/FollowViewModel.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/FollowViewModel.kt @@ -36,48 +36,86 @@ class FollowViewModel @Inject constructor( private var _followIdDTO = mutableStateOf(FollowIdDTO(userInfo.userId)) var followIdDTO: State = _followIdDTO - fun getFollowInfo(relation: String="FOLLOWER", first: Int=0){ + fun getFollowInfo(relation: String="FOLLOWER"){ viewModelScope.launch { - getFollowInfoUseCase(relation, first).collect() { result -> + getFollowInfoUseCase(relation, _state.value.offset).collect() { result -> when (result) { is Resource.Success -> { _state.update { it.copy( - follows = result.data?.follows ?: emptyList(), - isLoading = false + follows = it.follows + (result.data?.follows ?: emptyList()), + isLoading = false, + offset = it.offset + (result.data?.follows?.size ?: 0), + pagingLoading = false, + endReached = result.data?.follows?.size!! < 10 ) } + Log.d("팔로잉", "성공") } is Resource.Error -> { - _state.value = - FollowState(error = result.message ?: "An unexpected error occured") - Log.d("test", "error") + _state.update { + it.copy( + error = result.message ?: "An unexpected error occured in my review", + isLoading = false, + pagingLoading = false, + endReached = true + ) + } + Log.d("팔로잉", "error") } is Resource.Loading -> { - _state.value = FollowState(isLoading = true) - Log.d("test", "loading") + _state.update { + it.copy( + isLoading = true, + pagingLoading = true + ) + } + Log.d("팔로잉", "loading") } } } } } - fun getOtherFollow(relation: String="FOLLOWER", first: Int=0, userId: Long){ - getOtherFollowUseCase(relation, first, userId).onEach { result -> - when (result){ - is Resource.Success -> { - _state.value = result.data?.let { FollowState(otherFollows = it, isLoading = false)}!! - } - is Resource.Error -> { - _state.value = FollowState(error = result.message ?: "An unexpected error occured") - Log.d("test", "error") - } - is Resource.Loading -> { - _state.value = FollowState(isLoading = true) - Log.d("test", "loading") + fun getOtherFollow(relation: String="FOLLOWER", userId: Long){ + viewModelScope.launch { + getOtherFollowUseCase(relation, _state.value.offset, userId).collect() { result -> + when (result){ + is Resource.Success -> { + _state.update { + it.copy( + otherFollows = it.otherFollows + (result.data ?: emptyList()), + isLoading = false, + offset = it.offset + (result.data?.size ?: 0), + pagingLoading = false, + endReached = result.data?.size!! < 10 + ) + } + Log.d("팔로워", "성공 ${_state.value.endReached}") + } + is Resource.Error -> { + _state.update { + it.copy( + error = result.message ?: "An unexpected error occured in my review", + isLoading = false, + pagingLoading = false, + endReached = true + ) + } + Log.d("팔로워", "error") + } + is Resource.Loading -> { + _state.update { + it.copy( + isLoading = true, + pagingLoading = true + ) + } + Log.d("팔로워", "loading") + } } } - }.launchIn(viewModelScope) + } } fun postFollow(userId: Long){ _userIdDTO.value = userIdDTO.value.copy( diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/MyFollowScreen.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/MyFollowScreen.kt index e2ed192b..59147a95 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/MyFollowScreen.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/MyFollowScreen.kt @@ -3,10 +3,9 @@ package com.d_vide.D_VIDE.app.presentation.view.Followings import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterHorizontally @@ -14,11 +13,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController +import com.d_vide.D_VIDE.app._constants.Const import com.d_vide.D_VIDE.app.domain.util.log import com.d_vide.D_VIDE.app.presentation.view.UserFeed.BottomSheetUserFeedScreen import com.d_vide.D_VIDE.app.presentation.view.UserFeed.UserProfileViewModel import com.d_vide.D_VIDE.app.presentation.util.GradientComponent import com.d_vide.D_VIDE.app.presentation.view.Followings.components.FollowingItem +import com.d_vide.D_VIDE.app.presentation.view.component.BlankIndicator import com.d_vide.D_VIDE.app.presentation.view.component.TopRoundBar import kotlinx.coroutines.launch @@ -34,10 +35,16 @@ fun MyFollowScreen( ) { val userViewModel = hiltViewModel() val userId = rememberSaveable{ mutableStateOf(0L) } + val viewModelState by followViewModel.state.collectAsState() + val follows = viewModelState.follows + val endReached = viewModelState.endReached + val pagingLoading = viewModelState.pagingLoading + var relation = "" + LaunchedEffect(key1 = true) { - val relation = if(isFollowing) "FOLLOWING" else "FOLLOWER" - followViewModel.getFollowInfo(relation, 0) + relation = if(isFollowing) "FOLLOWING" else "FOLLOWER" + followViewModel.getFollowInfo(relation) } BottomSheetUserFeedScreen( @@ -53,32 +60,45 @@ fun MyFollowScreen( modifier = Modifier.fillMaxHeight() ) { LazyColumn( + modifier = Modifier.align(Alignment.Center), horizontalAlignment = CenterHorizontally, verticalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues(top = 16.dp) ) { - followViewModel.state.value.follows.forEach { item -> + itemsIndexed(follows) { index, item -> + if (index >= follows.size - 1 && !endReached && !pagingLoading) { + followViewModel.getFollowInfo(relation) + } + FollowingItem( + userName = item.nickname, + profileUrl = item.profileImgUrl, + modifier = Modifier.padding(start = 33.dp, end = 40.dp), + onUserClick = { + userId.value = item.userId + userViewModel.getOtherUserInfo(item.userId) + scope.launch { + state.animateTo( + ModalBottomSheetValue.Expanded, + tween(500) + ) + } + }, + isFollowing = isFollowing, + userId = item.userId, + followId = item.followId, + ) + } + + if(follows.isEmpty()) { item { - FollowingItem( - userName = item.nickname, - profileUrl = item.profileImageUrl, - modifier = Modifier.padding(start = 33.dp, end = 40.dp), - onUserClick = { - userId.value = item.userId - userViewModel.getOtherUserInfo(item.userId) - scope.launch { - state.animateTo( - ModalBottomSheetValue.Expanded, - tween(500) - ) - } - }, - isFollowing = isFollowing, - userId = item.userId, - followId = item.followId, + BlankIndicator( + modifier = Modifier + .align(Alignment.Center), + text = "회원님이 팔로우하는 사람들이\n 여기에 표시됩니다." ) } } + item { Spacer(Modifier.padding(bottom = Const.UIConst.HEIGHT_BOTTOM_BAR)) } } GradientComponent(Modifier.align(Alignment.BottomCenter)) diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/OtherFollowScreen.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/OtherFollowScreen.kt index ea4b213c..bd8bf676 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/OtherFollowScreen.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Followings/OtherFollowScreen.kt @@ -3,12 +3,11 @@ package com.d_vide.D_VIDE.app.presentation.view.Followings import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Scaffold -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterHorizontally @@ -20,6 +19,7 @@ import com.d_vide.D_VIDE.app.presentation.view.UserFeed.BottomSheetUserFeedScree import com.d_vide.D_VIDE.app.presentation.view.UserFeed.UserFeedViewModel import com.d_vide.D_VIDE.app.presentation.util.GradientComponent import com.d_vide.D_VIDE.app.presentation.view.Followings.components.FollowingItem +import com.d_vide.D_VIDE.app.presentation.view.component.BlankIndicator import com.d_vide.D_VIDE.app.presentation.view.component.TopRoundBar import kotlinx.coroutines.launch @@ -35,12 +35,17 @@ fun OtherFollowScreen( userViewModel: UserFeedViewModel = hiltViewModel(), followViewModel: FollowViewModel = hiltViewModel(), ) { - val otherFollows = followViewModel.state.value.otherFollows val coroutine = rememberCoroutineScope() val userRememberId = rememberSaveable{ mutableStateOf(0L) } + val viewModelState by followViewModel.state.collectAsState() + val otherFollows = viewModelState.otherFollows + val endReached = viewModelState.endReached + val pagingLoading = viewModelState.pagingLoading + var relation = "" - coroutine.launch { - followViewModel.getOtherFollow(relation = if(isFollowing) "FOLLOWING" else "FOLLOWER", userId = userId) + LaunchedEffect(key1 = true) { + relation = if(isFollowing) "FOLLOWING" else "FOLLOWER" + followViewModel.getOtherFollow(relation = relation, userId = userId) } BottomSheetUserFeedScreen( navController = navController, @@ -55,33 +60,46 @@ fun OtherFollowScreen( modifier = Modifier.fillMaxHeight() ) { LazyColumn( + modifier = Modifier.align(Alignment.Center), horizontalAlignment = CenterHorizontally, verticalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues(top = 16.dp, bottom = 16.dp) ) { - otherFollows.forEach { item -> + itemsIndexed(otherFollows) { index, item -> + if (index >= otherFollows.size - 1 && !endReached && !pagingLoading) { + followViewModel.getOtherFollow(relation, userId) + } + FollowingItem( + userName = item.nickname, + profileUrl = item.profileImgUrl, + modifier = Modifier.padding(start = 33.dp, end = 40.dp), + onUserClick = { + userRememberId.value = item.userId + scope.launch { + userViewModel.getOtherUserInfo(item.userId) + state.animateTo( + ModalBottomSheetValue.Expanded, + tween(500) + ) + } + }, + isFollowing = true, + userId = item.userId, + followId = item.followId, + followed = item.followed + ) + } + if(otherFollows.isEmpty()) { item { - FollowingItem( - userName = item.nickname, - profileUrl = item.profileImgUrl, - modifier = Modifier.padding(start = 33.dp, end = 40.dp), - onUserClick = { - userRememberId.value = item.userId - scope.launch { - userViewModel.getOtherUserInfo(item.userId) - state.animateTo( - ModalBottomSheetValue.Expanded, - tween(500) - ) - } - }, - isFollowing = true, - userId = item.userId, - followId = item.followId, - followed = item.followed + BlankIndicator( + modifier = Modifier + .align(Alignment.Center), + text = "회원님을 팔로우하는 사람들이\n 여기에 표시됩니다." ) } } + + } GradientComponent(Modifier.align(Alignment.BottomCenter)) diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersScreen.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersScreen.kt index e352c773..a0aa93b7 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersScreen.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersScreen.kt @@ -5,19 +5,24 @@ package com.d_vide.D_VIDE.app.presentation.view.MyOrders import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Scaffold 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.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController +import com.d_vide.D_VIDE.app._constants.Const import com.d_vide.D_VIDE.app.presentation.view.MyOrders.component.MyCompletedOrder import com.d_vide.D_VIDE.app.presentation.view.UserFeed.BottomSheetUserFeedScreen import com.d_vide.D_VIDE.app.presentation.view.component.TopRoundBar import com.d_vide.D_VIDE.app.presentation.navigation.Screen import com.d_vide.D_VIDE.app.presentation.util.* +import com.d_vide.D_VIDE.app.presentation.view.component.BlankIndicator import com.d_vide.D_VIDE.ui.theme.background @OptIn(ExperimentalMaterialApi::class) @@ -30,6 +35,12 @@ fun MyOrdersScreen( onTagClick: (String) -> Unit, onRecruitingClick: (Int) -> Unit ) { + val viewModelState by viewModel.state.collectAsState() + val recruitings = viewModelState.recruitings + val endReached = viewModelState.endReached + val pagingLoading = viewModelState.pagingLoading + + Scaffold( topBar = { TopRoundBar("주문내역", onClick = upPress) } ) { @@ -51,23 +62,36 @@ fun MyOrdersScreen( ) { item { Spacer(modifier = Modifier.width(9.dp)) } - viewModel.state.value.recruitingDTOs.forEach { + itemsIndexed(recruitings) { index, item -> + if (index >= recruitings.size - 1 && !endReached && !pagingLoading) { + viewModel.getMyOrders() + } + + MyCompletedOrder( + onClick = { onRecruitingClick(item.post.id) }, + onButtonClick = { navController.navigate(Screen.PostReviewScreen.route) }, + title = item.post.title, + imageURL = item.user.profileImgUrl, + price = item.post.orderedPrice, + time = item.post.targetTime.convertTimestampToHour() + .toString() + ":" + + item.post.targetTime.convertTimestampToMinute() + .toString(), + date = (item.post.targetTime * 1000).convertTimestampToPointFullDate(), + enabled = item.post.status == "RECRUIT_SUCCESS" + ) + } + + if(recruitings.isEmpty()) { item { - MyCompletedOrder( - onClick = { onRecruitingClick(it.post.id) }, - onButtonClick = { navController.navigate(Screen.PostReviewScreen.route) }, - title = it.post.title, - imageURL = it.user.profileImgUrl, - price = it.post.orderedPrice, - time = it.post.targetTime.convertTimestampToHour() - .toString() + ":" - + it.post.targetTime.convertTimestampToMinute() - .toString(), - date = (it.post.targetTime * 1000).convertTimestampToPointFullDate(), - enabled = it.post.status == "RECRUIT_SUCCESS" + BlankIndicator( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(vertical = 78.dp) ) } } + item { Spacer(Modifier.padding(bottom = Const.UIConst.HEIGHT_BOTTOM_BAR)) } } } GradientComponent(Modifier.align(Alignment.BottomCenter)) diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersState.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersState.kt index 9acda31b..3f877168 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersState.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersState.kt @@ -4,6 +4,9 @@ import com.d_vide.D_VIDE.app.data.remote.responseDTO.RecruitingDTO data class MyOrdersState( val isLoading: Boolean = false, - val recruitingDTOs: List = emptyList(), - val error: String = "" + val recruitings: List = emptyList(), + val error: String = "", + val offset: Int = 0, + val endReached: Boolean = false, + val pagingLoading : Boolean = false, ) diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersViewModel.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersViewModel.kt index 2b1b5147..e4415ed3 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersViewModel.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyOrders/MyOrdersViewModel.kt @@ -8,6 +8,8 @@ import com.d_vide.D_VIDE.app.domain.use_case.GetMyOrders import com.d_vide.D_VIDE.app.domain.util.Resource import com.d_vide.D_VIDE.app.domain.util.log import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -16,8 +18,8 @@ class MyOrdersViewModel @Inject constructor( val getMyOrdersUseCase: GetMyOrders, ) : ViewModel() { - private val _state = mutableStateOf(MyOrdersState()) - val state: State = _state + private val _state = MutableStateFlow(MyOrdersState()) + val state = _state init { getMyOrders() @@ -25,15 +27,43 @@ class MyOrdersViewModel @Inject constructor( fun getMyOrders() { viewModelScope.launch { - getMyOrdersUseCase().collect() { - when (it) { + getMyOrdersUseCase().collect() { result -> + when (result) { is Resource.Success -> { - _state.value = - it.data?.let { MyOrdersState(recruitingDTOs = it.recruitingDTOS) }!! - "내 주문 목록 가져오기 성공".log() + _state.update { + it.copy( + recruitings = it.recruitings + (result.data?.recruitingDTOS ?: emptyList()), + isLoading = false, + offset = it.offset + (result.data?.recruitingDTOS?.size ?: 0), + pagingLoading = false, + endReached = result.data?.recruitingDTOS?.size!! < 10 + ) + } + + "내 주문 목록 가져오기 성공${result.data?.recruitingDTOS?.size ?: 0}".log() + } + is Resource.Error -> { + _state.update { + it.copy( + error = result.message ?: "An unexpected error occured in my order", + isLoading = false, + pagingLoading = false, + endReached = true + ) + } + "내 주문 목록 가져오기 실패".log() } - is Resource.Error -> "내 주문 목록 가져오기 실패".log() - is Resource.Loading -> "내 주문 목록 가져오는 중".log() + + is Resource.Loading -> { + _state.update { + it.copy( + isLoading = true, + pagingLoading = true + ) + } + "내 주문 목록 가져오는 중".log() + } + } } } diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsScreen.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsScreen.kt index cb7fa235..867973bf 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsScreen.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsScreen.kt @@ -5,9 +5,12 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterEnd import androidx.compose.ui.Alignment.Companion.CenterStart import androidx.compose.ui.Modifier @@ -18,8 +21,10 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.d_vide.D_VIDE.R +import com.d_vide.D_VIDE.app._constants.Const import com.d_vide.D_VIDE.app.presentation.view.TaggedReviews.component.ReviewItem import com.d_vide.D_VIDE.app.presentation.util.MoreButton +import com.d_vide.D_VIDE.app.presentation.view.component.BlankIndicator import com.d_vide.D_VIDE.ui.theme.TextStyles import com.d_vide.D_VIDE.ui.theme.gray2 @@ -31,7 +36,10 @@ fun MyReviewsScreen( upPress: () -> Unit = {} ){ val viewModel = hiltViewModel() - val reviews = viewModel.state.value.reviewsDTO + val viewModelState by viewModel.state.collectAsState() + val reviews = viewModelState.reviews + val endReached = viewModelState.endReached + val pagingLoading = viewModelState.pagingLoading Column( modifier = Modifier @@ -65,16 +73,38 @@ fun MyReviewsScreen( ) } LazyColumn( + modifier = Modifier + .align(Alignment.CenterHorizontally), contentPadding = PaddingValues(vertical = 28.dp), - verticalArrangement = Arrangement.spacedBy(15.dp) + verticalArrangement = Arrangement.spacedBy(15.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { - items(reviews) { item -> + itemsIndexed(reviews) { index, item -> + if (index >= reviews.size - 1 && !endReached && !pagingLoading) { + viewModel.getMyOrders() + } ReviewItem( onReviewClick = { onReviewSelected(item.review.reviewId.toInt()) }, onTagClick = { onTagClick(item.review.storeName) }, - isLiked = item.review.isLiked + onLikeClick = {if(item.review.isLiked) viewModel.postUnlike(index) else viewModel.postLike(index)}, + isLiked = item.review.isLiked, + userImageURL = item.user.profileImgUrl, + userName = item.user.nickname, + reviewTitle = item.review.storeName, + reviewText = item.review.content, + reviewImage = item.review.reviewImgUrl ) } + if(reviews.isEmpty()) { + item { + BlankIndicator( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(vertical = 78.dp) + ) + } + } + item { Spacer(Modifier.padding(bottom = Const.UIConst.HEIGHT_BOTTOM_BAR)) } } } } \ No newline at end of file diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsState.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsState.kt index 32935b10..c88e8602 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsState.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsState.kt @@ -5,6 +5,9 @@ import com.d_vide.D_VIDE.app.data.remote.responseDTO.Review.ReviewDTO data class MyReviewsState( val isLoading: Boolean = false, - val reviewsDTO: List = emptyList(), - val error: String = "" + val reviews: List = emptyList(), + val error: String = "", + val offset: Int = 1, + val endReached: Boolean = false, + val pagingLoading : Boolean = false, ) \ No newline at end of file diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsViewModel.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsViewModel.kt index f774f62c..90d3bf4e 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsViewModel.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/MyReviews/MyReviewsViewModel.kt @@ -1,23 +1,32 @@ package com.d_vide.D_VIDE.app.presentation.view.MyReviews +import android.util.Log import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.d_vide.D_VIDE.app.domain.use_case.Review.GetMyReviews +import com.d_vide.D_VIDE.app.domain.use_case.Review.PostLike +import com.d_vide.D_VIDE.app.domain.use_case.Review.PostUnlike import com.d_vide.D_VIDE.app.domain.util.Resource import com.d_vide.D_VIDE.app.domain.util.log import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class MyReviewsViewModel @Inject constructor( private val getMyReviewsUseCase: GetMyReviews, + private val postLikeUseCase: PostLike, + private val postUnlikeUseCase: PostUnlike ) : ViewModel() { - private val _state = mutableStateOf(MyReviewsState()) - val state: State = _state + private val _state = MutableStateFlow(MyReviewsState()) + val state = _state init { getMyOrders() @@ -25,17 +34,67 @@ class MyReviewsViewModel @Inject constructor( fun getMyOrders() { viewModelScope.launch { - getMyReviewsUseCase(0).collect() { - when (it) { + getMyReviewsUseCase(_state.value.offset).collect() { result -> + when (result) { is Resource.Success -> { - _state.value = - it.data?.let { MyReviewsState(reviewsDTO = it.reviews) }!! + _state.update { + it.copy( + reviews = it.reviews + (result.data?.reviews ?: emptyList()), + isLoading = false, + offset = it.offset + (result.data?.reviews?.size ?: 0), + pagingLoading = false, + endReached = result.data?.reviews?.size!! < 10 + ) + } "내 리뷰 목록 가져오기 성공".log() } - is Resource.Error -> "내 리뷰 목록 가져오기 실패".log() - is Resource.Loading -> "내 리뷰 목록 가져오는 중".log() + is Resource.Error -> { + _state.update { + it.copy( + error = result.message ?: "An unexpected error occured in my review", + isLoading = false, + pagingLoading = false, + endReached = true + ) + } + + "내 리뷰 목록 가져오기 실패".log() + } + is Resource.Loading -> { + _state.update { + it.copy( + isLoading = true, + pagingLoading = true + ) + } + "내 리뷰 목록 가져오는 중".log() + } } } } } + + fun postLike(index: Int){ + _state.value.reviews[index].review.isLiked = !_state.value.reviews[index].review.isLiked + + postLikeUseCase(state.value.reviews[index].review.reviewId).onEach { + when (it) { + is Resource.Success -> Log.d("스크랩", "스크랩 성공") + is Resource.Error -> Log.d("스크랩", "스크랩 실패") + is Resource.Loading -> Log.d("스크랩", "스크랩 올리는중") + } + }.launchIn(viewModelScope) + } + fun postUnlike(index: Int){ + _state.value.reviews[index].review.isLiked = !_state.value.reviews[index].review.isLiked + + postUnlikeUseCase(state.value.reviews[index].review.reviewId).onEach { + Log.d("스크랩", "스크랩 2") + when (it) { + is Resource.Success -> Log.d("스크랩", "스크랩 취소 성공") + is Resource.Error -> Log.d("스크랩", "스크랩 취소 실패") + is Resource.Loading -> Log.d("스크랩", "스크랩 취소 올리는중") + } + }.launchIn(viewModelScope) + } } \ No newline at end of file diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Recruitings/RecruitingsScreen.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Recruitings/RecruitingsScreen.kt index 72c2825a..4abd5110 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Recruitings/RecruitingsScreen.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Recruitings/RecruitingsScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.* import androidx.compose.material.Scaffold import androidx.compose.runtime.* @@ -41,6 +42,10 @@ fun RecruitingsScreen( onRecruitingClick: (Int) -> Unit ) { val userId = rememberSaveable { mutableStateOf(0L) } + val pagingLoading by viewModel.pagingLoading.collectAsState() + val endReached by viewModel.endReached.collectAsState() + val recruitings by viewModel.recruitings.collectAsState() + //val recruitings by viewModel.state.collectAsState() BottomSheetUserFeedScreen( navController = navController, @@ -75,50 +80,55 @@ fun RecruitingsScreen( item { Spacer(modifier = Modifier.width(9.dp)) } - viewModel.state.value.recruitingDTOS.forEach { - item { - Log.d("testProgress","${it.post.orderedPrice.toFloat()/it.post.targetPrice.toFloat()}") - RecruitingItem( - onUserClick = { - userId.value = it.user.id - userFeedViewModel.getOtherUserInfo(userId.value) - userFeedViewModel.getOtherUserReviews(userId.value) - scope.launch { - state.animateTo( - ModalBottomSheetValue.Expanded, - tween(500) - ) - } - }, - onClick = { onRecruitingClick(it.post.id) }, - userName = it.user.nickname, - userLocation = LocationConverter( - LatLng( - it.post.latitude, - it.post.longitude + itemsIndexed(recruitings) { index, it -> + if (index >= recruitings.size - 1 && !endReached && !pagingLoading) { + viewModel.getRecruitings() + } + Log.d("testProgress","${it.post.orderedPrice.toFloat()/it.post.targetPrice.toFloat()}") + RecruitingItem( + onUserClick = { + userId.value = it.user.id + userFeedViewModel.getOtherUserInfo(userId.value) + userFeedViewModel.getOtherUserReviews(userId.value) + scope.launch { + state.animateTo( + ModalBottomSheetValue.Expanded, + tween(500) ) - ), - title = it.post.title, - imageURL = it.post.postImgUrl, - profileURL = it.user.profileImgUrl, - insufficientMoney = - if(it.post.targetPrice > it.post.orderedPrice) - it.post.targetPrice - it.post.orderedPrice - else 0, - timeRemaining = ((it.post.targetTime - System.currentTimeMillis() / 1000) / 60), - deadLineHour = it.post.targetTime.convertTimestampToHour(), - deadLineMinute = it.post.targetTime.convertTimestampToMinute(), - progress = it.post.orderedPrice.toFloat()/it.post.targetPrice.toFloat() + } + }, + onClick = { onRecruitingClick(it.post.id) }, + userName = it.user.nickname, + userLocation = LocationConverter( + LatLng( + it.post.latitude, + it.post.longitude + ) + ), + title = it.post.title, + imageURL = it.post.postImgUrl, + profileURL = it.user.profileImgUrl, + insufficientMoney = + if(it.post.targetPrice > it.post.orderedPrice) + it.post.targetPrice - it.post.orderedPrice + else 0, + timeRemaining = ((it.post.targetTime - System.currentTimeMillis() / 1000) / 60), + deadLineHour = it.post.targetTime.convertTimestampToHour(), + deadLineMinute = it.post.targetTime.convertTimestampToMinute(), + progress = it.post.orderedPrice.toFloat()/it.post.targetPrice.toFloat() + ) + } + + + if(recruitings.isEmpty()) { + item { + BlankIndicator( + modifier = Modifier + .align(CenterHorizontally) + .padding(vertical = 78.dp) ) } } - item { - BlankIndicator( - modifier = Modifier - .align(CenterHorizontally) - .padding(vertical = 78.dp) - ) - } } } GradientComponent(Modifier.align(Alignment.BottomCenter)) diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Recruitings/RecruitingsViewModel.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Recruitings/RecruitingsViewModel.kt index 00a276b6..e0ef3d4e 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Recruitings/RecruitingsViewModel.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Recruitings/RecruitingsViewModel.kt @@ -6,13 +6,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.d_vide.D_VIDE.app._enums.Category +import com.d_vide.D_VIDE.app.data.remote.responseDTO.RecruitingDTO +import com.d_vide.D_VIDE.app.data.remote.responseDTO.RecruitingsDTO import com.d_vide.D_VIDE.app.domain.use_case.GetRecruitings import com.d_vide.D_VIDE.app.domain.util.Resource import com.d_vide.D_VIDE.app.domain.util.log import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @@ -21,35 +21,55 @@ class RecruitingsViewModel @Inject constructor( private val getRecruitingsUseCase: GetRecruitings ) : ViewModel() { - private val _state = mutableStateOf(RecruitingsState()) - val state: State = _state + var state = MutableStateFlow(RecruitingsState()) + private set + + var recruitings = MutableStateFlow>(emptyList()) + private set + + var offset = MutableStateFlow(0) + private set + var endReached = MutableStateFlow(false) + private set + var pagingLoading = MutableStateFlow(false) + private set init { - getRecruitings(37.49015482509, 127.030767490, Category.ALL, 0) + getRecruitings(37.49015482509, 127.030767490, Category.ALL) } fun getRecruitings( latitude: Double = 37.49015482509, longitude: Double = 127.030767490, - category: Category = Category.ALL, - offset: Int = 0 + category: Category = Category.ALL ) { viewModelScope.launch { - getRecruitingsUseCase(latitude, longitude, category, offset).collect() { result -> + getRecruitingsUseCase(latitude, longitude, category, offset.value).collect() { result -> when (result) { is Resource.Success -> { - _state.value = - result.data?.let { RecruitingsState(recruitingDTOS = it.recruitingDTOS) }!! - result.data.recruitingDTOS.toString().log() + recruitings.value = recruitings.value + (result.data?.recruitingDTOS ?: emptyList()) + state.update { + it.copy( + recruitingDTOS = it.recruitingDTOS + result.data!!.recruitingDTOS + ) + } + + offset.value += result.data?.recruitingDTOS?.size ?: 0 + endReached.value = result.data?.recruitingDTOS?.size!! < 10 + pagingLoading.value = false + Log.d("가희", "호출 ${recruitings.value.size}") + } is Resource.Error -> { - _state.value = RecruitingsState( + state.value = RecruitingsState( error = result.message ?: "An unexpected error occured" ) + endReached.value = true Log.d("test", "error") } is Resource.Loading -> { - _state.value = RecruitingsState(isLoading = true) + state.value = RecruitingsState(isLoading = true) + pagingLoading.value = true Log.d("test", "loading") } } diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/Reviews.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/Reviews.kt index d061b015..9212f70d 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/Reviews.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/Reviews.kt @@ -10,6 +10,8 @@ import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Scaffold import androidx.compose.material.Surface import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment @@ -27,6 +29,7 @@ import com.d_vide.D_VIDE.app.presentation.view.component.RecruitingWriteButton import com.d_vide.D_VIDE.app.presentation.view.component.TopRoundBarWithImage import com.d_vide.D_VIDE.app.presentation.navigation.Screen import com.d_vide.D_VIDE.app.presentation.util.GradientComponent +import com.d_vide.D_VIDE.app.presentation.view.component.BlankIndicator import com.d_vide.D_VIDE.ui.theme.gray6 import kotlinx.coroutines.launch @@ -44,9 +47,11 @@ fun Reviews( ){ val userId = rememberSaveable{ mutableStateOf(0L) } val viewModel = hiltViewModel() - val userViewModel = hiltViewModel() - val reviews = viewModel.state.value.reviews + val viewModelState by viewModel.state.collectAsState() + val reviews = viewModelState.reviews val recommend = viewModel.state.value.recommendStore + val endReached = viewModelState.endReached + val pagingLoading = viewModelState.pagingLoading BottomSheetUserFeedScreen( navController = navController, @@ -71,6 +76,7 @@ fun Reviews( ) { Box() { LazyColumn( + modifier = Modifier.align(Alignment.Center), contentPadding = PaddingValues(top = 15.dp), verticalArrangement = Arrangement.spacedBy(15.dp), ) { @@ -78,6 +84,9 @@ fun Reviews( RecommendRow(onTagClick, recommend) } itemsIndexed(reviews){ index, item -> + if (index >= reviews.size - 1 && !endReached && !pagingLoading) { + viewModel.getReviews() + } ReviewItem( onUserClick = { userId.value = item.user.id @@ -97,7 +106,17 @@ fun Reviews( reviewImage = item.review.reviewImgUrl ) } - item { Spacer(modifier = Modifier.size(it.calculateBottomPadding())) } + + if(reviews.isEmpty()) { + item { + BlankIndicator( + modifier = Modifier + .align(Alignment.Center) + .padding(vertical = 78.dp) + ) + } + } + item { Spacer(Modifier.padding(bottom = Const.UIConst.HEIGHT_BOTTOM_BAR)) } } GradientComponent(Modifier.align(Alignment.BottomCenter)) } diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/ReviewsState.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/ReviewsState.kt index 73b8169f..f226a54b 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/ReviewsState.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/ReviewsState.kt @@ -7,5 +7,8 @@ data class ReviewsState( val isLoading: Boolean = false, var reviews: List = emptyList(), val error: String = "", - val recommendStore: List = emptyList() + val recommendStore: List = emptyList(), + val offset: Int = 1, + val endReached: Boolean = false, + val pagingLoading : Boolean = false, ) diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/ReviewsViewModel.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/ReviewsViewModel.kt index 7c9e8f35..a99ccef2 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/ReviewsViewModel.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/Reviews/ReviewsViewModel.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -27,29 +28,53 @@ class ReviewsViewModel @Inject constructor( val state = _state init{ - getReviews( latitude = 37.49015482509, longitude = 127.030767490, first = 1) + getReviews( latitude = 37.49015482509, longitude = 127.030767490) getRecommend() } - private fun getReviews(longitude: Double, latitude: Double, first: Int){ - getReviewsUseCase(longitude, latitude, first).onEach { result -> - when (result) { - is Resource.Success -> { - _state.update { - it.copy(reviews = result.data?.reviews ?: emptyList(), isLoading = false) + fun getReviews( + longitude: Double = 127.030767490, + latitude: Double = 37.49015482509 + ){ + viewModelScope.launch { + getReviewsUseCase(longitude, latitude, _state.value.offset).collect() { result -> + when (result) { + is Resource.Success -> { + _state.update { + it.copy( + reviews = it.reviews + (result.data?.reviews ?: emptyList()), + isLoading = false, + offset = it.offset + (result.data?.reviews?.size ?: 0), + pagingLoading = false, + endReached = false + ) + } + Log.d("가희", "리뷰 호출 ${result.data?.reviews ?: emptyList()}") + } + is Resource.Error -> { + _state.update { + it.copy( + error = result.message ?: "An unexpected error occured", + isLoading = false, + pagingLoading = false, + endReached = result.data?.reviews?.size!! < 10 + ) + } + Log.d("test", "error") + } + is Resource.Loading -> { + _state.update { + it.copy( + isLoading = true, + pagingLoading = true + ) + } + Log.d("test", "loading") } } - is Resource.Error -> { - _state.value = ReviewsState(error = result.message ?: "An unexpected error occured") - Log.d("test", "error") - } - is Resource.Loading -> { - _state.value = ReviewsState(isLoading = true) - Log.d("test", "loading") - } - } - }.launchIn(viewModelScope) + } + } } private fun getRecommend(){ diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsScreen.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsScreen.kt index 38316fa3..cf12165f 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsScreen.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsScreen.kt @@ -6,9 +6,12 @@ import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment @@ -18,12 +21,14 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController +import com.d_vide.D_VIDE.app._constants.Const import com.d_vide.D_VIDE.app.presentation.view.TaggedReviews.component.ReviewItem import com.d_vide.D_VIDE.app.presentation.view.UserFeed.BottomSheetUserFeedScreen import com.d_vide.D_VIDE.app.presentation.view.UserFeed.UserProfileViewModel import com.d_vide.D_VIDE.app.presentation.view.component.RecruitingWriteButton import com.d_vide.D_VIDE.app.presentation.navigation.Screen import com.d_vide.D_VIDE.app.presentation.util.GradientComponent +import com.d_vide.D_VIDE.app.presentation.view.component.BlankIndicator import com.d_vide.D_VIDE.ui.theme.* import kotlinx.coroutines.launch @@ -38,8 +43,13 @@ fun TaggedReviewsScreen( val userViewModel = hiltViewModel() val viewModel = hiltViewModel() - val reviews = viewModel.state.value.reviews + val viewModelState by viewModel.state.collectAsState() val userId = rememberSaveable{ mutableStateOf(0L) } + val reviews = viewModelState.reviews + val endReached = viewModelState.endReached + val pagingLoading = viewModelState.pagingLoading + + BottomSheetUserFeedScreen( navController = navController, onReviewSelected = onReviewSelected, @@ -57,6 +67,7 @@ fun TaggedReviewsScreen( Box() { Column( modifier = Modifier + .align(Alignment.Center) .fillMaxSize() .background(background) ) { @@ -65,7 +76,10 @@ fun TaggedReviewsScreen( contentPadding = PaddingValues(vertical = 28.dp), verticalArrangement = Arrangement.spacedBy(15.dp) ) { - items(reviews){ review -> + itemsIndexed(reviews){ index, review -> + if (index >= reviews.size - 1 && !endReached && !pagingLoading) { + viewModel.getReviews() + } ReviewItem( onUserClick = { userId.value = 1 // need to change @@ -85,7 +99,16 @@ fun TaggedReviewsScreen( starRating = review.review.starRating ) } - + if(reviews.isEmpty()) { + item { + BlankIndicator( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(vertical = 78.dp) + ) + } + } + item { Spacer(Modifier.padding(bottom = Const.UIConst.HEIGHT_BOTTOM_BAR)) } } } diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsState.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsState.kt index c91d0b04..cc5bf6a0 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsState.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsState.kt @@ -5,5 +5,8 @@ import com.d_vide.D_VIDE.app.data.remote.responseDTO.Review.ReviewDTO data class TaggedReviewsState( val isLoading: Boolean = false, val reviews: List = emptyList(), - val error: String = "" + val error: String = "", + val offset: Int = 0, + val endReached: Boolean = false, + val pagingLoading : Boolean = false, ) diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsViewModel.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsViewModel.kt index 53011a1a..71472480 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsViewModel.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/TaggedReviews/TaggedReviewsViewModel.kt @@ -8,10 +8,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.d_vide.D_VIDE.app.domain.use_case.Review.GetStoreReview import com.d_vide.D_VIDE.app.domain.util.Resource +import com.d_vide.D_VIDE.app.domain.util.log import com.d_vide.D_VIDE.app.presentation.navigation.DetailDestinationKey import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -22,29 +23,53 @@ class TaggedReviewsViewModel @Inject constructor( val storeName = savedStateHandle.get(DetailDestinationKey.TAGGEDREVIEW)!! - private val _state = mutableStateOf(TaggedReviewsState()) - val state: State = _state + private val _state = MutableStateFlow(TaggedReviewsState()) + val state = _state init{ - getReviews(0, storeName) + getReviews() } - private fun getReviews(first: Int = 0, storeName: String){ - getStoreReviewUseCase(first, storeName).onEach { result -> - when (result) { - is Resource.Success -> { - _state.value = result.data?.let { TaggedReviewsState(reviews = result.data.reviews) }!! - } - is Resource.Error -> { - _state.value = TaggedReviewsState(error = result.message ?: "An unexpected error occured") - Log.d("test", "error") - } - is Resource.Loading -> { - _state.value = TaggedReviewsState(isLoading = true) - Log.d("test", "loading") + fun getReviews(){ + viewModelScope.launch { + getStoreReviewUseCase(_state.value.offset, storeName).collect() { result -> + when (result) { + is Resource.Success -> { + _state.update { + it.copy( + reviews = it.reviews + (result.data?.reviews ?: emptyList()), + isLoading = false, + offset = it.offset + (result.data?.reviews?.size ?: 0), + pagingLoading = false, + endReached = result.data?.reviews?.size!! < 10 + ) + } + Log.d("가희", "식당 리뷰 :${_state.value.offset}") + } + is Resource.Error -> { + _state.update { + it.copy( + error = result.message ?: "An unexpected error occured in my review", + isLoading = false, + pagingLoading = false, + endReached = true + ) + } + + "식당 리뷰 가져오기 실패".log() + } + is Resource.Loading -> { + _state.update { + it.copy( + isLoading = true, + pagingLoading = true + ) + } + "식당 리뷰 목록 가져오는 중".log() + } } - } - }.launchIn(viewModelScope) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/component/BlankIndicator.kt b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/component/BlankIndicator.kt index 09a2a7b8..57cd7c18 100644 --- a/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/component/BlankIndicator.kt +++ b/app/src/main/java/com/d_vide/D_VIDE/app/presentation/view/component/BlankIndicator.kt @@ -18,7 +18,8 @@ import com.d_vide.D_VIDE.ui.theme.gray1 @Composable fun BlankIndicator( - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + text: String = "아직 새로운 글이 \n 업로드 되지 않았어요" ){ Box( modifier = modifier @@ -33,7 +34,7 @@ fun BlankIndicator( modifier = Modifier.size(82.9.dp, 82.33.dp) ) Text( - text = "아직 새로운 모집글이\n업로드 되지 않았어요", + text = text, modifier = Modifier.padding(top = 11.67.dp), textAlign = TextAlign.Center, color = gray1,