From 5297512f87f20b888992ffe4d337b0e831a09a5f Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 31 Aug 2021 11:50:23 +0200 Subject: [PATCH 1/7] Support Space explore pagination --- changelog.d/3693.feature | 1 + .../api/session/room/model/SpaceChildInfo.kt | 2 +- .../android/sdk/api/session/space/Space.kt | 6 +- .../sdk/api/session/space/SpaceService.kt | 13 +- .../session/space/model/SpaceChildContent.kt | 12 +- .../database/mapper/RoomSummaryMapper.kt | 2 +- .../relationship/RoomChildRelationInfo.kt | 4 +- .../room/summary/RoomSummaryUpdater.kt | 2 +- .../internal/session/space/DefaultSpace.kt | 55 +++--- .../session/space/DefaultSpaceService.kt | 106 +++++----- .../internal/session/space/JoinSpaceTask.kt | 65 +++---- .../session/space/ResolveSpaceInfoTask.kt | 47 +++-- .../sdk/internal/session/space/SpaceApi.kt | 26 ++- .../space/SpaceChildSummaryResponse.kt | 22 ++- .../session/space/SpaceHierarchySummary.kt | 28 +++ .../session/space/SpaceSummaryParams.kt | 34 ---- .../internal/session/space/SpacesResponse.kt | 5 +- .../session/space/peeking/PeekSpaceTask.kt | 6 +- .../session/space/peeking/SpacePeekResult.kt | 6 +- .../upgrade/UpgradeRoomViewModelTask.kt | 2 +- .../room/list/RoomListSectionBuilderSpace.kt | 7 +- .../features/matrixto/SpaceCardRenderer.kt | 2 +- .../spaces/create/CreateSpaceViewModelTask.kt | 2 +- .../explore/SpaceDirectoryController.kt | 33 +++- .../spaces/explore/SpaceDirectoryFragment.kt | 29 +-- .../spaces/explore/SpaceDirectoryState.kt | 13 +- .../explore/SpaceDirectoryViewAction.kt | 1 + .../spaces/explore/SpaceDirectoryViewModel.kt | 184 +++++++++++++++--- .../manage/SpaceManageRoomsViewModel.kt | 8 +- .../spaces/preview/SpacePreviewViewModel.kt | 4 +- 30 files changed, 463 insertions(+), 264 deletions(-) create mode 100644 changelog.d/3693.feature create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceHierarchySummary.kt delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceSummaryParams.kt diff --git a/changelog.d/3693.feature b/changelog.d/3693.feature new file mode 100644 index 00000000000..6351ded5223 --- /dev/null +++ b/changelog.d/3693.feature @@ -0,0 +1 @@ +Space summary pagination \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/SpaceChildInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/SpaceChildInfo.kt index 8cd2a0538dc..7d3109fb6e6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/SpaceChildInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/SpaceChildInfo.kt @@ -27,7 +27,7 @@ data class SpaceChildInfo( val avatarUrl: String?, val order: String?, val activeMemberCount: Int?, - val autoJoin: Boolean, +// val autoJoin: Boolean, val viaServers: List, val parentRoomId: String?, val suggested: Boolean?, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/Space.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/Space.kt index 3bae6126e07..207050be7d9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/Space.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/Space.kt @@ -36,7 +36,7 @@ interface Space { suspend fun addChildren(roomId: String, viaServers: List?, order: String?, - autoJoin: Boolean = false, +// autoJoin: Boolean = false, suggested: Boolean? = false) fun getChildInfo(roomId: String): SpaceChildContent? @@ -46,8 +46,8 @@ interface Space { @Throws suspend fun setChildrenOrder(roomId: String, order: String?) - @Throws - suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean) +// @Throws +// suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean) @Throws suspend fun setChildrenSuggested(roomId: String, suggested: Boolean) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt index e5288e4045b..5a8a0398f70 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt @@ -18,9 +18,10 @@ package org.matrix.android.sdk.api.session.space import android.net.Uri import androidx.lifecycle.LiveData +import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams 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 import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult typealias SpaceSummaryQueryParams = RoomSummaryQueryParams @@ -58,10 +59,18 @@ interface SpaceService { /** * Get's information of a space by querying the server + * @param suggestedOnly If true, return only child events and rooms where the m.space.child event has suggested: true. + * @param limit a client-defined limit to the maximum number of rooms to return per page. Must be a non-negative integer. + * @param maxDepth: Optional: The maximum depth in the tree (from the root room) to return. + * @param from: Optional. Pagination token given to retrieve the next set of rooms. Note that if a pagination token is provided, + * then the parameters given for suggested_only and max_depth must be the same. */ suspend fun querySpaceChildren(spaceId: String, suggestedOnly: Boolean? = null, - autoJoinedOnly: Boolean? = null): Pair> + limit: Int? = null, + from: String? = null, + // when paginating, pass back the m.space.child state events + knownStateList: List? = null): SpaceHierarchySummary /** * Get a live list of space summaries. This list is refreshed as soon as the data changes. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/model/SpaceChildContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/model/SpaceChildContent.kt index 0c33cfa1e60..b55f90cef06 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/model/SpaceChildContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/model/SpaceChildContent.kt @@ -40,12 +40,12 @@ data class SpaceChildContent( * or consist of more than 50 characters, are forbidden and should be ignored if received.) */ @Json(name = "order") val order: String? = null, - /** - * The auto_join flag on a child listing allows a space admin to list the sub-spaces and rooms in that space which should - * be automatically joined by members of that space. - * (This is not a force-join, which are descoped for a future MSC; the user can subsequently part these room if they desire.) - */ - @Json(name = "auto_join") val autoJoin: Boolean? = false, +// /** +// * The auto_join flag on a child listing allows a space admin to list the sub-spaces and rooms in that space which should +// * be automatically joined by members of that space. +// * (This is not a force-join, which are descoped for a future MSC; the user can subsequently part these room if they desire.) +// */ +// @Json(name = "auto_join") val autoJoin: Boolean? = false, /** * If `suggested` is set to `true`, that indicates that the child should be advertised to diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt index c32c019625e..0cf431c340c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/RoomSummaryMapper.kt @@ -88,7 +88,7 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa avatarUrl = it.childSummaryEntity?.avatarUrl, activeMemberCount = it.childSummaryEntity?.joinedMembersCount, order = it.order, - autoJoin = it.autoJoin ?: false, +// autoJoin = it.autoJoin ?: false, viaServers = it.viaServers.toList(), parentRoomId = roomSummaryEntity.roomId, suggested = it.suggested, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relationship/RoomChildRelationInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relationship/RoomChildRelationInfo.kt index 2efea7f118f..5bad334afc1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relationship/RoomChildRelationInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relationship/RoomChildRelationInfo.kt @@ -43,7 +43,7 @@ internal class RoomChildRelationInfo( data class SpaceChildInfo( val roomId: String, val order: String?, - val autoJoin: Boolean, +// val autoJoin: Boolean, val viaServers: List ) @@ -71,7 +71,7 @@ internal class RoomChildRelationInfo( SpaceChildInfo( roomId = it.stateKey, order = scc.validOrder(), - autoJoin = scc.autoJoin ?: false, +// autoJoin = scc.autoJoin ?: false, viaServers = via ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 842c9d3abad..89a3533946d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -220,7 +220,7 @@ internal class RoomSummaryUpdater @Inject constructor( this.childRoomId = child.roomId this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst() this.order = child.order - this.autoJoin = child.autoJoin +// this.autoJoin = child.autoJoin this.viaServers.addAll(child.viaServers) } ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt index 7b513678e35..8a6bbc18fd1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt @@ -51,7 +51,7 @@ internal class DefaultSpace( override suspend fun addChildren(roomId: String, viaServers: List?, order: String?, - autoJoin: Boolean, +// autoJoin: Boolean, suggested: Boolean?) { // Find best via val bestVia = viaServers @@ -69,7 +69,6 @@ internal class DefaultSpace( stateKey = roomId, body = SpaceChildContent( via = bestVia, - autoJoin = autoJoin, order = order, suggested = suggested ).toContent() @@ -90,7 +89,7 @@ internal class DefaultSpace( body = SpaceChildContent( order = null, via = null, - autoJoin = null, +// autoJoin = null, suggested = null ).toContent() ) @@ -115,35 +114,35 @@ internal class DefaultSpace( body = SpaceChildContent( order = order, via = existing.via, - autoJoin = existing.autoJoin, +// autoJoin = existing.autoJoin, suggested = existing.suggested ).toContent() ) } - override suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean) { - val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId)) - .firstOrNull() - ?.content.toModel() - ?: throw IllegalArgumentException("$roomId is not a child of this space") - - if (existing.autoJoin == autoJoin) { - // nothing to do? - return - } - - // edit state event and set via to null - room.sendStateEvent( - eventType = EventType.STATE_SPACE_CHILD, - stateKey = roomId, - body = SpaceChildContent( - order = existing.order, - via = existing.via, - autoJoin = autoJoin, - suggested = existing.suggested - ).toContent() - ) - } +// override suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean) { +// val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId)) +// .firstOrNull() +// ?.content.toModel() +// ?: throw IllegalArgumentException("$roomId is not a child of this space") +// +// if (existing.autoJoin == autoJoin) { +// // nothing to do? +// return +// } +// +// // edit state event and set via to null +// room.sendStateEvent( +// eventType = EventType.STATE_SPACE_CHILD, +// stateKey = roomId, +// body = SpaceChildContent( +// order = existing.order, +// via = existing.via, +// autoJoin = autoJoin, +// suggested = existing.suggested +// ).toContent() +// ) +// } override suspend fun setChildrenSuggested(roomId: String, suggested: Boolean) { val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId)) @@ -162,7 +161,7 @@ internal class DefaultSpace( body = SpaceChildContent( order = existing.order, via = existing.via, - autoJoin = existing.autoJoin, +// autoJoin = existing.autoJoin, suggested = suggested ).toContent() ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index 0c5c0416f96..e23179b9e82 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.space import android.net.Uri import androidx.lifecycle.LiveData import org.matrix.android.sdk.api.query.QueryStringValue +import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent import org.matrix.android.sdk.api.session.events.model.toModel @@ -27,6 +28,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules 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.api.session.room.model.create.CreateRoomPreset @@ -108,53 +110,65 @@ internal class DefaultSpaceService @Inject constructor( override suspend fun querySpaceChildren(spaceId: String, suggestedOnly: Boolean?, - autoJoinedOnly: Boolean?): Pair> { - return resolveSpaceInfoTask.execute(ResolveSpaceInfoTask.Params.withId(spaceId, suggestedOnly, autoJoinedOnly)).let { response -> + limit: Int?, + from: String?, + knownStateList: List?): SpaceHierarchySummary { + return resolveSpaceInfoTask.execute( + ResolveSpaceInfoTask.Params( + spaceId = spaceId, limit = limit, maxDepth = 1, from = from, suggestedOnly = suggestedOnly + ) + ).let { response -> val spaceDesc = response.rooms?.firstOrNull { it.roomId == spaceId } - Pair( - first = RoomSummary( - roomId = spaceDesc?.roomId ?: spaceId, - roomType = spaceDesc?.roomType, - name = spaceDesc?.name ?: "", - displayName = spaceDesc?.name ?: "", - topic = spaceDesc?.topic ?: "", - joinedMembersCount = spaceDesc?.numJoinedMembers, - avatarUrl = spaceDesc?.avatarUrl ?: "", - encryptionEventTs = null, - typingUsers = emptyList(), - isEncrypted = false, - flattenParentIds = emptyList() - ), - second = response.rooms - ?.filter { it.roomId != spaceId } - ?.flatMap { childSummary -> - response.events - ?.filter { it.stateKey == childSummary.roomId && it.type == EventType.STATE_SPACE_CHILD } - ?.mapNotNull { childStateEv -> - // create a child entry for everytime this room is the child of a space - // beware that a room could appear then twice in this list - childStateEv.content.toModel()?.let { childStateEvContent -> - SpaceChildInfo( - childRoomId = childSummary.roomId, - isKnown = true, - roomType = childSummary.roomType, - name = childSummary.name, - topic = childSummary.topic, - avatarUrl = childSummary.avatarUrl, - order = childStateEvContent.order, - autoJoin = childStateEvContent.autoJoin ?: false, - viaServers = childStateEvContent.via.orEmpty(), - activeMemberCount = childSummary.numJoinedMembers, - parentRoomId = childStateEv.roomId, - suggested = childStateEvContent.suggested, - canonicalAlias = childSummary.canonicalAlias, - aliases = childSummary.aliases, - worldReadable = childSummary.worldReadable - ) - } - }.orEmpty() - } - .orEmpty() + val root = RoomSummary( + roomId = spaceDesc?.roomId ?: spaceId, + roomType = spaceDesc?.roomType, + name = spaceDesc?.name ?: "", + displayName = spaceDesc?.name ?: "", + topic = spaceDesc?.topic ?: "", + joinedMembersCount = spaceDesc?.numJoinedMembers, + avatarUrl = spaceDesc?.avatarUrl ?: "", + encryptionEventTs = null, + typingUsers = emptyList(), + isEncrypted = false, + flattenParentIds = emptyList(), + canonicalAlias = spaceDesc?.canonicalAlias, + joinRules = RoomJoinRules.PUBLIC.takeIf { spaceDesc?.worldReadable == true } + ) + val children = response.rooms + ?.filter { it.roomId != spaceId } + ?.flatMap { childSummary -> + (spaceDesc?.childrenState ?: knownStateList) + ?.filter { it.stateKey == childSummary.roomId && it.type == EventType.STATE_SPACE_CHILD } + ?.mapNotNull { childStateEv -> + // create a child entry for everytime this room is the child of a space + // beware that a room could appear then twice in this list + childStateEv.content.toModel()?.let { childStateEvContent -> + SpaceChildInfo( + childRoomId = childSummary.roomId, + isKnown = true, + roomType = childSummary.roomType, + name = childSummary.name, + topic = childSummary.topic, + avatarUrl = childSummary.avatarUrl, + order = childStateEvContent.order, +// autoJoin = childStateEvContent.autoJoin ?: false, + viaServers = childStateEvContent.via.orEmpty(), + activeMemberCount = childSummary.numJoinedMembers, + parentRoomId = childStateEv.roomId, + suggested = childStateEvContent.suggested, + canonicalAlias = childSummary.canonicalAlias, + aliases = childSummary.aliases, + worldReadable = childSummary.worldReadable + ) + } + }.orEmpty() + } + .orEmpty() + SpaceHierarchySummary( + rootSummary = root, + children = children, + childrenState = spaceDesc?.childrenState.orEmpty(), + nextToken = response.nextBatch ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/JoinSpaceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/JoinSpaceTask.kt index e9d5ba51937..7eeaed4ff62 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/JoinSpaceTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/JoinSpaceTask.kt @@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.session.space import io.realm.RealmConfiguration import kotlinx.coroutines.TimeoutCancellationException import org.matrix.android.sdk.api.session.room.model.Membership -import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.space.JoinSpaceResult import org.matrix.android.sdk.internal.database.awaitNotEmptyResult import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity @@ -84,39 +83,39 @@ internal class DefaultJoinSpaceTask @Inject constructor( // after that i should have the children (? do I need to paginate to get state) val summary = roomSummaryDataSource.getSpaceSummary(params.roomIdOrAlias) Timber.v("## Space: Found space summary Name:[${summary?.name}] children: ${summary?.spaceChildren?.size}") - summary?.spaceChildren?.forEach { +// summary?.spaceChildren?.forEach { // val childRoomSummary = it.roomSummary ?: return@forEach - Timber.v("## Space: Processing child :[${it.childRoomId}] autoJoin:${it.autoJoin}") - if (it.autoJoin) { - // I should try to join as well - if (it.roomType == RoomType.SPACE) { - // recursively join auto-joined child of this space? - when (val subspaceJoinResult = execute(JoinSpaceTask.Params(it.childRoomId, null, it.viaServers))) { - JoinSpaceResult.Success -> { - // nop - } - is JoinSpaceResult.Fail -> { - errors[it.childRoomId] = subspaceJoinResult.error - } - is JoinSpaceResult.PartialSuccess -> { - errors.putAll(subspaceJoinResult.failedRooms) - } - } - } else { - try { - Timber.v("## Space: Joining room child ${it.childRoomId}") - joinRoomTask.execute(JoinRoomTask.Params( - roomIdOrAlias = it.childRoomId, - reason = "Auto-join parent space", - viaServers = it.viaServers - )) - } catch (failure: Throwable) { - errors[it.childRoomId] = failure - Timber.e("## Space: Failed to join room child ${it.childRoomId}") - } - } - } - } +// Timber.v("## Space: Processing child :[${it.childRoomId}] suggested:${it.suggested}") +// if (it.autoJoin) { +// // I should try to join as well +// if (it.roomType == RoomType.SPACE) { +// // recursively join auto-joined child of this space? +// when (val subspaceJoinResult = execute(JoinSpaceTask.Params(it.childRoomId, null, it.viaServers))) { +// JoinSpaceResult.Success -> { +// // nop +// } +// is JoinSpaceResult.Fail -> { +// errors[it.childRoomId] = subspaceJoinResult.error +// } +// is JoinSpaceResult.PartialSuccess -> { +// errors.putAll(subspaceJoinResult.failedRooms) +// } +// } +// } else { +// try { +// Timber.v("## Space: Joining room child ${it.childRoomId}") +// joinRoomTask.execute(JoinRoomTask.Params( +// roomIdOrAlias = it.childRoomId, +// reason = "Auto-join parent space", +// viaServers = it.viaServers +// )) +// } catch (failure: Throwable) { +// errors[it.childRoomId] = failure +// Timber.e("## Space: Failed to join room child ${it.childRoomId}") +// } +// } +// } +// } return if (errors.isEmpty()) { JoinSpaceResult.Success diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/ResolveSpaceInfoTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/ResolveSpaceInfoTask.kt index d2be49b70b0..0d305fcff47 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/ResolveSpaceInfoTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/ResolveSpaceInfoTask.kt @@ -24,23 +24,24 @@ import javax.inject.Inject internal interface ResolveSpaceInfoTask : Task { data class Params( val spaceId: String, - val maxRoomPerSpace: Int?, - val limit: Int, - val batchToken: String?, - val suggestedOnly: Boolean?, - val autoJoinOnly: Boolean? +// val maxRoomPerSpace: Int?, + val limit: Int?, + val maxDepth: Int?, + val from: String?, + val suggestedOnly: Boolean? +// val autoJoinOnly: Boolean? ) { - companion object { - fun withId(spaceId: String, suggestedOnly: Boolean?, autoJoinOnly: Boolean?) = - Params( - spaceId = spaceId, - maxRoomPerSpace = 10, - limit = 20, - batchToken = null, - suggestedOnly = suggestedOnly, - autoJoinOnly = autoJoinOnly - ) - } +// companion object { +// fun withId(spaceId: String, suggestedOnly: Boolean?) = +// Params( +// spaceId = spaceId, +// // maxRoomPerSpace = 10, +// limit = 20, +// from = null, +// suggestedOnly = suggestedOnly +// // autoJoinOnly = autoJoinOnly +// ) +// } } } @@ -49,15 +50,13 @@ internal class DefaultResolveSpaceInfoTask @Inject constructor( private val globalErrorReceiver: GlobalErrorReceiver ) : ResolveSpaceInfoTask { override suspend fun execute(params: ResolveSpaceInfoTask.Params): SpacesResponse { - val body = SpaceSummaryParams( - maxRoomPerSpace = params.maxRoomPerSpace, - limit = params.limit, - batch = params.batchToken ?: "", - autoJoinedOnly = params.autoJoinOnly, - suggestedOnly = params.suggestedOnly - ) return executeRequest(globalErrorReceiver) { - spaceApi.getSpaces(params.spaceId, body) + spaceApi.getSpaceHierarchy( + spaceId = params.spaceId, + suggestedOnly = params.suggestedOnly, + limit = params.limit, + maxDepth = params.maxDepth, + from = params.from) } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt index 0fcc95fdb33..4d3de2c8a02 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt @@ -17,9 +17,9 @@ package org.matrix.android.sdk.internal.session.space import org.matrix.android.sdk.internal.network.NetworkConstants -import retrofit2.http.Body -import retrofit2.http.POST +import retrofit2.http.GET import retrofit2.http.Path +import retrofit2.http.Query internal interface SpaceApi { @@ -37,7 +37,23 @@ internal interface SpaceApi { * - MSC 2946 https://github.com/matrix-org/matrix-doc/blob/kegan/spaces-summary/proposals/2946-spaces-summary.md * - https://hackmd.io/fNYh4tjUT5mQfR1uuRzWDA */ - @POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/spaces") - suspend fun getSpaces(@Path("roomId") spaceId: String, - @Body params: SpaceSummaryParams): SpacesResponse +// @POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/spaces") +// suspend fun getSpaces(@Path("roomId") spaceId: String, +// @Body params: SpaceSummaryParams): SpacesResponse + + /** + * @param limit: Optional: a client-defined limit to the maximum number of rooms to return per page. Must be a non-negative integer. + * @param max_depth: Optional: The maximum depth in the tree (from the root room) to return. + * The deepest depth returned will not include children events. Defaults to no-limit. Must be a non-negative integer. + * + * @param from: Optional. Pagination token given to retrieve the next set of rooms. + * Note that if a pagination token is provided, then the parameters given for suggested_only and max_depth must be the same. + */ + @GET(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/hierarchy") + suspend fun getSpaceHierarchy( + @Path("roomId") spaceId: String, + @Query("suggested_only") suggestedOnly: Boolean?, + @Query("limit") limit: Int?, + @Query("max_depth") maxDepth: Int?, + @Query("from") from: String?): SpacesResponse } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt index 5021ff638f2..74faa2653aa 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt @@ -18,14 +18,21 @@ package org.matrix.android.sdk.internal.session.space import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import org.matrix.android.sdk.api.session.events.model.Event +/** + * The fields are the same as those returned by /publicRooms (see spec), with the addition of: + * room_type: the value of the m.type field from the room's m.room.create event, if any. + * children_state: The m.space.child events of the room. For each event, only the following fields are included1: type, state_key, content, room_id, sender, with the addition of: + * origin_server_ts: This is required for sorting of rooms as specified below. + */ @JsonClass(generateAdapter = true) internal data class SpaceChildSummaryResponse( - /** - * The total number of state events which point to or from this room (inbound/outbound edges). - * This includes all m.space.child events in the room, in addition to m.room.parent events which point to this room as a parent. - */ - @Json(name = "num_refs") val numRefs: Int? = null, +// /** +// * The total number of state events which point to or from this room (inbound/outbound edges). +// * This includes all m.space.child events in the room, in addition to m.room.parent events which point to this room as a parent. +// */ +// @Json(name = "num_refs") val numRefs: Int? = null, /** * The room type, which is m.space for subspaces. @@ -33,6 +40,11 @@ internal data class SpaceChildSummaryResponse( */ @Json(name = "room_type") val roomType: String? = null, + /** The m.space.child events of the room. For each event, only the following fields are included: + * type, state_key, content, room_id, sender, with the addition of origin_server_ts: This is required for sorting of rooms as specified below. + */ + @Json(name = "children_state") val childrenState: List? = null, + /** * Aliases of the room. May be empty. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceHierarchySummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceHierarchySummary.kt new file mode 100644 index 00000000000..bb362b416b9 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceHierarchySummary.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 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 org.matrix.android.sdk.internal.session.space + +import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo + +data class SpaceHierarchySummary( + val rootSummary: RoomSummary, + val children: List, + val childrenState: List, + val nextToken: String? = null +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceSummaryParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceSummaryParams.kt deleted file mode 100644 index 013db1c2862..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceSummaryParams.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * 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 org.matrix.android.sdk.internal.session.space - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass - -@JsonClass(generateAdapter = true) -internal data class SpaceSummaryParams( - /** The maximum number of rooms/subspaces to return for a given space, if negative unbounded. default: -1 */ - @Json(name = "max_rooms_per_space") val maxRoomPerSpace: Int?, - /** The maximum number of rooms/subspaces to return, server can override this, default: 100 */ - @Json(name = "limit") val limit: Int?, - /** A token to use if this is a subsequent HTTP hit, default: "". */ - @Json(name = "batch") val batch: String = "", - /** whether we should only return children with the "suggested" flag set. */ - @Json(name = "suggested_only") val suggestedOnly: Boolean?, - /** whether we should only return children with the "suggested" flag set. */ - @Json(name = "auto_join_only") val autoJoinedOnly: Boolean? -) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpacesResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpacesResponse.kt index 20d63c88148..7d22dce96bc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpacesResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpacesResponse.kt @@ -18,14 +18,11 @@ package org.matrix.android.sdk.internal.session.space import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.api.session.events.model.Event @JsonClass(generateAdapter = true) internal data class SpacesResponse( /** Its presence indicates that there are more results to return. */ @Json(name = "next_batch") val nextBatch: String? = null, /** Rooms information like name/avatar/type ... */ - @Json(name = "rooms") val rooms: List? = null, - /** These are the edges of the graph. The objects in the array are complete (or stripped?) m.room.parent or m.space.child events. */ - @Json(name = "events") val events: List? = null + @Json(name = "rooms") val rooms: List? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/PeekSpaceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/PeekSpaceTask.kt index f6b156a6fb1..5cbaaa45c40 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/PeekSpaceTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/PeekSpaceTask.kt @@ -103,7 +103,7 @@ internal class DefaultPeekSpaceTask @Inject constructor( // can't peek :/ spaceChildResults.add( SpaceChildPeekResult( - childId, childPeek, entry.second?.autoJoin, entry.second?.order + childId, childPeek, entry.second?.order ) ) // continue to next child @@ -116,7 +116,7 @@ internal class DefaultPeekSpaceTask @Inject constructor( SpaceSubChildPeekResult( childId, childPeek, - entry.second?.autoJoin, +// entry.second?.autoJoin, entry.second?.order, peekChildren(childStateEvents, depth + 1, maxDepth) ) @@ -127,7 +127,7 @@ internal class DefaultPeekSpaceTask @Inject constructor( Timber.v("## SPACE_PEEK: room child $entry") spaceChildResults.add( SpaceChildPeekResult( - childId, childPeek, entry.second?.autoJoin, entry.second?.order + childId, childPeek, entry.second?.order ) ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/SpacePeekResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/SpacePeekResult.kt index 1df62e94e84..44d879f05d4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/SpacePeekResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/SpacePeekResult.kt @@ -28,21 +28,21 @@ data class SpacePeekSummary( interface ISpaceChild { val id: String val roomPeekResult: PeekResult - val default: Boolean? +// val default: Boolean? val order: String? } data class SpaceChildPeekResult( override val id: String, override val roomPeekResult: PeekResult, - override val default: Boolean? = null, +// override val default: Boolean? = null, override val order: String? = null ) : ISpaceChild data class SpaceSubChildPeekResult( override val id: String, override val roomPeekResult: PeekResult, - override val default: Boolean?, +// override val default: Boolean?, override val order: String?, val children: List ) : ISpaceChild diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/UpgradeRoomViewModelTask.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/UpgradeRoomViewModelTask.kt index 32c8e6ee92b..8859aaeacf4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/UpgradeRoomViewModelTask.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/UpgradeRoomViewModelTask.kt @@ -80,7 +80,7 @@ class UpgradeRoomViewModelTask @Inject constructor( roomId = updatedRoomId, viaServers = currentInfo.via, order = currentInfo.order, - autoJoin = currentInfo.autoJoin ?: false, +// autoJoin = currentInfo.autoJoin ?: false, suggested = currentInfo.suggested ) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilderSpace.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilderSpace.kt index 13a6fc0d2d7..70632818535 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilderSpace.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilderSpace.kt @@ -230,8 +230,11 @@ class RoomListSectionBuilderSpace( Observable.just(emptyList()) } else { liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) { - val spaceSum = tryOrNull { session.spaceService().querySpaceChildren(selectedSpace.roomId, suggestedOnly = true) } - val value = spaceSum?.second.orEmpty().distinctBy { it.childRoomId } + val spaceSum = tryOrNull { + session.spaceService() + .querySpaceChildren(selectedSpace.roomId, suggestedOnly = true, null, null) + } + val value = spaceSum?.children.orEmpty().distinctBy { it.childRoomId } // i need to check if it's already joined. val filtered = value.filter { session.getRoomSummary(it.childRoomId)?.membership?.isActive() != true diff --git a/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt b/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt index e51490a59c5..88d65e23b16 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt @@ -62,7 +62,7 @@ class SpaceCardRenderer @Inject constructor( inCard.matrixToAccessImage.isVisible = true inCard.matrixToAccessImage.setImageResource(R.drawable.ic_room_private) } - val memberCount = spaceSummary.otherMemberIds.size + val memberCount = spaceSummary.joinedMembersCount?.let { it - 1 } ?: 0 if (memberCount != 0) { inCard.matrixToMemberPills.isVisible = true inCard.spaceChildMemberCountText.text = stringProvider.getQuantityString(R.plurals.room_title_members, memberCount, memberCount) diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt index 1440efe6fe6..c68b8a0b9ba 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt @@ -134,7 +134,7 @@ class CreateSpaceViewModelTask @Inject constructor( timeout.roomID } val via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList() - createdSpace!!.addChildren(roomId, via, null, autoJoin = false, suggested = true) + createdSpace!!.addChildren(roomId, via, null, suggested = true) // set canonical session.spaceService().setSpaceParent( roomId, diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt index 1e67da7a4df..c76eec257b0 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt @@ -18,8 +18,10 @@ package im.vector.app.features.spaces.explore import android.view.View import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.epoxy.VisibilityState import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Incomplete +import com.airbnb.mvrx.Uninitialized import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.errorWithRetryItem @@ -54,13 +56,15 @@ class SpaceDirectoryController @Inject constructor( fun onRoomClick(spaceChildInfo: SpaceChildInfo) fun retry() fun addExistingRooms(spaceId: String) + fun loadAdditionalItemsIfNeeded() } var listener: InteractionListener? = null override fun buildModels(data: SpaceDirectoryState?) { val host = this - val results = data?.spaceSummaryApiResult + val currentRootId = data?.hierarchyStack?.lastOrNull() ?: data?.spaceId ?: return + val results = data?.apiResults?.get(currentRootId) if (results is Incomplete) { loadingItem { @@ -94,7 +98,9 @@ class SpaceDirectoryController @Inject constructor( } } } else { - val flattenChildInfo = results?.invoke() + val hierarchySummary = results?.invoke() + val flattenChildInfo = hierarchySummary + ?.children ?.filter { it.parentRoomId == (data.hierarchyStack.lastOrNull() ?: data.spaceId) } @@ -132,6 +138,7 @@ class SpaceDirectoryController @Inject constructor( // if it's known use that matrixItem because it would have a better computed name val matrixItem = data?.knownRoomSummaries?.find { it.roomId == info.childRoomId }?.toMatrixItem() ?: info.toMatrixItem() + spaceChildInfoItem { id(info.childRoomId) matrixItem(matrixItem) @@ -162,6 +169,28 @@ class SpaceDirectoryController @Inject constructor( } } } + if (hierarchySummary?.nextToken != null) { + val paginationStatus = data.paginationStatus[currentRootId] ?: Uninitialized + if (paginationStatus is Fail) { + errorWithRetryItem { + id("error_${currentRootId}_${hierarchySummary.nextToken}") + text(host.errorFormatter.toHumanReadable(paginationStatus.error)) + listener { host.listener?.retry() } + } + } else { + loadingItem { + id("pagination_${currentRootId}_${hierarchySummary.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() + } + } + } + } + } } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt index 323d8a7c87d..9714cebd4ec 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt @@ -25,6 +25,7 @@ import android.view.View import android.view.ViewGroup import androidx.core.text.toSpannable import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyVisibilityTracker import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -72,6 +73,7 @@ class SpaceDirectoryFragment @Inject constructor( FragmentSpaceDirectoryBinding.inflate(layoutInflater, container, false) private val viewModel by activityViewModel(SpaceDirectoryViewModel::class) + private val epoxyVisibilityTracker = EpoxyVisibilityTracker() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -84,6 +86,7 @@ class SpaceDirectoryFragment @Inject constructor( } epoxyController.listener = this views.spaceDirectoryList.configureWith(epoxyController) + epoxyVisibilityTracker.attach(views.spaceDirectoryList) viewModel.selectSubscribe(this, SpaceDirectoryState::canAddRooms) { invalidateOptionsMenu() @@ -95,6 +98,7 @@ class SpaceDirectoryFragment @Inject constructor( override fun onDestroyView() { epoxyController.listener = null + epoxyVisibilityTracker.detach(views.spaceDirectoryList) views.spaceDirectoryList.cleanup() super.onDestroyView() } @@ -102,21 +106,20 @@ class SpaceDirectoryFragment @Inject constructor( override fun invalidate() = withState(viewModel) { state -> epoxyController.setData(state) - val currentParent = state.hierarchyStack.lastOrNull()?.let { currentParent -> - state.spaceSummaryApiResult.invoke()?.firstOrNull { it.childRoomId == currentParent } - } + val currentParentId = state.hierarchyStack.lastOrNull() - if (currentParent == null) { + if (currentParentId == null) { + // it's the root val title = getString(R.string.space_explore_activity_title) views.toolbar.title = title - - spaceCardRenderer.render(state.spaceSummary.invoke(), emptyList(), this, views.spaceCard) } else { - val title = currentParent.name ?: currentParent.canonicalAlias ?: getString(R.string.space_explore_activity_title) + val title = state.currentRootSummary?.name + ?: state.currentRootSummary?.canonicalAlias + ?: getString(R.string.space_explore_activity_title) views.toolbar.title = title - - spaceCardRenderer.render(currentParent, emptyList(), this, views.spaceCard) } + + spaceCardRenderer.render(state.currentRootSummary, emptyList(), this, views.spaceCard) } override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state -> @@ -170,6 +173,10 @@ class SpaceDirectoryFragment @Inject constructor( addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRooms)) } + override fun loadAdditionalItemsIfNeeded() { + viewModel.handle(SpaceDirectoryViewAction.LoadAdditionalItemsIfNeeded) + } + override fun onUrlClicked(url: String, title: String): Boolean { permalinkHandler .launch(requireActivity(), url, null) @@ -206,7 +213,5 @@ class SpaceDirectoryFragment @Inject constructor( // nothing? return false } -// override fun navigateToRoom(roomId: String) { -// viewModel.handle(SpaceDirectoryViewAction.NavigateToRoom(roomId)) -// } + } diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt index 21541a51abf..f762ac7629d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt @@ -18,28 +18,27 @@ package im.vector.app.features.spaces.explore import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState -import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState 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 SpaceDirectoryState( // The current filter val spaceId: String, val currentFilter: String = "", - val spaceSummary: Async = Uninitialized, - val spaceSummaryApiResult: Async> = Uninitialized, + val apiResults: Map> = emptyMap(), + val currentRootSummary: RoomSummary? = null, val childList: List = emptyList(), val hierarchyStack: List = emptyList(), - // True if more result are available server side - val hasMore: Boolean = false, // Set of joined roomId / spaces, val joinedRoomsIds: Set = emptySet(), // keys are room alias or roomId val changeMembershipStates: Map = emptyMap(), val canAddRooms: Boolean = false, - // cached room summaries of known rooms - val knownRoomSummaries : List = emptyList() + // cached room summaries of known rooms, we use it because computed room name would be better using it + val knownRoomSummaries : List = emptyList(), + val paginationStatus: Map> = emptyMap() ) : MvRxState { constructor(args: SpaceDirectoryArgs) : this( spaceId = args.spaceId diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt index 83cdf2916d3..3ced017d61b 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewAction.kt @@ -26,4 +26,5 @@ sealed class SpaceDirectoryViewAction : VectorViewModelAction { data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewAction() object HandleBack : SpaceDirectoryViewAction() object Retry : SpaceDirectoryViewAction() + object LoadAdditionalItemsIfNeeded : SpaceDirectoryViewAction() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index de31f433222..578223cd36c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -23,6 +23,7 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -34,6 +35,8 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules +import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper @@ -67,11 +70,11 @@ class SpaceDirectoryViewModel @AssistedInject constructor( setState { copy( childList = spaceSum?.spaceChildren ?: emptyList(), - spaceSummary = spaceSum?.let { Success(spaceSum) } ?: Loading() + currentRootSummary = spaceSum ) } - refreshFromApi() + refreshFromApi(initialState.spaceId) observeJoinedRooms() observeMembershipChanges() observePermissions() @@ -93,29 +96,44 @@ class SpaceDirectoryViewModel @AssistedInject constructor( .disposeOnClear() } - private fun refreshFromApi() { + private fun refreshFromApi(rootId: String?) = withState { state -> + val spaceId = rootId ?: initialState.spaceId setState { copy( - spaceSummaryApiResult = Loading() + apiResults = state.apiResults.toMutableMap().apply { + this[spaceId] = Loading() + }.toMap() ) } viewModelScope.launch(Dispatchers.IO) { + val cachedResults = state.apiResults.toMutableMap() try { - val query = session.spaceService().querySpaceChildren(initialState.spaceId) - val knownSummaries = query.second.mapNotNull { + val query = session.spaceService().querySpaceChildren( + spaceId, + limit = 10 + ) + val knownSummaries = query.children.mapNotNull { session.getRoomSummary(it.childRoomId) ?.takeIf { it.membership == Membership.JOIN } // only take if joined because it will be up to date (synced) - } + }.distinctBy { it.roomId } setState { copy( - spaceSummaryApiResult = Success(query.second), - knownRoomSummaries = knownSummaries + apiResults = cachedResults.apply { + this[spaceId] = Success(query) + }, + currentRootSummary = query.rootSummary, + paginationStatus = state.paginationStatus.toMutableMap().apply { + this[spaceId] = Uninitialized + }.toMap(), + knownRoomSummaries = (state.knownRoomSummaries + knownSummaries).distinctBy { it.roomId }, ) } } catch (failure: Throwable) { setState { copy( - spaceSummaryApiResult = Fail(failure) + apiResults = cachedResults.apply { + this[spaceId] = Fail(failure) + } ) } } @@ -149,39 +167,143 @@ class SpaceDirectoryViewModel @AssistedInject constructor( override fun handle(action: SpaceDirectoryViewAction) { when (action) { - is SpaceDirectoryViewAction.ExploreSubSpace -> { - setState { - copy(hierarchyStack = hierarchyStack + listOf(action.spaceChildInfo.childRoomId)) - } + is SpaceDirectoryViewAction.ExploreSubSpace -> { + handleExploreSubSpace(action) } - SpaceDirectoryViewAction.HandleBack -> { - withState { - if (it.hierarchyStack.isEmpty()) { - _viewEvents.post(SpaceDirectoryViewEvents.Dismiss) - } else { - setState { - copy( - hierarchyStack = hierarchyStack.dropLast(1) - ) - } - } - } + SpaceDirectoryViewAction.HandleBack -> { + handleBack() } - is SpaceDirectoryViewAction.JoinOrOpen -> { + is SpaceDirectoryViewAction.JoinOrOpen -> { handleJoinOrOpen(action.spaceChildInfo) } - is SpaceDirectoryViewAction.NavigateToRoom -> { + is SpaceDirectoryViewAction.NavigateToRoom -> { _viewEvents.post(SpaceDirectoryViewEvents.NavigateToRoom(action.roomId)) } - is SpaceDirectoryViewAction.ShowDetails -> { + is SpaceDirectoryViewAction.ShowDetails -> { // This is temporary for now to at least display something for the space beta // It's not ideal as it's doing some peeking that is not needed. session.permalinkService().createRoomPermalink(action.spaceChildInfo.childRoomId)?.let { _viewEvents.post(SpaceDirectoryViewEvents.NavigateToMxToBottomSheet(it)) } } - SpaceDirectoryViewAction.Retry -> { - refreshFromApi() + SpaceDirectoryViewAction.Retry -> { + handleRetry() + } + SpaceDirectoryViewAction.LoadAdditionalItemsIfNeeded -> { + loadAdditionalItemsIfNeeded() + } + } + } + + private fun handleBack() = withState { state -> + if (state.hierarchyStack.isEmpty()) { + _viewEvents.post(SpaceDirectoryViewEvents.Dismiss) + } else { + val newStack = state.hierarchyStack.dropLast(1) + val newRootId = newStack.lastOrNull() ?: initialState.spaceId + val rootSummary = state.apiResults[newRootId]?.invoke()?.rootSummary + setState { + copy( + hierarchyStack = hierarchyStack.dropLast(1), + currentRootSummary = rootSummary + ) + } + } + } + + private fun handleRetry() = withState { state -> + refreshFromApi(state.hierarchyStack.lastOrNull() ?: initialState.spaceId) + } + + private fun handleExploreSubSpace(action: SpaceDirectoryViewAction.ExploreSubSpace) = withState { state -> + val newRootId = action.spaceChildInfo.childRoomId + val curSum = RoomSummary( + roomId = action.spaceChildInfo.childRoomId, + roomType = action.spaceChildInfo.roomType, + name = action.spaceChildInfo.name ?: "", + canonicalAlias = action.spaceChildInfo.canonicalAlias, + topic = action.spaceChildInfo.topic ?: "", + joinedMembersCount = action.spaceChildInfo.activeMemberCount, + avatarUrl = action.spaceChildInfo.avatarUrl ?: "", + isEncrypted = false, + joinRules = if (action.spaceChildInfo.worldReadable) RoomJoinRules.PUBLIC else RoomJoinRules.PRIVATE, + encryptionEventTs = 0, + typingUsers = emptyList() + ) + setState { + copy( + hierarchyStack = hierarchyStack + listOf(newRootId), + currentRootSummary = curSum + ) + } + val shouldLoad = when (state.apiResults[newRootId]) { + Uninitialized -> true + is Loading -> false + is Success -> false + is Fail -> true + null -> true + } + + if (shouldLoad) { + refreshFromApi(newRootId) + } + } + + private fun loadAdditionalItemsIfNeeded() = withState { state -> + val currentRootId = state.hierarchyStack.lastOrNull() ?: initialState.spaceId + val mutablePaginationStatus = state.paginationStatus.toMutableMap().apply { + if (this[currentRootId] == null) { + this[currentRootId] = Uninitialized + } + } + + if (mutablePaginationStatus[currentRootId] is Loading) return@withState + + setState { + copy(paginationStatus = mutablePaginationStatus.toMap()) + } + + viewModelScope.launch(Dispatchers.IO) { + val cachedResults = state.apiResults.toMutableMap() + try { + val currentResponse = cachedResults[currentRootId]?.invoke() + if (currentResponse == null) { + // nothing to paginate through... + setState { + copy(paginationStatus = mutablePaginationStatus.apply { this[currentRootId] = Uninitialized }.toMap()) + } + return@launch + } + val query = session.spaceService().querySpaceChildren( + currentRootId, + limit = 10, + from = currentResponse.nextToken, + knownStateList = currentResponse.childrenState + ) + val knownSummaries = query.children.mapNotNull { + session.getRoomSummary(it.childRoomId) + ?.takeIf { it.membership == Membership.JOIN } // only take if joined because it will be up to date (synced) + }.distinctBy { it.roomId } + + cachedResults[currentRootId] = Success( + currentResponse.copy( + children = currentResponse.children + query.children, + nextToken = query.nextToken, + ) + ) + setState { + copy( + apiResults = cachedResults.toMap(), + paginationStatus = mutablePaginationStatus.apply { this[currentRootId] = Success(Unit) }.toMap(), + knownRoomSummaries = (state.knownRoomSummaries + knownSummaries).distinctBy { it.roomId } + ) + } + } catch (failure: Throwable) { + setState { + copy( + paginationStatus = mutablePaginationStatus.apply { this[currentRootId] = Fail(failure) }.toMap() + ) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt index d1e1d8f0fb1..6db6bb4b469 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt @@ -50,7 +50,7 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( viewModelScope.launch(Dispatchers.IO) { val apiResult = runCatchingToAsync { - session.spaceService().querySpaceChildren(spaceId = initialState.spaceId).second + session.spaceService().querySpaceChildren(spaceId = initialState.spaceId).children } setState { copy( @@ -131,8 +131,8 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( roomId = info.childRoomId, viaServers = info.viaServers, order = info.order, - suggested = suggested, - autoJoin = info.autoJoin + suggested = suggested +// autoJoin = info.autoJoin ) } catch (failure: Throwable) { errorList.add(failure) @@ -156,7 +156,7 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( } viewModelScope.launch(Dispatchers.IO) { val apiResult = runCatchingToAsync { - session.spaceService().querySpaceChildren(spaceId = initialState.spaceId).second + session.spaceService().querySpaceChildren(spaceId = initialState.spaceId).children } setState { copy( diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt index 61328d2a1c8..0f1afd8371a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt @@ -151,7 +151,7 @@ class SpacePreviewViewModel @AssistedInject constructor( setState { copy( spaceInfo = Success( - resolveResult.first.let { + resolveResult.rootSummary.let { ChildInfo( roomId = it.roomId, avatarUrl = it.avatarUrl, @@ -165,7 +165,7 @@ class SpacePreviewViewModel @AssistedInject constructor( } ), childInfoList = Success( - resolveResult.second.map { + resolveResult.children.map { ChildInfo( roomId = it.childRoomId, avatarUrl = it.avatarUrl, From c01b36a648d53cee270a79d0dcb1f3d4667d54ac Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 31 Aug 2021 15:22:27 +0200 Subject: [PATCH 2/7] Support summary pagination API in manage space --- .../spaces/explore/SpaceDirectoryFragment.kt | 1 - .../spaces/explore/SpaceDirectoryViewModel.kt | 4 +- .../manage/SpaceManageRoomViewAction.kt | 1 + .../spaces/manage/SpaceManageRoomViewState.kt | 7 +- .../manage/SpaceManageRoomsController.kt | 28 ++++++- .../spaces/manage/SpaceManageRoomsFragment.kt | 10 ++- .../manage/SpaceManageRoomsViewModel.kt | 84 +++++++++++++++---- 7 files changed, 110 insertions(+), 25 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt index 9714cebd4ec..63813042b21 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt @@ -213,5 +213,4 @@ class SpaceDirectoryFragment @Inject constructor( // nothing? return false } - } diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt index 578223cd36c..1006f5a5702 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryViewModel.kt @@ -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 { diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewAction.kt index c232377abac..d11155f3655 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewAction.kt @@ -25,4 +25,5 @@ sealed class SpaceManageRoomViewAction : VectorViewModelAction { data class MarkAllAsSuggested(val suggested: Boolean) : SpaceManageRoomViewAction() object BulkRemove : SpaceManageRoomViewAction() object RefreshFromServer : SpaceManageRoomViewAction() + object LoadAdditionalItemsIfNeeded : SpaceManageRoomViewAction() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt index 7bae216b10c..9b0256e9518 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt @@ -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 = Uninitialized, - val childrenInfo: Async> = Uninitialized, + val childrenInfo: Async = Uninitialized, val selectedRooms: List = emptyList(), val currentFilter: String = "", - val actionState: Async = Uninitialized + val actionState: Async = Uninitialized, + val paginationStatus: Async = Uninitialized ) : MvRxState { constructor(args: SpaceManageArgs) : this( spaceId = args.spaceId diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt index 0374f2518fc..ffd5743a10d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt @@ -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() + } + } + } + } + } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt index 8bc66e7232c..186d7339822 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsFragment.kt @@ -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 diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt index 6db6bb4b469..18eb24231df 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt @@ -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(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() } } @@ -77,22 +73,25 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( override fun handle(action: SpaceManageRoomViewAction) { when (action) { - is SpaceManageRoomViewAction.ToggleSelection -> handleToggleSelection(action) - is SpaceManageRoomViewAction.UpdateFilter -> { + is SpaceManageRoomViewAction.ToggleSelection -> handleToggleSelection(action) + is SpaceManageRoomViewAction.UpdateFilter -> { setState { copy(currentFilter = action.filter) } } - SpaceManageRoomViewAction.ClearSelection -> { + SpaceManageRoomViewAction.ClearSelection -> { setState { copy(selectedRooms = emptyList()) } } - SpaceManageRoomViewAction.BulkRemove -> { + SpaceManageRoomViewAction.BulkRemove -> { handleBulkRemove() } - is SpaceManageRoomViewAction.MarkAllAsSuggested -> { + is SpaceManageRoomViewAction.MarkAllAsSuggested -> { handleBulkMarkAsSuggested(action.suggested) } - SpaceManageRoomViewAction.RefreshFromServer -> { + 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,13 +155,64 @@ 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) + ) + } + } } } From acf7a226f0fa05cc0d16fc901fe4e8e76b292381 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 31 Aug 2021 15:39:22 +0200 Subject: [PATCH 3/7] Fix / summary showing roomIds instead of names --- .../spaces/manage/SpaceManageRoomViewState.kt | 4 +++- .../spaces/manage/SpaceManageRoomsController.kt | 5 ++++- .../spaces/manage/SpaceManageRoomsViewModel.kt | 13 ++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt index 9b0256e9518..064a561aa0d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt @@ -29,7 +29,9 @@ data class SpaceManageRoomViewState( val selectedRooms: List = emptyList(), val currentFilter: String = "", val actionState: Async = Uninitialized, - val paginationStatus: Async = Uninitialized + val paginationStatus: Async = Uninitialized, + // cached room summaries of known rooms, we use it because computed room name would be better using it + val knownRoomSummaries : List = emptyList() ) : MvRxState { constructor(args: SpaceManageArgs) : this( spaceId = args.spaceId diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt index ffd5743a10d..dadcf9d8267 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt @@ -80,7 +80,10 @@ class SpaceManageRoomsController @Inject constructor( filteredResult.forEach { childInfo -> roomManageSelectionItem { id(childInfo.childRoomId) - matrixItem(childInfo.toMatrixItem()) + matrixItem( + data.knownRoomSummaries.firstOrNull { it.roomId == childInfo.childRoomId }?.toMatrixItem() + ?: childInfo.toMatrixItem() + ) avatarRenderer(host.avatarRenderer) suggested(childInfo.suggested ?: false) selected(data.selectedRooms.contains(childInfo.childRoomId)) diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt index 18eb24231df..0222dde275a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt @@ -51,9 +51,7 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( ) } - viewModelScope.launch(Dispatchers.IO) { - refreshSummaryAPI() - } + refreshSummaryAPI() } @AssistedFactory @@ -163,7 +161,10 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( setState { copy( childrenInfo = apiResult, - paginationStatus = Uninitialized + paginationStatus = Uninitialized, + knownRoomSummaries = apiResult.invoke()?.children?.mapNotNull { + session.getRoomSummary(it.childRoomId) + }.orEmpty() ) } } @@ -195,6 +196,7 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( knownStateList = knownResults.childrenState.orEmpty(), limit = paginationLimit ) + val newKnown = apiResult.children.mapNotNull { session.getRoomSummary(it.childRoomId) } setState { copy( childrenInfo = Success( @@ -203,7 +205,8 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( nextToken = apiResult.nextToken ) ), - paginationStatus = Success(Unit) + paginationStatus = Success(Unit), + knownRoomSummaries = (state.knownRoomSummaries + newKnown).distinctBy { it.roomId } ) } } catch (failure: Throwable) { From 0bcd3636770386e8ffc290fb4bc432945d44c3ce Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 31 Aug 2021 15:49:28 +0200 Subject: [PATCH 4/7] Cleaning --- .../session/space/SpaceHierarchySummary.kt | 4 ++-- .../session/space/SpaceChildSummaryResponse.kt | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/{internal => api}/session/space/SpaceHierarchySummary.kt (90%) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceHierarchySummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchySummary.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceHierarchySummary.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchySummary.kt index bb362b416b9..481d6867ca3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceHierarchySummary.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchySummary.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 New Vector Ltd + * Copyright 2021 The Matrix.org Foundation C.I.C. * * 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 org.matrix.android.sdk.internal.session.space +package org.matrix.android.sdk.api.session.space import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.RoomSummary diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt index 74faa2653aa..7188e77c4f6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt @@ -23,16 +23,12 @@ import org.matrix.android.sdk.api.session.events.model.Event /** * The fields are the same as those returned by /publicRooms (see spec), with the addition of: * room_type: the value of the m.type field from the room's m.room.create event, if any. - * children_state: The m.space.child events of the room. For each event, only the following fields are included1: type, state_key, content, room_id, sender, with the addition of: - * origin_server_ts: This is required for sorting of rooms as specified below. + * children_state: The m.space.child events of the room. + * For each event, only the following fields are included1: type, state_key, content, room_id, sender, + * with the addition of: origin_server_ts: This is required for sorting of rooms as specified below. */ @JsonClass(generateAdapter = true) internal data class SpaceChildSummaryResponse( -// /** -// * The total number of state events which point to or from this room (inbound/outbound edges). -// * This includes all m.space.child events in the room, in addition to m.room.parent events which point to this room as a parent. -// */ -// @Json(name = "num_refs") val numRefs: Int? = null, /** * The room type, which is m.space for subspaces. @@ -41,7 +37,7 @@ internal data class SpaceChildSummaryResponse( @Json(name = "room_type") val roomType: String? = null, /** The m.space.child events of the room. For each event, only the following fields are included: - * type, state_key, content, room_id, sender, with the addition of origin_server_ts: This is required for sorting of rooms as specified below. + * type, state_key, content, room_id, sender, with the addition of origin_server_ts. */ @Json(name = "children_state") val childrenState: List? = null, From 97114b4f480060a4a1c6eedc3fb91e2f8dbbd78a Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 31 Aug 2021 15:59:24 +0200 Subject: [PATCH 5/7] cleaning --- .../session/space/ResolveSpaceInfoTask.kt | 15 +-------------- .../sdk/internal/session/space/SpaceApi.kt | 18 ------------------ 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/ResolveSpaceInfoTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/ResolveSpaceInfoTask.kt index 0d305fcff47..2a396d6ee7b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/ResolveSpaceInfoTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/ResolveSpaceInfoTask.kt @@ -24,25 +24,12 @@ import javax.inject.Inject internal interface ResolveSpaceInfoTask : Task { data class Params( val spaceId: String, -// val maxRoomPerSpace: Int?, val limit: Int?, val maxDepth: Int?, val from: String?, val suggestedOnly: Boolean? // val autoJoinOnly: Boolean? - ) { -// companion object { -// fun withId(spaceId: String, suggestedOnly: Boolean?) = -// Params( -// spaceId = spaceId, -// // maxRoomPerSpace = 10, -// limit = 20, -// from = null, -// suggestedOnly = suggestedOnly -// // autoJoinOnly = autoJoinOnly -// ) -// } - } + ) } internal class DefaultResolveSpaceInfoTask @Inject constructor( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt index 4d3de2c8a02..43958fd5565 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt @@ -23,24 +23,6 @@ import retrofit2.http.Query internal interface SpaceApi { - /** - * - * POST /_matrix/client/r0/rooms/{roomID}/spaces - * { - * "max_rooms_per_space": 5, // The maximum number of rooms/subspaces to return for a given space, if negative unbounded. default: -1. - * "auto_join_only": true, // If true, only return m.space.child events with auto_join:true, default: false, which returns all events. - * "limit": 100, // The maximum number of rooms/subspaces to return, server can override this, default: 100. - * "batch": "opaque_string" // A token to use if this is a subsequent HTTP hit, default: "". - * } - * - * Ref: - * - MSC 2946 https://github.com/matrix-org/matrix-doc/blob/kegan/spaces-summary/proposals/2946-spaces-summary.md - * - https://hackmd.io/fNYh4tjUT5mQfR1uuRzWDA - */ -// @POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/spaces") -// suspend fun getSpaces(@Path("roomId") spaceId: String, -// @Body params: SpaceSummaryParams): SpacesResponse - /** * @param limit: Optional: a client-defined limit to the maximum number of rooms to return per page. Must be a non-negative integer. * @param max_depth: Optional: The maximum depth in the tree (from the root room) to return. From 9687a5937126a32da514d83a35854c91013fced2 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 2 Sep 2021 11:31:13 +0200 Subject: [PATCH 6/7] Code review --- .../{SpaceHierarchySummary.kt => SpaceHierarchyData.kt} | 2 +- .../matrix/android/sdk/api/session/space/SpaceService.kt | 4 +--- .../sdk/internal/session/space/DefaultSpaceService.kt | 5 +++-- .../matrix/android/sdk/internal/session/space/SpaceApi.kt | 4 ++-- .../sdk/internal/session/space/SpaceChildSummaryResponse.kt | 2 +- .../im/vector/app/features/matrixto/SpaceCardRenderer.kt | 2 +- .../app/features/spaces/explore/SpaceDirectoryController.kt | 2 +- .../app/features/spaces/explore/SpaceDirectoryState.kt | 6 +++--- .../app/features/spaces/manage/SpaceManageRoomViewState.kt | 6 +++--- .../features/spaces/manage/SpaceManageRoomsController.kt | 2 +- 10 files changed, 17 insertions(+), 18 deletions(-) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/{SpaceHierarchySummary.kt => SpaceHierarchyData.kt} (96%) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchySummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchyData.kt similarity index 96% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchySummary.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchyData.kt index 481d6867ca3..ecc3eb52248 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchySummary.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceHierarchyData.kt @@ -20,7 +20,7 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo -data class SpaceHierarchySummary( +data class SpaceHierarchyData( val rootSummary: RoomSummary, val children: List, val childrenState: List, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt index 5a8a0398f70..bcc36b579a3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt @@ -21,7 +21,6 @@ import androidx.lifecycle.LiveData import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams import org.matrix.android.sdk.api.session.room.model.RoomSummary -import org.matrix.android.sdk.internal.session.space.SpaceHierarchySummary import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult typealias SpaceSummaryQueryParams = RoomSummaryQueryParams @@ -61,7 +60,6 @@ interface SpaceService { * Get's information of a space by querying the server * @param suggestedOnly If true, return only child events and rooms where the m.space.child event has suggested: true. * @param limit a client-defined limit to the maximum number of rooms to return per page. Must be a non-negative integer. - * @param maxDepth: Optional: The maximum depth in the tree (from the root room) to return. * @param from: Optional. Pagination token given to retrieve the next set of rooms. Note that if a pagination token is provided, * then the parameters given for suggested_only and max_depth must be the same. */ @@ -70,7 +68,7 @@ interface SpaceService { limit: Int? = null, from: String? = null, // when paginating, pass back the m.space.child state events - knownStateList: List? = null): SpaceHierarchySummary + knownStateList: List? = null): SpaceHierarchyData /** * Get a live list of space summaries. This list is refreshed as soon as the data changes. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index e23179b9e82..7be4cdcda95 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -36,6 +36,7 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.space.CreateSpaceParams import org.matrix.android.sdk.api.session.space.JoinSpaceResult import org.matrix.android.sdk.api.session.space.Space +import org.matrix.android.sdk.api.session.space.SpaceHierarchyData import org.matrix.android.sdk.api.session.space.SpaceService import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams import org.matrix.android.sdk.api.session.space.model.SpaceChildContent @@ -112,7 +113,7 @@ internal class DefaultSpaceService @Inject constructor( suggestedOnly: Boolean?, limit: Int?, from: String?, - knownStateList: List?): SpaceHierarchySummary { + knownStateList: List?): SpaceHierarchyData { return resolveSpaceInfoTask.execute( ResolveSpaceInfoTask.Params( spaceId = spaceId, limit = limit, maxDepth = 1, from = from, suggestedOnly = suggestedOnly @@ -164,7 +165,7 @@ internal class DefaultSpaceService @Inject constructor( }.orEmpty() } .orEmpty() - SpaceHierarchySummary( + SpaceHierarchyData( rootSummary = root, children = children, childrenState = spaceDesc?.childrenState.orEmpty(), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt index 43958fd5565..edd10bc2ef9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceApi.kt @@ -24,10 +24,10 @@ import retrofit2.http.Query internal interface SpaceApi { /** + * @param suggestedOnly Optional. If true, return only child events and rooms where the m.space.child event has suggested: true. * @param limit: Optional: a client-defined limit to the maximum number of rooms to return per page. Must be a non-negative integer. - * @param max_depth: Optional: The maximum depth in the tree (from the root room) to return. + * @param maxDepth: Optional: The maximum depth in the tree (from the root room) to return. * The deepest depth returned will not include children events. Defaults to no-limit. Must be a non-negative integer. - * * @param from: Optional. Pagination token given to retrieve the next set of rooms. * Note that if a pagination token is provided, then the parameters given for suggested_only and max_depth must be the same. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt index 7188e77c4f6..e0f273d0c25 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceChildSummaryResponse.kt @@ -24,7 +24,7 @@ import org.matrix.android.sdk.api.session.events.model.Event * The fields are the same as those returned by /publicRooms (see spec), with the addition of: * room_type: the value of the m.type field from the room's m.room.create event, if any. * children_state: The m.space.child events of the room. - * For each event, only the following fields are included1: type, state_key, content, room_id, sender, + * For each event, only the following fields are included: type, state_key, content, room_id, sender, * with the addition of: origin_server_ts: This is required for sorting of rooms as specified below. */ @JsonClass(generateAdapter = true) diff --git a/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt b/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt index 88d65e23b16..00d107513c7 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt @@ -62,7 +62,7 @@ class SpaceCardRenderer @Inject constructor( inCard.matrixToAccessImage.isVisible = true inCard.matrixToAccessImage.setImageResource(R.drawable.ic_room_private) } - val memberCount = spaceSummary.joinedMembersCount?.let { it - 1 } ?: 0 + val memberCount = spaceSummary.joinedMembersCount ?: 0 if (memberCount != 0) { inCard.matrixToMemberPills.isVisible = true inCard.spaceChildMemberCountText.text = stringProvider.getQuantityString(R.plurals.room_title_members, memberCount, memberCount) diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt index c76eec257b0..4aa4256857d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryController.kt @@ -175,7 +175,7 @@ class SpaceDirectoryController @Inject constructor( errorWithRetryItem { id("error_${currentRootId}_${hierarchySummary.nextToken}") text(host.errorFormatter.toHumanReadable(paginationStatus.error)) - listener { host.listener?.retry() } + listener { host.listener?.loadAdditionalItemsIfNeeded() } } } else { loadingItem { diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt index f762ac7629d..33b494075d5 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryState.kt @@ -21,13 +21,13 @@ import com.airbnb.mvrx.MvRxState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState 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 +import org.matrix.android.sdk.api.session.space.SpaceHierarchyData data class SpaceDirectoryState( // The current filter val spaceId: String, val currentFilter: String = "", - val apiResults: Map> = emptyMap(), + val apiResults: Map> = emptyMap(), val currentRootSummary: RoomSummary? = null, val childList: List = emptyList(), val hierarchyStack: List = emptyList(), @@ -37,7 +37,7 @@ data class SpaceDirectoryState( val changeMembershipStates: Map = emptyMap(), val canAddRooms: Boolean = false, // cached room summaries of known rooms, we use it because computed room name would be better using it - val knownRoomSummaries : List = emptyList(), + val knownRoomSummaries: List = emptyList(), val paginationStatus: Map> = emptyMap() ) : MvRxState { constructor(args: SpaceDirectoryArgs) : this( diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt index 064a561aa0d..34173828a71 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomViewState.kt @@ -20,18 +20,18 @@ 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.internal.session.space.SpaceHierarchySummary +import org.matrix.android.sdk.api.session.space.SpaceHierarchyData data class SpaceManageRoomViewState( val spaceId: String, val spaceSummary: Async = Uninitialized, - val childrenInfo: Async = Uninitialized, + val childrenInfo: Async = Uninitialized, val selectedRooms: List = emptyList(), val currentFilter: String = "", val actionState: Async = Uninitialized, val paginationStatus: Async = Uninitialized, // cached room summaries of known rooms, we use it because computed room name would be better using it - val knownRoomSummaries : List = emptyList() + val knownRoomSummaries: List = emptyList() ) : MvRxState { constructor(args: SpaceManageArgs) : this( spaceId = args.spaceId diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt index dadcf9d8267..f9dfec8f404 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsController.kt @@ -101,7 +101,7 @@ class SpaceManageRoomsController @Inject constructor( errorWithRetryItem { id("error_$nextToken") text(host.errorFormatter.toHumanReadable(paginationStatus.error)) - listener { host.listener?.retry() } + listener { host.listener?.loadAdditionalItemsIfNeeded() } } } else { loadingItem { From 1c7e5671874be97558bf2ef5faf4f6c695903311 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 2 Sep 2021 11:56:16 +0200 Subject: [PATCH 7/7] Fix test compilation (autojoin descoped) --- .../sdk/session/space/SpaceCreationTest.kt | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt index a1744a0dae2..b635ca15711 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceCreationTest.kt @@ -28,12 +28,9 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest -import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams import org.matrix.android.sdk.api.session.room.model.GuestAccess -import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility @@ -42,7 +39,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent -import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.space.JoinSpaceResult import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.SessionTestParams @@ -162,7 +158,7 @@ class SpaceCreationTest : InstrumentedTest { commonTestHelper.waitWithLatch { GlobalScope.launch { - syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", true, suggested = true) + syncedSpace?.addChildren(firstChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "a", suggested = true) it.countDown() } } @@ -181,7 +177,7 @@ class SpaceCreationTest : InstrumentedTest { commonTestHelper.waitWithLatch { GlobalScope.launch { - syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", false, suggested = true) + syncedSpace?.addChildren(secondChild!!, listOf(aliceSession.sessionParams.homeServerHost ?: ""), "b", suggested = true) it.countDown() } } @@ -202,19 +198,20 @@ class SpaceCreationTest : InstrumentedTest { assertEquals("Room name should be set", roomName, spaceBobPov?.asRoom()?.roomSummary()?.name) assertEquals("Room topic should be set", topic, spaceBobPov?.asRoom()?.roomSummary()?.topic) + // /!\ AUTO_JOIN has been descoped // check if bob has joined automatically the first room - val bobMembershipFirstRoom = bobSession.getRoomSummary(firstChild!!)?.membership - assertEquals("Bob should have joined this room", Membership.JOIN, bobMembershipFirstRoom) - RoomSummaryQueryParams.Builder() - - val childCount = bobSession.getRoomSummaries( - roomSummaryQueryParams { - activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(spaceId) - } - ).size - - assertEquals("Unexpected number of joined children", 1, childCount) +// val bobMembershipFirstRoom = bobSession.getRoomSummary(firstChild!!)?.membership +// assertEquals("Bob should have joined this room", Membership.JOIN, bobMembershipFirstRoom) +// RoomSummaryQueryParams.Builder() +// +// val childCount = bobSession.getRoomSummaries( +// roomSummaryQueryParams { +// activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(spaceId) +// } +// ).size +// +// assertEquals("Unexpected number of joined children", 1, childCount) commonTestHelper.signOutAndClose(aliceSession) commonTestHelper.signOutAndClose(bobSession)