Fake loading more process

This commit is contained in:
Maxime NATUREL 2023-01-11 15:37:31 +01:00
parent e8e94b5189
commit dba9e29881
11 changed files with 102 additions and 27 deletions

View file

@ -18,4 +18,6 @@ package im.vector.app.features.roomprofile.polls
import im.vector.app.core.platform.VectorViewModelAction
sealed interface RoomPollsAction : VectorViewModelAction
sealed interface RoomPollsAction : VectorViewModelAction {
object LoadMorePolls : RoomPollsAction
}

View file

@ -23,12 +23,16 @@ import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.roomprofile.polls.list.domain.GetPollsUseCase
import im.vector.app.features.roomprofile.polls.list.domain.LoadMorePollsUseCase
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
class RoomPollsViewModel @AssistedInject constructor(
@Assisted initialState: RoomPollsViewState,
private val getPollsUseCase: GetPollsUseCase,
private val loadMorePollsUseCase: LoadMorePollsUseCase,
) : VectorViewModel<RoomPollsViewState, RoomPollsAction, RoomPollsViewEvent>(initialState) {
@AssistedFactory
@ -42,13 +46,24 @@ class RoomPollsViewModel @AssistedInject constructor(
observePolls()
}
private fun observePolls() {
getPollsUseCase.execute()
private fun observePolls() = withState { viewState ->
getPollsUseCase.execute(viewState.roomId)
.onEach { setState { copy(polls = it) } }
.launchIn(viewModelScope)
}
// TODO add unit tests
override fun handle(action: RoomPollsAction) {
// do nothing for now
when (action) {
RoomPollsAction.LoadMorePolls -> handleLoadMore()
}
}
private fun handleLoadMore() = withState { viewState ->
viewModelScope.launch {
setState { copy(isLoadingMore = true) }
loadMorePollsUseCase.execute(viewState.roomId)
setState { copy(isLoadingMore = false) }
}
}
}

View file

@ -18,10 +18,14 @@ package im.vector.app.features.roomprofile.polls
import com.airbnb.mvrx.MavericksState
import im.vector.app.features.roomprofile.RoomProfileArgs
import im.vector.app.features.roomprofile.polls.list.PollSummary
// TODO parameter to know whether load more is possible
// TODO parameter to know whether initial loading is in progress
data class RoomPollsViewState(
val roomId: String,
val polls: List<PollSummary> = emptyList(),
val isLoadingMore: Boolean = false,
) : MavericksState {
constructor(roomProfileArgs: RoomProfileArgs) : this(roomId = roomProfileArgs.roomId)

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package im.vector.app.features.roomprofile.polls
package im.vector.app.features.roomprofile.polls.list
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState

View file

@ -17,6 +17,8 @@
package im.vector.app.features.roomprofile.polls.list
import android.widget.Button
import android.widget.ProgressBar
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.app.R
@ -28,15 +30,21 @@ import im.vector.app.core.epoxy.onClick
@EpoxyModelClass
abstract class RoomPollLoadMoreItem : VectorEpoxyModel<RoomPollLoadMoreItem.Holder>(R.layout.item_poll_load_more) {
@EpoxyAttribute
var loadingMore: Boolean = false
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var clickListener: ClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.loadMoreButton.isEnabled = loadingMore.not()
holder.loadMoreButton.onClick(clickListener)
holder.loadMoreProgressBar.isVisible = loadingMore
}
class Holder : VectorEpoxyHolder() {
val loadMoreButton by bind<Button>(R.id.roomPollsLoadMore)
val loadMoreProgressBar by bind<ProgressBar>(R.id.roomPollsLoadMoreProgress)
}
}

View file

@ -21,13 +21,14 @@ import im.vector.app.R
import im.vector.app.core.date.DateFormatKind
import im.vector.app.core.date.VectorDateFormatter
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.roomprofile.polls.PollSummary
import im.vector.app.features.roomprofile.polls.RoomPollsViewState
import java.util.UUID
import javax.inject.Inject
class RoomPollsController @Inject constructor(
val dateFormatter: VectorDateFormatter,
val stringProvider: StringProvider,
) : TypedEpoxyController<List<PollSummary>>() {
) : TypedEpoxyController<RoomPollsViewState>() {
interface Listener {
fun onPollClicked(pollId: String)
@ -36,19 +37,20 @@ class RoomPollsController @Inject constructor(
var listener: Listener? = null
override fun buildModels(data: List<PollSummary>?) {
if (data.isNullOrEmpty()) {
override fun buildModels(viewState: RoomPollsViewState?) {
val polls = viewState?.polls
if (polls.isNullOrEmpty()) {
return
}
for (poll in data) {
for (poll in polls) {
when (poll) {
is PollSummary.ActivePoll -> buildActivePollItem(poll)
is PollSummary.EndedPoll -> buildEndedPollItem(poll)
}
}
buildLoadMoreItem()
buildLoadMoreItem(viewState.isLoadingMore)
}
private fun buildActivePollItem(poll: PollSummary.ActivePoll) {
@ -77,11 +79,14 @@ class RoomPollsController @Inject constructor(
}
}
private fun buildLoadMoreItem() {
private fun buildLoadMoreItem(isLoadingMore: Boolean) {
val host = this
roomPollLoadMoreItem {
id("roomPollLoadMore")
host.listener?.onLoadMoreClicked()
id(UUID.randomUUID().toString())
loadingMore(isLoadingMore)
clickListener {
host.listener?.onLoadMoreClicked()
}
}
}
}

View file

@ -27,9 +27,10 @@ import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentRoomPollsListBinding
import im.vector.app.features.roomprofile.polls.PollSummary
import im.vector.app.features.roomprofile.polls.RoomPollsAction
import im.vector.app.features.roomprofile.polls.RoomPollsType
import im.vector.app.features.roomprofile.polls.RoomPollsViewModel
import im.vector.app.features.roomprofile.polls.RoomPollsViewState
import timber.log.Timber
import javax.inject.Inject
@ -74,15 +75,16 @@ abstract class RoomPollsListFragment :
}
override fun invalidate() = withState(viewModel) { viewState ->
when (getRoomPollsType()) {
RoomPollsType.ACTIVE -> renderList(viewState.polls.filterIsInstance(PollSummary.ActivePoll::class.java))
RoomPollsType.ENDED -> renderList(viewState.polls.filterIsInstance(PollSummary.EndedPoll::class.java))
val filteredPolls = when (getRoomPollsType()) {
RoomPollsType.ACTIVE -> viewState.polls.filterIsInstance(PollSummary.ActivePoll::class.java)
RoomPollsType.ENDED -> viewState.polls.filterIsInstance(PollSummary.EndedPoll::class.java)
}
renderList(viewState.copy(polls = filteredPolls))
}
private fun renderList(polls: List<PollSummary>) {
roomPollsController.setData(polls)
views.roomPollsEmptyTitle.isVisible = polls.isEmpty()
private fun renderList(viewState: RoomPollsViewState) {
roomPollsController.setData(viewState)
views.roomPollsEmptyTitle.isVisible = viewState.polls.isEmpty()
}
override fun onPollClicked(pollId: String) {
@ -91,6 +93,6 @@ abstract class RoomPollsListFragment :
}
override fun onLoadMoreClicked() {
// TODO call viewAction
viewModel.handle(RoomPollsAction.LoadMorePolls)
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 New Vector Ltd
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,18 +14,23 @@
* limitations under the License.
*/
package im.vector.app.features.roomprofile.polls
package im.vector.app.features.roomprofile.polls.list.domain
import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState
import im.vector.app.features.roomprofile.polls.list.PollSummary
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import timber.log.Timber
import javax.inject.Inject
// TODO add unit tests
class GetPollsUseCase @Inject constructor() {
fun execute(): Flow<List<PollSummary>> {
// TODO unmock and add unit tests
// TODO create a repo + datasources (local + remote) with mocked data by passing a room Id
fun execute(roomId: String): Flow<List<PollSummary>> {
// TODO call repository to get flow on polls
Timber.d("roomId=$roomId")
return flowOf(getActivePolls() + getEndedPolls())
.map { it.sortedByDescending { poll -> poll.creationTimestamp } }
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.roomprofile.polls.list.domain
import kotlinx.coroutines.delay
import timber.log.Timber
import javax.inject.Inject
// TODO add unit tests
class LoadMorePollsUseCase @Inject constructor() {
suspend fun execute(roomId: String) {
// TODO call repository to load more polls to be published in a flow
Timber.d("roomId=$roomId")
delay(5000)
}
}

View file

@ -13,6 +13,7 @@
android:layout_marginBottom="46dp"
android:padding="0dp"
android:text="@string/room_polls_load_more"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View file

@ -17,6 +17,8 @@
package im.vector.app.features.roomprofile.polls
import com.airbnb.mvrx.test.MavericksTestRule
import im.vector.app.features.roomprofile.polls.list.PollSummary
import im.vector.app.features.roomprofile.polls.list.domain.GetPollsUseCase
import im.vector.app.test.test
import im.vector.app.test.testDispatcher
import io.mockk.every