diff --git a/app/src/main/java/info/imdang/imdang/common/SpaceItemDecoration.kt b/app/src/main/java/info/imdang/imdang/common/SpaceItemDecoration.kt index 45afa63..8a29216 100644 --- a/app/src/main/java/info/imdang/imdang/common/SpaceItemDecoration.kt +++ b/app/src/main/java/info/imdang/imdang/common/SpaceItemDecoration.kt @@ -15,24 +15,21 @@ class SpaceItemDecoration(private var space: Int) : RecyclerView.ItemDecoration( state: RecyclerView.State ) { val position = parent.getChildAdapterPosition(view) - val isLast = position == parent.adapter?.itemCount?.minus(1) + + if (position == RecyclerView.NO_POSITION) return + when (val layoutManager = parent.layoutManager) { is LinearLayoutManager -> { when (layoutManager.orientation) { RecyclerView.HORIZONTAL -> { - outRect.right += getSpaceByLocation(isLast, view.context.dpToPx(space)) + outRect.right += view.context.dpToPx(space) } RecyclerView.VERTICAL -> { - outRect.bottom += getSpaceByLocation(isLast, view.context.dpToPx(space)) + outRect.bottom += view.context.dpToPx(space) } } } } } - - private fun getSpaceByLocation( - isLast: Boolean, - space: Int - ): Int = if (isLast) 0 else space } diff --git a/app/src/main/java/info/imdang/imdang/common/bindingadapter/ChipBindingAdapter.kt b/app/src/main/java/info/imdang/imdang/common/bindingadapter/ChipBindingAdapter.kt index fcaa1e2..cd08ec1 100644 --- a/app/src/main/java/info/imdang/imdang/common/bindingadapter/ChipBindingAdapter.kt +++ b/app/src/main/java/info/imdang/imdang/common/bindingadapter/ChipBindingAdapter.kt @@ -73,11 +73,9 @@ fun Chip.bindChipBackground(isSelected: Boolean) { ) fun Chip.bindChipStatus( status: ExchangeRequestStatus, - count: Map?, + count: Int, isSelected: Boolean ) { - val chipCount = count?.get(status) ?: 0 - val statusText = when (status) { ExchangeRequestStatus.PENDING -> { context.getString(info.imdang.component.R.string.waiting) @@ -93,7 +91,7 @@ fun Chip.bindChipStatus( } val text = if (isSelected) { - "$statusText ($chipCount)" + "$statusText ($count)" } else { statusText } diff --git a/app/src/main/java/info/imdang/imdang/common/bindingadapter/TextViewBindingAdapter.kt b/app/src/main/java/info/imdang/imdang/common/bindingadapter/TextViewBindingAdapter.kt index b3af7ea..46e4095 100644 --- a/app/src/main/java/info/imdang/imdang/common/bindingadapter/TextViewBindingAdapter.kt +++ b/app/src/main/java/info/imdang/imdang/common/bindingadapter/TextViewBindingAdapter.kt @@ -30,7 +30,7 @@ fun TextView.bindSpan(text: String, spanText: String) { value = ["bindChipSelectedId", "bindChipSelectedCounts"], requireAll = true ) -fun TextView.bindChipAlarmDescription(chipId: Int, chipCounts: Map?) { +fun TextView.bindChipAlarmDescription(chipId: Int, count: Int) { val status = when (chipId) { 1 -> ExchangeRequestStatus.PENDING 2 -> ExchangeRequestStatus.REJECTED @@ -38,8 +38,6 @@ fun TextView.bindChipAlarmDescription(chipId: Int, chipCounts: Map null } - val count = status?.let { chipCounts?.get(it) } ?: 0 - val description = when (status) { ExchangeRequestStatus.PENDING -> if (count > 0) { context.getString(info.imdang.component.R.string.waiting_details_existence) diff --git a/app/src/main/java/info/imdang/imdang/ui/main/home/exchange/HomeExchangeEvent.kt b/app/src/main/java/info/imdang/imdang/ui/main/home/exchange/HomeExchangeEvent.kt new file mode 100644 index 0000000..b75614d --- /dev/null +++ b/app/src/main/java/info/imdang/imdang/ui/main/home/exchange/HomeExchangeEvent.kt @@ -0,0 +1,11 @@ +package info.imdang.imdang.ui.main.home.exchange + +import androidx.paging.PagingData +import info.imdang.imdang.model.insight.InsightVo + +sealed class HomeExchangeEvent { + + data class UpdateMyExchanges(val exchanges: PagingData) : HomeExchangeEvent() + + data class UpdateOthersExchanges(val exchanges: PagingData) : HomeExchangeEvent() +} diff --git a/app/src/main/java/info/imdang/imdang/ui/main/home/exchange/HomeExchangeViewModel.kt b/app/src/main/java/info/imdang/imdang/ui/main/home/exchange/HomeExchangeViewModel.kt index 8aef7ff..d8c2a6e 100644 --- a/app/src/main/java/info/imdang/imdang/ui/main/home/exchange/HomeExchangeViewModel.kt +++ b/app/src/main/java/info/imdang/imdang/ui/main/home/exchange/HomeExchangeViewModel.kt @@ -1,6 +1,8 @@ package info.imdang.imdang.ui.main.home.exchange import androidx.lifecycle.viewModelScope +import androidx.paging.cachedIn +import androidx.paging.map import dagger.hilt.android.lifecycle.HiltViewModel import info.imdang.domain.model.common.MyExchangesParams import info.imdang.domain.model.common.PagingParams @@ -10,13 +12,15 @@ import info.imdang.domain.usecase.myexchange.GetMyExchangeUseCase import info.imdang.domain.usecase.myexchange.GetOthersExchangeUseCase import info.imdang.imdang.base.BaseViewModel import info.imdang.imdang.common.util.logEvent +import info.imdang.imdang.model.common.PagingState import info.imdang.imdang.model.coupon.CouponVo import info.imdang.imdang.model.coupon.mapper import info.imdang.imdang.model.insight.ExchangeRequestStatus -import info.imdang.imdang.model.insight.InsightVo import info.imdang.imdang.model.insight.mapper import info.imdang.imdang.ui.main.home.history.ExchangeType +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -28,27 +32,21 @@ class HomeExchangeViewModel @Inject constructor( private val getCouponCountUseCase: GetCouponUseCase ) : BaseViewModel() { + private val _event = MutableSharedFlow() + val event = _event.asSharedFlow() + private val _currentExchangeType = MutableStateFlow(ExchangeType.REQUESTED) val currentExchangeType = _currentExchangeType.asStateFlow() private val _selectedChipId = MutableStateFlow(1) val selectedChipId = _selectedChipId.asStateFlow() - private val _mySelectedChipCounts = MutableStateFlow(mapOf()) - val mySelectedChipCounts = _mySelectedChipCounts.asStateFlow() - - private val _othersSelectedChipCounts = MutableStateFlow(mapOf()) - val othersSelectedChipCounts = _othersSelectedChipCounts.asStateFlow() - - private val _myExchanges = MutableStateFlow>(emptyList()) - val myExchanges = _myExchanges.asStateFlow() - - private val _othersExchanges = MutableStateFlow>(emptyList()) - val othersExchanges = _othersExchanges.asStateFlow() - private val _coupon = MutableStateFlow(CouponVo.init()) val coupon = _coupon.asStateFlow() + private val _pagingState = MutableStateFlow(PagingState()) + val pagingState = _pagingState.asStateFlow() + init { fetchMyExchange(ExchangeRequestStatus.PENDING) fetchOthersExchange(ExchangeRequestStatus.PENDING) @@ -96,37 +94,39 @@ class HomeExchangeViewModel @Inject constructor( private fun fetchMyExchange(exchangeRequestStatus: ExchangeRequestStatus) { viewModelScope.launch { - val response = getMyExchangeUseCase( + getMyExchangeUseCase( MyExchangesParams( exchangeRequestStatus = exchangeRequestStatus.name, - pagingParams = PagingParams() + pagingParams = PagingParams( + totalCountListener = { + updatePagingState(itemCount = it) + } + ) ) ) - - val totalCount = response?.totalElements ?: 0 - _mySelectedChipCounts.value = _mySelectedChipCounts.value.toMutableMap().apply { - this[exchangeRequestStatus] = totalCount - } - - _myExchanges.value = response?.content?.map(InsightDto::mapper) ?: emptyList() + ?.cachedIn(this) + ?.collect { + _event.emit(HomeExchangeEvent.UpdateMyExchanges(it.map(InsightDto::mapper))) + } } } private fun fetchOthersExchange(exchangeRequestStatus: ExchangeRequestStatus) { viewModelScope.launch { - val response = getOthersExchangeUseCase( + getOthersExchangeUseCase( MyExchangesParams( exchangeRequestStatus = exchangeRequestStatus.name, - pagingParams = PagingParams() + pagingParams = PagingParams( + totalCountListener = { + updatePagingState(itemCount = it) + } + ) ) ) - - val totalCount = response?.totalElements ?: 0 - _othersSelectedChipCounts.value = _othersSelectedChipCounts.value.toMutableMap().apply { - this[exchangeRequestStatus] = totalCount - } - - _othersExchanges.value = response?.content?.map(InsightDto::mapper) ?: emptyList() + ?.cachedIn(this) + ?.collect { + _event.emit(HomeExchangeEvent.UpdateOthersExchanges(it.map(InsightDto::mapper))) + } } } @@ -137,4 +137,16 @@ class HomeExchangeViewModel @Inject constructor( } } } + + fun updatePagingState( + isLoading: Boolean? = null, + itemCount: Int? = null, + error: String? = null + ) { + _pagingState.value = pagingState.value.copy( + isLoading = isLoading ?: pagingState.value.isLoading, + itemCount = itemCount ?: pagingState.value.itemCount, + error = error ?: pagingState.value.error + ) + } } diff --git a/app/src/main/java/info/imdang/imdang/ui/main/home/history/HomeHistoryRequestedFragment.kt b/app/src/main/java/info/imdang/imdang/ui/main/home/history/HomeHistoryRequestedFragment.kt index 19dd492..e94b33e 100644 --- a/app/src/main/java/info/imdang/imdang/ui/main/home/history/HomeHistoryRequestedFragment.kt +++ b/app/src/main/java/info/imdang/imdang/ui/main/home/history/HomeHistoryRequestedFragment.kt @@ -11,13 +11,14 @@ import dagger.hilt.android.AndroidEntryPoint import info.imdang.imdang.R import info.imdang.imdang.base.BaseFragment import info.imdang.imdang.common.SpaceItemDecoration -import info.imdang.imdang.common.bindingadapter.BaseSingleViewAdapter +import info.imdang.imdang.common.bindingadapter.BaseSingleViewPagingAdapter import info.imdang.imdang.common.ext.startActivity import info.imdang.imdang.common.util.logEvent import info.imdang.imdang.databinding.FragmentHomeHistoryRequestedBinding import info.imdang.imdang.model.insight.InsightVo import info.imdang.imdang.ui.insight.InsightDetailActivity import info.imdang.imdang.ui.insight.InsightDetailActivity.Companion.INSIGHT_ID +import info.imdang.imdang.ui.main.home.exchange.HomeExchangeEvent import info.imdang.imdang.ui.main.home.exchange.HomeExchangeViewModel import kotlinx.coroutines.launch @@ -29,11 +30,14 @@ class HomeHistoryRequestedFragment : private var type: ExchangeType = ExchangeType.REQUESTED + private lateinit var adapter: BaseSingleViewPagingAdapter + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.updateExchangeType(type) setupBinding() + setupCollect() } private fun setupBinding() { @@ -42,7 +46,7 @@ class HomeHistoryRequestedFragment : rvHomeHistoryRequested.run { addItemDecoration(SpaceItemDecoration(space = 12)) - adapter = BaseSingleViewAdapter( + this@HomeHistoryRequestedFragment.adapter = BaseSingleViewPagingAdapter( layoutResourceId = R.layout.item_insight_horizontal, bindingItemId = BR.item, viewModel = mapOf(BR.viewModel to this@HomeHistoryRequestedFragment.viewModel), @@ -81,21 +85,61 @@ class HomeHistoryRequestedFragment : ) } } - } + when (type) { + ExchangeType.REQUESTED -> { + setupLoadStateListener( + scope = lifecycleScope, + onLoading = { + this@HomeHistoryRequestedFragment.viewModel.updatePagingState( + isLoading = it + ) + }, + onError = { + this@HomeHistoryRequestedFragment.viewModel.updatePagingState( + error = it + ) + } + ) + } - val dataObserve = when (type) { - ExchangeType.REQUESTED -> { - this@HomeHistoryRequestedFragment.viewModel.myExchanges + ExchangeType.RECEIVED -> { + setupLoadStateListener( + scope = lifecycleScope, + onLoading = { + this@HomeHistoryRequestedFragment.viewModel.updatePagingState( + isLoading = it + ) + }, + onError = { + this@HomeHistoryRequestedFragment.viewModel.updatePagingState( + error = it + ) + } + ) + } } + } + adapter = this@HomeHistoryRequestedFragment.adapter + } + } + } - ExchangeType.RECEIVED -> { - this@HomeHistoryRequestedFragment.viewModel.othersExchanges + private fun setupCollect() { + lifecycleScope.launch { + when (type) { + ExchangeType.REQUESTED -> { + viewModel.event.collect { event -> + if (event is HomeExchangeEvent.UpdateMyExchanges) { + adapter.submitData(lifecycle, event.exchanges) + } } } - lifecycleScope.launch { - dataObserve.collect { items -> - (adapter as BaseSingleViewAdapter).submitList(items) + ExchangeType.RECEIVED -> { + viewModel.event.collect { event -> + if (event is HomeExchangeEvent.UpdateOthersExchanges) { + adapter.submitData(lifecycle, event.exchanges) + } } } } diff --git a/app/src/main/res/layout/fragment_home_history_requested.xml b/app/src/main/res/layout/fragment_home_history_requested.xml index 9e1e73f..539a6b7 100644 --- a/app/src/main/res/layout/fragment_home_history_requested.xml +++ b/app/src/main/res/layout/fragment_home_history_requested.xml @@ -38,7 +38,7 @@ ? - ): PagingDto = myExchangeRemoteDataSource.getRequestedMyExchanges( - requestMemberId = requestMemberId, - exchangeRequestStatus = exchangeRequestStatus, - page = page, - size = size, - direction = direction, - properties = properties - ).mapper() + properties: List?, + totalCountListener: ((Int) -> Unit)? + ): Flow> = getPagingFlow( + initialPage = page ?: 0, + pageSize = size ?: 20, + loadData = { currentPage, pageSize -> + myExchangeRemoteDataSource.getRequestedMyExchanges( + requestMemberId = requestMemberId, + exchangeRequestStatus = exchangeRequestStatus, + page = currentPage, + size = pageSize, + direction = direction, + properties = properties + ).mapper() + }, + totalCountListener = totalCountListener + ) override suspend fun getRequestedOthersExchanges( requestedMemberId: String, @@ -32,13 +42,21 @@ internal class MyExchangeRepositoryImpl @Inject constructor( page: Int?, size: Int?, direction: String?, - properties: List? - ): PagingDto = myExchangeRemoteDataSource.getRequestedOthersExchanges( - requestedMemberId = requestedMemberId, - exchangeRequestStatus = exchangeRequestStatus, - page = page, - size = size, - direction = direction, - properties = properties - ).mapper() + properties: List?, + totalCountListener: ((Int) -> Unit)? + ): Flow> = getPagingFlow( + initialPage = page ?: 0, + pageSize = size ?: 2, + loadData = { currentPage, pageSize -> + myExchangeRemoteDataSource.getRequestedOthersExchanges( + requestedMemberId = requestedMemberId, + exchangeRequestStatus = exchangeRequestStatus, + page = currentPage, + size = pageSize, + direction = direction, + properties = properties + ).mapper() + }, + totalCountListener = totalCountListener + ) } diff --git a/domain/src/main/java/info/imdang/domain/repository/MyExchangeRepository.kt b/domain/src/main/java/info/imdang/domain/repository/MyExchangeRepository.kt index 236d25c..8bcb024 100644 --- a/domain/src/main/java/info/imdang/domain/repository/MyExchangeRepository.kt +++ b/domain/src/main/java/info/imdang/domain/repository/MyExchangeRepository.kt @@ -1,7 +1,8 @@ package info.imdang.domain.repository -import info.imdang.domain.model.common.PagingDto +import androidx.paging.PagingData import info.imdang.domain.model.insight.InsightDto +import kotlinx.coroutines.flow.Flow interface MyExchangeRepository { @@ -11,8 +12,9 @@ interface MyExchangeRepository { page: Int?, size: Int?, direction: String?, - properties: List? - ): PagingDto + properties: List?, + totalCountListener: ((Int) -> Unit)? + ): Flow> suspend fun getRequestedOthersExchanges( requestedMemberId: String, @@ -20,6 +22,7 @@ interface MyExchangeRepository { page: Int?, size: Int?, direction: String?, - properties: List? - ): PagingDto + properties: List?, + totalCountListener: ((Int) -> Unit)? + ): Flow> } diff --git a/domain/src/main/java/info/imdang/domain/usecase/myexchange/GetMyExchangeUseCase.kt b/domain/src/main/java/info/imdang/domain/usecase/myexchange/GetMyExchangeUseCase.kt index 37b7d2e..cf62200 100644 --- a/domain/src/main/java/info/imdang/domain/usecase/myexchange/GetMyExchangeUseCase.kt +++ b/domain/src/main/java/info/imdang/domain/usecase/myexchange/GetMyExchangeUseCase.kt @@ -1,31 +1,33 @@ package info.imdang.domain.usecase.myexchange +import androidx.paging.PagingData import info.imdang.domain.IoDispatcher import info.imdang.domain.model.common.MyExchangesParams -import info.imdang.domain.model.common.PagingDto import info.imdang.domain.model.insight.InsightDto import info.imdang.domain.repository.MyExchangeRepository import info.imdang.domain.usecase.UseCase import info.imdang.domain.usecase.auth.GetMemberIdUseCase import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.Flow import javax.inject.Inject class GetMyExchangeUseCase @Inject constructor( private val myExchangeRepository: MyExchangeRepository, private val getMemberIdUseCase: GetMemberIdUseCase, @IoDispatcher dispatcher: CoroutineDispatcher -) : UseCase>( +) : UseCase>>( coroutineDispatcher = dispatcher ) { override suspend fun execute( parameters: MyExchangesParams - ): PagingDto = myExchangeRepository.getRequestedMyExchanges( + ): Flow> = myExchangeRepository.getRequestedMyExchanges( requestMemberId = getMemberIdUseCase(), exchangeRequestStatus = parameters.exchangeRequestStatus, page = parameters.pagingParams.page - 1, size = parameters.pagingParams.size, direction = parameters.pagingParams.direction, - properties = parameters.pagingParams.properties + properties = parameters.pagingParams.properties, + totalCountListener = parameters.pagingParams.totalCountListener ) } diff --git a/domain/src/main/java/info/imdang/domain/usecase/myexchange/GetOthersExchangeUseCase.kt b/domain/src/main/java/info/imdang/domain/usecase/myexchange/GetOthersExchangeUseCase.kt index 003e323..38d37e8 100644 --- a/domain/src/main/java/info/imdang/domain/usecase/myexchange/GetOthersExchangeUseCase.kt +++ b/domain/src/main/java/info/imdang/domain/usecase/myexchange/GetOthersExchangeUseCase.kt @@ -1,31 +1,33 @@ package info.imdang.domain.usecase.myexchange +import androidx.paging.PagingData import info.imdang.domain.IoDispatcher import info.imdang.domain.model.common.MyExchangesParams -import info.imdang.domain.model.common.PagingDto import info.imdang.domain.model.insight.InsightDto import info.imdang.domain.repository.MyExchangeRepository import info.imdang.domain.usecase.UseCase import info.imdang.domain.usecase.auth.GetMemberIdUseCase import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.Flow import javax.inject.Inject class GetOthersExchangeUseCase @Inject constructor( private val myExchangeRepository: MyExchangeRepository, private val getMemberIdUseCase: GetMemberIdUseCase, @IoDispatcher dispatcher: CoroutineDispatcher -) : UseCase>( +) : UseCase>>( coroutineDispatcher = dispatcher ) { override suspend fun execute( parameters: MyExchangesParams - ): PagingDto = myExchangeRepository.getRequestedOthersExchanges( + ): Flow> = myExchangeRepository.getRequestedOthersExchanges( requestedMemberId = getMemberIdUseCase(), exchangeRequestStatus = parameters.exchangeRequestStatus, page = parameters.pagingParams.page - 1, size = parameters.pagingParams.size, direction = parameters.pagingParams.direction, - properties = parameters.pagingParams.properties + properties = parameters.pagingParams.properties, + totalCountListener = parameters.pagingParams.totalCountListener ) }