Support summary pagination API in manage space

This commit is contained in:
Valere 2021-08-31 15:22:27 +02:00
parent 5297512f87
commit c01b36a648
7 changed files with 110 additions and 25 deletions

View file

@ -213,5 +213,4 @@ class SpaceDirectoryFragment @Inject constructor(
// nothing?
return false
}
}

View file

@ -125,7 +125,7 @@ class SpaceDirectoryViewModel @AssistedInject constructor(
paginationStatus = state.paginationStatus.toMutableMap().apply {
this[spaceId] = Uninitialized
}.toMap(),
knownRoomSummaries = (state.knownRoomSummaries + knownSummaries).distinctBy { it.roomId },
knownRoomSummaries = (state.knownRoomSummaries + knownSummaries).distinctBy { it.roomId }
)
}
} catch (failure: Throwable) {
@ -288,7 +288,7 @@ class SpaceDirectoryViewModel @AssistedInject constructor(
cachedResults[currentRootId] = Success(
currentResponse.copy(
children = currentResponse.children + query.children,
nextToken = query.nextToken,
nextToken = query.nextToken
)
)
setState {

View file

@ -25,4 +25,5 @@ sealed class SpaceManageRoomViewAction : VectorViewModelAction {
data class MarkAllAsSuggested(val suggested: Boolean) : SpaceManageRoomViewAction()
object BulkRemove : SpaceManageRoomViewAction()
object RefreshFromServer : SpaceManageRoomViewAction()
object LoadAdditionalItemsIfNeeded : SpaceManageRoomViewAction()
}

View file

@ -20,15 +20,16 @@ import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
import org.matrix.android.sdk.internal.session.space.SpaceHierarchySummary
data class SpaceManageRoomViewState(
val spaceId: String,
val spaceSummary: Async<RoomSummary> = Uninitialized,
val childrenInfo: Async<List<SpaceChildInfo>> = Uninitialized,
val childrenInfo: Async<SpaceHierarchySummary> = Uninitialized,
val selectedRooms: List<String> = emptyList(),
val currentFilter: String = "",
val actionState: Async<Unit> = Uninitialized
val actionState: Async<Unit> = Uninitialized,
val paginationStatus: Async<Unit> = Uninitialized
) : MvRxState {
constructor(args: SpaceManageArgs) : this(
spaceId = args.spaceId

View file

@ -17,6 +17,7 @@
package im.vector.app.features.spaces.manage
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.epoxy.VisibilityState
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete
import im.vector.app.R
@ -39,6 +40,7 @@ class SpaceManageRoomsController @Inject constructor(
interface Listener {
fun toggleSelection(childInfo: SpaceChildInfo)
fun retry()
fun loadAdditionalItemsIfNeeded()
}
var listener: Listener? = null
@ -60,7 +62,7 @@ class SpaceManageRoomsController @Inject constructor(
return
}
val roomList = roomListAsync?.invoke() ?: return
val roomList = roomListAsync?.invoke()?.children ?: return
val directChildren = roomList.filter {
it.parentRoomId == data.spaceId
@ -88,5 +90,29 @@ class SpaceManageRoomsController @Inject constructor(
}
}
}
val nextToken = roomListAsync.invoke()?.nextToken
if (nextToken != null) {
// show loading item
val paginationStatus = data.paginationStatus
if (paginationStatus is Fail) {
errorWithRetryItem {
id("error_$nextToken")
text(host.errorFormatter.toHumanReadable(paginationStatus.error))
listener { host.listener?.retry() }
}
} else {
loadingItem {
id("pagination_$nextToken")
showLoader(true)
onVisibilityStateChanged { _, _, visibilityState ->
// Do something with the new visibility state
if (visibilityState == VisibilityState.VISIBLE) {
// we can trigger a seamless load of additional items
host.listener?.loadAdditionalItemsIfNeeded()
}
}
}
}
}
}
}

View file

@ -26,6 +26,7 @@ import androidx.appcompat.view.ActionMode
import androidx.appcompat.view.ActionMode.Callback
import androidx.core.view.isVisible
import androidx.transition.TransitionManager
import com.airbnb.epoxy.EpoxyVisibilityTracker
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel
@ -54,6 +55,7 @@ class SpaceManageRoomsFragment @Inject constructor(
private val viewModel by fragmentViewModel(SpaceManageRoomsViewModel::class)
private val sharedViewModel: SpaceManageSharedViewModel by activityViewModel()
private val epoxyVisibilityTracker = EpoxyVisibilityTracker()
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentSpaceAddRoomsBinding.inflate(inflater)
@ -69,6 +71,7 @@ class SpaceManageRoomsFragment @Inject constructor(
views.createNewRoom.isVisible = false
epoxyController.listener = this
views.roomList.configureWith(epoxyController, hasFixedSize = true, dividerDrawable = R.drawable.divider_horizontal)
epoxyVisibilityTracker.attach(views.roomList)
views.publicRoomsFilter.queryTextChanges()
.debounce(200, TimeUnit.MILLISECONDS)
@ -99,6 +102,7 @@ class SpaceManageRoomsFragment @Inject constructor(
override fun onDestroyView() {
epoxyController.listener = null
epoxyVisibilityTracker.detach(views.roomList)
views.roomList.cleanup()
super.onDestroyView()
}
@ -136,6 +140,10 @@ class SpaceManageRoomsFragment @Inject constructor(
viewModel.handle(SpaceManageRoomViewAction.RefreshFromServer)
}
override fun loadAdditionalItemsIfNeeded() {
viewModel.handle(SpaceManageRoomViewAction.LoadAdditionalItemsIfNeeded)
}
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
val inflater = mode?.menuInflater
inflater?.inflate(R.menu.menu_manage_space, menu)
@ -150,7 +158,7 @@ class SpaceManageRoomsFragment @Inject constructor(
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
withState(viewModel) { state ->
// check if we show mark as suggested or not
val areAllSuggested = state.childrenInfo.invoke().orEmpty().filter { state.selectedRooms.contains(it.childRoomId) }
val areAllSuggested = state.childrenInfo.invoke()?.children.orEmpty().filter { state.selectedRooms.contains(it.childRoomId) }
.all { it.suggested == true }
menu?.findItem(R.id.action_mark_as_suggested)?.isVisible = !areAllSuggested
menu?.findItem(R.id.action_mark_as_not_suggested)?.isVisible = areAllSuggested

View file

@ -18,6 +18,7 @@ package im.vector.app.features.spaces.manage
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory
@ -39,6 +40,8 @@ class SpaceManageRoomsViewModel @AssistedInject constructor(
private val session: Session
) : VectorViewModel<SpaceManageRoomViewState, SpaceManageRoomViewAction, SpaceManageRoomViewEvents>(initialState) {
private val paginationLimit = 10
init {
val spaceSummary = session.getRoomSummary(initialState.spaceId)
setState {
@ -49,14 +52,7 @@ class SpaceManageRoomsViewModel @AssistedInject constructor(
}
viewModelScope.launch(Dispatchers.IO) {
val apiResult = runCatchingToAsync {
session.spaceService().querySpaceChildren(spaceId = initialState.spaceId).children
}
setState {
copy(
childrenInfo = apiResult
)
}
refreshSummaryAPI()
}
}
@ -93,6 +89,9 @@ class SpaceManageRoomsViewModel @AssistedInject constructor(
SpaceManageRoomViewAction.RefreshFromServer -> {
refreshSummaryAPI()
}
SpaceManageRoomViewAction.LoadAdditionalItemsIfNeeded -> {
paginateIfNeeded()
}
}
}
@ -120,7 +119,7 @@ class SpaceManageRoomsViewModel @AssistedInject constructor(
private fun handleBulkMarkAsSuggested(suggested: Boolean) = withState { state ->
setState { copy(actionState = Loading()) }
val selection = state.childrenInfo.invoke()?.filter {
val selection = state.childrenInfo.invoke()?.children?.filter {
state.selectedRooms.contains(it.childRoomId)
}.orEmpty()
session.coroutineScope.launch(Dispatchers.IO) {
@ -156,16 +155,67 @@ class SpaceManageRoomsViewModel @AssistedInject constructor(
}
viewModelScope.launch(Dispatchers.IO) {
val apiResult = runCatchingToAsync {
session.spaceService().querySpaceChildren(spaceId = initialState.spaceId).children
session.spaceService().querySpaceChildren(
spaceId = initialState.spaceId,
limit = paginationLimit
)
}
setState {
copy(
childrenInfo = apiResult
childrenInfo = apiResult,
paginationStatus = Uninitialized
)
}
}
}
private fun paginateIfNeeded() = withState { state ->
if (state.paginationStatus is Loading) return@withState
val knownResults = state.childrenInfo.invoke()
val nextToken = knownResults?.nextToken
if (knownResults == null || nextToken == null) {
setState {
copy(
paginationStatus = Uninitialized
)
}
return@withState
}
setState {
copy(
paginationStatus = Loading()
)
}
viewModelScope.launch(Dispatchers.IO) {
try {
val apiResult = session.spaceService().querySpaceChildren(
spaceId = initialState.spaceId,
from = nextToken,
knownStateList = knownResults.childrenState.orEmpty(),
limit = paginationLimit
)
setState {
copy(
childrenInfo = Success(
knownResults.copy(
children = knownResults.children + apiResult.children,
nextToken = apiResult.nextToken
)
),
paginationStatus = Success(Unit)
)
}
} catch (failure: Throwable) {
setState {
copy(
paginationStatus = Fail(failure)
)
}
}
}
}
private fun handleToggleSelection(action: SpaceManageRoomViewAction.ToggleSelection) = withState { state ->
val existing = state.selectedRooms.toMutableList()
if (existing.contains(action.roomId)) {