From 0d3c2b4bef7888c8f6da909d03208d8661794fc0 Mon Sep 17 00:00:00 2001 From: Valere Date: Sun, 11 Apr 2021 23:17:45 +0200 Subject: [PATCH] Fix space filtering --- .../sdk/api/query/ActiveSpaceFilter.kt | 18 +- .../android/sdk/api/session/room/Room.kt | 1 - .../sdk/api/session/room/RoomService.kt | 4 +- .../session/room/RoomSummaryQueryParams.kt | 12 +- ...geResult.kt => UpdatableLivePageResult.kt} | 4 +- .../android/sdk/api/session/space/Space.kt | 2 - .../database/RealmSessionStoreMigration.kt | 2 + .../database/model/RoomSummaryEntity.kt | 7 +- .../sdk/internal/session/room/DefaultRoom.kt | 3 - .../session/room/DefaultRoomService.kt | 6 +- .../session/room/summary/GrapUtils.kt | 166 +++++++++++++ .../room/summary/RoomSummaryDataSource.kt | 48 ++-- .../room/summary/RoomSummaryUpdater.kt | 167 ++++++++++--- .../internal/session/space/DefaultSpace.kt | 2 - .../session/space/DefaultSpaceService.kt | 2 +- .../session/space/ResolveSpaceInfoTask.kt | 2 +- .../sdk/internal/session/space/SpaceApi.kt | 5 +- .../internal/session/sync/RoomSyncHandler.kt | 5 +- .../session/sync/SyncResponseHandler.kt | 5 + .../sdk/internal/util/GraphUtilsTest.kt | 111 +++++++++ vector/build.gradle | 1 + .../java/im/vector/app/AppStateHandler.kt | 18 +- .../im/vector/app/core/di/VectorComponent.kt | 5 +- .../app/core/ui/list/GenericFooterItem.kt | 1 - .../vector/app/features/home/HomeActivity.kt | 8 +- .../app/features/home/HomeDetailViewModel.kt | 7 +- .../home/room/list/RoomListListener.kt | 2 + .../home/room/list/RoomListViewModel.kt | 220 ++++++++++++++++-- .../room/list/RoomListViewModelFactory.kt | 5 +- .../home/room/list/RoomListViewState.kt | 10 +- .../features/navigation/DefaultNavigator.kt | 10 +- .../features/spaces/SpacesListViewModel.kt | 8 +- .../spaces/create/ChooseSpaceTypeFragment.kt | 4 +- 33 files changed, 739 insertions(+), 132 deletions(-) rename vector/src/main/java/im/vector/app/features/grouplist/SelectedSpaceDataSource.kt => matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/ActiveSpaceFilter.kt (54%) rename matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/{UpdatableFilterLivePageResult.kt => UpdatableLivePageResult.kt} (88%) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/GrapUtils.kt create mode 100644 matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/util/GraphUtilsTest.kt diff --git a/vector/src/main/java/im/vector/app/features/grouplist/SelectedSpaceDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/ActiveSpaceFilter.kt similarity index 54% rename from vector/src/main/java/im/vector/app/features/grouplist/SelectedSpaceDataSource.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/ActiveSpaceFilter.kt index e8293d7e99..3c716fe0fe 100644 --- a/vector/src/main/java/im/vector/app/features/grouplist/SelectedSpaceDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/query/ActiveSpaceFilter.kt @@ -1,11 +1,11 @@ /* - * Copyright 2019 New Vector Ltd + * Copyright (c) 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. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -14,13 +14,9 @@ * limitations under the License. */ -package im.vector.app.features.grouplist +package org.matrix.android.sdk.api.query -import arrow.core.Option -import im.vector.app.core.utils.BehaviorDataSource -import org.matrix.android.sdk.api.session.room.model.RoomSummary -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SelectedSpaceDataSource @Inject constructor() : BehaviorDataSource>(Option.empty()) +sealed class ActiveSpaceFilter { + object None : ActiveSpaceFilter() + data class ActiveSpace(val currentSpaceId: String?) : ActiveSpaceFilter() +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt index 6784df565f..f3eeb902a8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/Room.kt @@ -35,7 +35,6 @@ import org.matrix.android.sdk.api.session.room.typing.TypingService import org.matrix.android.sdk.api.session.room.uploads.UploadsService import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.api.session.space.Space -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Optional /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index 982a67c4c2..092c31de1d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -184,7 +184,7 @@ interface RoomService { * TODO Doc */ fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, - pagedListConfig: PagedList.Config = defaultPagedListConfig): UpdatableFilterLivePageResult + pagedListConfig: PagedList.Config = defaultPagedListConfig): UpdatableLivePageResult /** * TODO Doc @@ -199,7 +199,7 @@ interface RoomService { .setPrefetchDistance(10) .build() - fun getFlattenRoomSummaryChildOf(spaceId: String?, memberships: List = Membership.activeMemberships()) : List + fun getFlattenRoomSummaryChildrenOf(spaceId: String?, memberships: List = Membership.activeMemberships()) : List /** * Returns all the children of this space, as LiveData diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt index f50d8f76ee..0b1db09773 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomSummaryQueryParams.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.api.session.room +import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.query.RoomTagQueryFilter @@ -54,9 +55,10 @@ data class RoomSummaryQueryParams( val canonicalAlias: QueryStringValue, val memberships: List, val roomCategoryFilter: RoomCategoryFilter?, - val roomTagQueryFilter: RoomTagQueryFilter? + val roomTagQueryFilter: RoomTagQueryFilter?, val excludeType: List?, - val includeType: List? + val includeType: List?, + val activeSpaceId: ActiveSpaceFilter? ) { class Builder { @@ -67,8 +69,9 @@ data class RoomSummaryQueryParams( var memberships: List = Membership.all() var roomCategoryFilter: RoomCategoryFilter? = RoomCategoryFilter.ALL var roomTagQueryFilter: RoomTagQueryFilter? = null - var excludeType: List = listOf(RoomType.SPACE) + var excludeType: List? = listOf(RoomType.SPACE) var includeType: List? = null + var activeSpaceId: ActiveSpaceFilter = ActiveSpaceFilter.None fun build() = RoomSummaryQueryParams( roomId = roomId, @@ -78,7 +81,8 @@ data class RoomSummaryQueryParams( roomCategoryFilter = roomCategoryFilter, roomTagQueryFilter = roomTagQueryFilter, excludeType = excludeType, - includeType = includeType + includeType = includeType, + activeSpaceId = activeSpaceId ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableFilterLivePageResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableLivePageResult.kt similarity index 88% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableFilterLivePageResult.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableLivePageResult.kt index 71b3c665e7..3bcca0c241 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableFilterLivePageResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/UpdatableLivePageResult.kt @@ -20,8 +20,8 @@ import androidx.lifecycle.LiveData import androidx.paging.PagedList import org.matrix.android.sdk.api.session.room.model.RoomSummary -interface UpdatableFilterLivePageResult { +interface UpdatableLivePageResult { val livePagedList: LiveData> - fun updateQuery(queryParams: RoomSummaryQueryParams) + fun updateQuery(builder: (RoomSummaryQueryParams) -> RoomSummaryQueryParams) } 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 8180189079..9dba4f90af 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 @@ -16,10 +16,8 @@ package org.matrix.android.sdk.api.session.space -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.RoomSummary -import org.matrix.android.sdk.api.util.Cancelable interface Space { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt index 48479fd746..5e126a1536 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.database import io.realm.DynamicRealm +import io.realm.FieldAttribute import io.realm.RealmMigration import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields @@ -216,6 +217,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration { realm.schema.get("RoomSummaryEntity") ?.addField(RoomSummaryEntityFields.ROOM_TYPE, String::class.java) + ?.addField(RoomSummaryEntityFields.FLATTEN_PARENT_IDS, String::class.java) ?.transform { obj -> // Should I put messaging type here? obj.setString(RoomSummaryEntityFields.ROOM_TYPE, null) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt index 85e4595da5..0adcec067e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomSummaryEntity.kt @@ -27,7 +27,7 @@ import org.matrix.android.sdk.api.session.room.model.VersioningState import org.matrix.android.sdk.api.session.room.model.tag.RoomTag internal open class RoomSummaryEntity( - @PrimaryKey var roomId: String = "" + @PrimaryKey var roomId: String = "", var roomType: String? = null, var parents: RealmList = RealmList(), var children: RealmList = RealmList() @@ -207,6 +207,11 @@ internal open class RoomSummaryEntity( if (value != field) field = value } + var flattenParentIds: String? = null + set(value) { + if (value != field) field = value + } + @Index private var membershipStr: String = Membership.NONE.name diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt index 1c22faa7b8..c6059f84ea 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoom.kt @@ -38,15 +38,12 @@ import org.matrix.android.sdk.api.session.room.typing.TypingService import org.matrix.android.sdk.api.session.room.uploads.UploadsService import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.api.session.space.Space -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.session.room.state.SendStateTask import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource import org.matrix.android.sdk.internal.session.search.SearchTask import org.matrix.android.sdk.internal.session.space.DefaultSpace -import org.matrix.android.sdk.internal.task.TaskExecutor -import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.util.awaitCallback import java.security.InvalidParameterException import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index 070270aac9..77469ddb40 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -24,7 +24,7 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.RoomService import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams -import org.matrix.android.sdk.api.session.room.UpdatableFilterLivePageResult +import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary @@ -105,8 +105,8 @@ internal class DefaultRoomService @Inject constructor( } override fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config) - : UpdatableFilterLivePageResult { - return roomSummaryDataSource.getFilteredPagedRoomSummariesLive(queryParams, pagedListConfig) + : UpdatableLivePageResult { + return roomSummaryDataSource.getUpdatablePagedRoomSummariesLive(queryParams, pagedListConfig) } override fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/GrapUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/GrapUtils.kt new file mode 100644 index 0000000000..c1c40c1be1 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/GrapUtils.kt @@ -0,0 +1,166 @@ +/* + * Copyright (c) 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. + * 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.room.summary + +import java.util.LinkedList + +data class GraphNode( + val name: String +) + +data class GraphEdge( + val source: GraphNode, + val destination: GraphNode +) + +class Graph { + + private val adjacencyList: HashMap> = HashMap() + + fun getOrCreateNode(name: String): GraphNode { + return adjacencyList.entries.firstOrNull { it.key.name == name }?.key + ?: GraphNode(name).also { + adjacencyList[it] = ArrayList() + } + } + + fun addEdge(sourceName: String, destinationName: String) { + val source = getOrCreateNode(sourceName) + val destination = getOrCreateNode(destinationName) + adjacencyList.getOrPut(source) { ArrayList() }.add( + GraphEdge(source, destination) + ) + } + + fun addEdge(source: GraphNode, destination: GraphNode) { + adjacencyList.getOrPut(source) { ArrayList() }.add( + GraphEdge(source, destination) + ) + } + + fun edgesOf(node: GraphNode): List { + return adjacencyList[node]?.toList() ?: emptyList() + } + + fun withoutEdges(edgesToPrune: List): Graph { + val output = Graph() + this.adjacencyList.forEach { (vertex, edges) -> + output.getOrCreateNode(vertex.name) + edges.forEach { + if (!edgesToPrune.contains(it)) { + // add it + output.addEdge(it.source, it.destination) + } + } + } + return output + } + + /** + * Depending on the chosen starting point the background edge might change + */ + fun findBackwardEdges(startFrom: GraphNode? = null): List { + val backwardEdges = mutableSetOf() + val visited = mutableMapOf() + val notVisited = -1 + val inPath = 0 + val completed = 1 + adjacencyList.keys.forEach { + visited[it] = notVisited + } + val stack = LinkedList() + + (startFrom ?: adjacencyList.entries.firstOrNull { visited[it.key] == notVisited }?.key) + ?.let { + stack.push(it) + visited[it] = inPath + } + + while (stack.isNotEmpty()) { +// Timber.w("VAL: current stack: ${stack.reversed().joinToString { it.name }}") + val vertex = stack.peek() + // peek a path to follow + var destination: GraphNode? = null + edgesOf(vertex).forEach { + when (visited[it.destination]) { + notVisited -> { + // it's a candidate + destination = it.destination + } + inPath -> { + // Cycle!! + backwardEdges.add(it) + } + completed -> { + // dead end + } + } + } + if (destination == null) { + // dead end, pop + stack.pop().let { + visited[it] = completed + } + } else { + // go down this path + stack.push(destination) + visited[destination!!] = inPath + } + + if (stack.isEmpty()) { + // try to get another graph of forest? + adjacencyList.entries.firstOrNull { visited[it.key] == notVisited }?.key?.let { + stack.push(it) + visited[it] = inPath + } + } + } + + return backwardEdges.toList() + } + + /** + * Only call that on acyclic graph! + */ + fun flattenDestination(): Map> { + val result = HashMap>() + adjacencyList.keys.forEach { vertex -> + result[vertex] = flattenOf(vertex) + } + return result + } + + private fun flattenOf(node: GraphNode): Set { + val result = mutableSetOf() + val edgesOf = edgesOf(node) + result.addAll(edgesOf.map { it.destination }) + edgesOf.forEach { + result.addAll(flattenOf(it.destination)) + } + return result + } + + override fun toString(): String { + return buildString { + adjacencyList.forEach { (node, edges) -> + append("${node.name} : [") + append(edges.joinToString(" ") { it.destination.name }) + append("]\n") + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt index 335198af68..9e2ca600dc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryDataSource.kt @@ -25,19 +25,18 @@ import com.zhuinden.monarchy.Monarchy import io.realm.Realm import io.realm.RealmQuery import io.realm.Sort +import io.realm.kotlin.where +import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams -import org.matrix.android.sdk.api.session.room.UpdatableFilterLivePageResult -import io.realm.kotlin.where -import org.matrix.android.sdk.api.session.room.RoomCategoryFilter -import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams +import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.model.Membership 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.VersioningState -import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams +import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.toOptional @@ -174,8 +173,8 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat ) } - fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, - pagedListConfig: PagedList.Config): UpdatableFilterLivePageResult { + fun getUpdatablePagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, + pagedListConfig: PagedList.Config): UpdatableLivePageResult { val realmDataSourceFactory = monarchy.createDataSourceFactory { realm -> roomSummariesQuery(realm, queryParams) .sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) @@ -189,13 +188,12 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat LivePagedListBuilder(dataSourceFactory, pagedListConfig) ) - return object : UpdatableFilterLivePageResult { + return object : UpdatableLivePageResult { override val livePagedList: LiveData> = mapped - override fun updateQuery(queryParams: RoomSummaryQueryParams) { + override fun updateQuery(builder: (RoomSummaryQueryParams) -> RoomSummaryQueryParams) { realmDataSourceFactory.updateQuery { - roomSummariesQuery(it, queryParams) - .sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) + roomSummariesQuery(it, builder.invoke(queryParams)) } } } @@ -225,10 +223,10 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat queryParams.roomCategoryFilter?.let { when (it) { - RoomCategoryFilter.ONLY_DM -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, true) - RoomCategoryFilter.ONLY_ROOMS -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, false) + RoomCategoryFilter.ONLY_DM -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, true) + RoomCategoryFilter.ONLY_ROOMS -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, false) RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS -> query.greaterThan(RoomSummaryEntityFields.NOTIFICATION_COUNT, 0) - RoomCategoryFilter.ALL -> { + RoomCategoryFilter.ALL -> { // nop } } @@ -252,9 +250,27 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat query.equalTo(RoomSummaryEntityFields.ROOM_TYPE, it) } when (queryParams.roomCategoryFilter) { - RoomCategoryFilter.ONLY_DM -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, true) + RoomCategoryFilter.ONLY_DM -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, true) RoomCategoryFilter.ONLY_ROOMS -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, false) - RoomCategoryFilter.ALL -> Unit // nop + RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS -> query.greaterThan(RoomSummaryEntityFields.NOTIFICATION_COUNT, 0) + RoomCategoryFilter.ALL -> Unit // nop + } + + // Timber.w("VAL: activeSpaceId : ${queryParams.activeSpaceId}") + when (queryParams.activeSpaceId) { + is ActiveSpaceFilter.ActiveSpace -> { + // It's annoying but for now realm java does not support querying in primitive list :/ + // https://github.com/realm/realm-java/issues/5361 + if (queryParams.activeSpaceId.currentSpaceId == null) { + // orphan rooms + query.isNull(RoomSummaryEntityFields.FLATTEN_PARENT_IDS) + } else { + query.contains(RoomSummaryEntityFields.FLATTEN_PARENT_IDS, queryParams.activeSpaceId.currentSpaceId) + } + } + else -> { + // nop + } } return query } 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 e7cb9688dd..72bac7c12b 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 @@ -39,6 +39,7 @@ import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.EventEntityFields import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity +import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.model.SpaceChildSummaryEntity import org.matrix.android.sdk.internal.database.model.SpaceParentSummaryEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntity @@ -50,6 +51,7 @@ import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.database.query.whereType import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.extensions.clearWith +import org.matrix.android.sdk.internal.query.process import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper @@ -59,6 +61,7 @@ import org.matrix.android.sdk.internal.session.sync.model.RoomSyncSummary import org.matrix.android.sdk.internal.session.sync.model.RoomSyncUnreadNotifications import timber.log.Timber import javax.inject.Inject +import kotlin.system.measureTimeMillis internal class RoomSummaryUpdater @Inject constructor( @UserId private val userId: String, @@ -183,46 +186,144 @@ internal class RoomSummaryUpdater @Inject constructor( * Should be called at the end of the room sync, to check and validate all parent/child relations */ fun validateSpaceRelationship(realm: Realm) { - // Do level 0 stuffs + measureTimeMillis { + val lookupMap = realm.where(RoomSummaryEntity::class.java) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) + .equalTo(RoomSummaryEntityFields.IS_DIRECT, false) + // we order by roomID to be consistent when breaking parent/child cycles + .sort(RoomSummaryEntityFields.ROOM_ID) + .findAll().map { + it.flattenParentIds = null + it to emptyList().toMutableSet() + } + .toMap() - realm.where(RoomSummaryEntity::class.java).findAll().forEach { roomSummary -> - if (roomSummary.roomType == RoomType.SPACE) { - roomSummary.children.clearWith { it.deleteFromRealm() } - roomSummary.children.addAll( - RoomChildRelationInfo(realm, roomSummary.roomId).getDirectChildrenDescriptions() - .map { - Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with info $it") - realm.createObject().apply { - this.childRoomId = it.roomId - this.childSummaryEntity = RoomSummaryEntity.where(realm, it.roomId).findFirst() - this.order = it.order - this.autoJoin = it.autoJoin - this.viaServers.addAll(it.viaServers) -// this.level = 0 - }.also { - Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with children $it") + lookupMap.keys.forEach { lookedUp -> + if (lookedUp.roomType == RoomType.SPACE) { + // get childrens + + lookedUp.children.clearWith { it.deleteFromRealm() } + + RoomChildRelationInfo(realm, lookedUp.roomId).getDirectChildrenDescriptions().forEach { child -> + + lookedUp.children.add( + realm.createObject().apply { + this.childRoomId = child.roomId + this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst() + this.order = child.order + this.autoJoin = child.autoJoin + this.viaServers.addAll(child.viaServers) + } + ) + + RoomSummaryEntity.where(realm, child.roomId) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) + .findFirst() + ?.let { childSum -> + lookupMap.entries.firstOrNull { it.key.roomId == lookedUp.roomId }?.let { entry -> + if (entry.value.indexOfFirst { it.roomId == childSum.roomId } == -1) { + // add looked up as a parent + entry.value.add(childSum) + } } } - ) + } + } else { + lookedUp.parents.clearWith { it.deleteFromRealm() } + // can we check parent relations here?? + RoomChildRelationInfo(realm, lookedUp.roomId).getParentDescriptions() + .map { parentInfo -> + + lookedUp.parents.add( + realm.createObject().apply { + this.parentRoomId = parentInfo.roomId + this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst() + this.canonical = parentInfo.canonical + this.viaServers.addAll(parentInfo.viaServers) + } + ) + + RoomSummaryEntity.where(realm, parentInfo.roomId) + .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships()) + .findFirst() + ?.let { parentSum -> + if (lookupMap[parentSum]?.indexOfFirst { it.roomId == lookedUp.roomId } == -1) { + // add lookedup as a parent + lookupMap[parentSum]?.add(lookedUp) + } + } + } + } } - // check parents - roomSummary.parents.clearWith { it.deleteFromRealm() } - roomSummary.parents.addAll( - RoomChildRelationInfo(realm, roomSummary.roomId).getParentDescriptions() - .map { parentInfo -> - Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with parent info $parentInfo") - realm.createObject().apply { - this.parentRoomId = parentInfo.roomId - this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst() - this.canonical = parentInfo.canonical - this.viaServers.addAll(parentInfo.viaServers) -// this.level = 0 - }.also { - Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with parent $it") + // Simple algorithm to break cycles + // Need more work to decide how to break, probably need to be as consistent as possible + // and also find best way to root the tree + + val graph = Graph() + lookupMap + // focus only on spaces, as room are just leaf + .filter { it.key.roomType == RoomType.SPACE } + .forEach { (sum, children) -> + graph.getOrCreateNode(sum.roomId) + children.forEach { + graph.addEdge(it.roomId, sum.roomId) + } + } + + val backEdges = graph.findBackwardEdges() + Timber.v("## SPACES: Cycle detected = ${backEdges.isNotEmpty()}") + + // break cycles + backEdges.forEach { edge -> + lookupMap.entries.find { it.key.roomId == edge.source.name }?.let { + it.value.removeAll { it.roomId == edge.destination.name } + } + } + + val acyclicGraph = graph.withoutEdges(backEdges) +// Timber.v("## SPACES: acyclicGraph $acyclicGraph") + val flattenSpaceParents = acyclicGraph.flattenDestination().map { + it.key.name to it.value.map { it.name } + }.toMap() +// Timber.v("## SPACES: flattenSpaceParents ${flattenSpaceParents.map { it.key.name to it.value.map { it.name } }.joinToString("\n") { +// it.first + ": [" + it.second.joinToString(",") + "]" +// }}") + +// Timber.v("## SPACES: lookup map ${lookupMap.map { it.key.name to it.value.map { it.name } }.toMap()}") + + lookupMap.entries + .filter { it.key.roomType == RoomType.SPACE } + .forEach { entry -> + val parent = RoomSummaryEntity.where(realm, entry.key.roomId).findFirst() + if (parent != null) { +// Timber.v("## SPACES: check hierarchy of ${parent.name} id ${parent.roomId}") +// Timber.v("## SPACES: flat known parents of ${parent.name} are ${flattenSpaceParents[parent.roomId]}") + val flattenParentsIds = (flattenSpaceParents[parent.roomId] ?: emptyList()) + listOf(parent.roomId) +// Timber.v("## SPACES: flatten known parents of children of ${parent.name} are ${flattenParentsIds}") + entry.value.forEach { child -> + RoomSummaryEntity.where(realm, child.roomId).findFirst()?.let { childSum -> + + Timber.w("## SPACES: ${childSum.name} is ${childSum.roomId} fc: ${childSum.flattenParentIds}") +// var allParents = childSum.flattenParentIds ?: "" + if (childSum.flattenParentIds == null) childSum.flattenParentIds = "" + flattenParentsIds.forEach { + if (childSum.flattenParentIds?.contains(it) != true) { + childSum.flattenParentIds += "|$it" + } + } +// childSum.flattenParentIds = "$allParents|" + +// Timber.v("## SPACES: flatten of ${childSum.name} is ${childSum.flattenParentIds}") } } - ) + } + } + + // we need also to filter DMs... + // it's more annoying as based on if the other members belong the space or not + }.also { + Timber.v("## SPACES: Finish checking room hierarchy in $it ms") } } 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 c41ccc7af4..93cb9d9d34 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 @@ -16,7 +16,6 @@ package org.matrix.android.sdk.internal.session.space -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toContent @@ -25,7 +24,6 @@ import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.space.Space import org.matrix.android.sdk.api.session.space.model.SpaceChildContent -import org.matrix.android.sdk.api.util.Cancelable import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource internal class DefaultSpace( 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 a116d4fe15..892cfbbd0e 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 @@ -169,7 +169,7 @@ internal class DefaultSpaceService @Inject constructor( stateKey = QueryStringValue.NoCondition ) val powerLevelsContent = powerLevelsEvent?.content?.toModel() - ?: throw UnsupportedOperationException("Cannot add canonical child, not enough power level") + ?: throw UnsupportedOperationException("Cannot add canonical child, missing powerlevel") val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) if (!powerLevelsHelper.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) { throw UnsupportedOperationException("Cannot add canonical child, not enough power level") 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 9bf6fa0256..d2be49b70b 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 @@ -57,7 +57,7 @@ internal class DefaultResolveSpaceInfoTask @Inject constructor( suggestedOnly = params.suggestedOnly ) return executeRequest(globalErrorReceiver) { - apiCall = spaceApi.getSpaces(params.spaceId, body) + spaceApi.getSpaces(params.spaceId, body) } } } 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 3f8da574fe..0fcc95fdb3 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,7 +17,6 @@ package org.matrix.android.sdk.internal.session.space import org.matrix.android.sdk.internal.network.NetworkConstants -import retrofit2.Call import retrofit2.http.Body import retrofit2.http.POST import retrofit2.http.Path @@ -39,6 +38,6 @@ internal interface SpaceApi { * - https://hackmd.io/fNYh4tjUT5mQfR1uuRzWDA */ @POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/spaces") - fun getSpaces(@Path("roomId") spaceId: String, - @Body params: SpaceSummaryParams): Call + suspend fun getSpaces(@Path("roomId") spaceId: String, + @Body params: SpaceSummaryParams): SpacesResponse } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt index acba679ad3..7cebbb0192 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt @@ -97,9 +97,12 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), isInitialSync, aggregator, reporter) // post room sync validation - roomSummaryUpdater.validateSpaceRelationship(realm) +// roomSummaryUpdater.validateSpaceRelationship(realm) } + fun postSyncSpaceHierarchyHandle(realm: Realm) { + roomSummaryUpdater.validateSpaceRelationship(realm) + } // PRIVATE METHODS ***************************************************************************** private fun handleRoomSync(realm: Realm, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt index 8e243c3443..157787c8cf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt @@ -132,6 +132,11 @@ internal class SyncResponseHandler @Inject constructor( Timber.v("On sync completed") cryptoSyncHandler.onSyncCompleted(syncResponse) + + // post sync stuffs + monarchy.writeAsync { + roomSyncHandler.postSyncSpaceHierarchyHandle(it) + } } /** diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/util/GraphUtilsTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/util/GraphUtilsTest.kt new file mode 100644 index 0000000000..618f6f4714 --- /dev/null +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/util/GraphUtilsTest.kt @@ -0,0 +1,111 @@ +/* + * 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. + * 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.util + +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runners.MethodSorters +import org.matrix.android.sdk.MatrixTest +import org.matrix.android.sdk.internal.session.room.summary.Graph +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +@FixMethodOrder(MethodSorters.JVM) +class GraphUtilsTest : MatrixTest { + + @Test + fun testCreateGraph() { + val graph = Graph() + + graph.addEdge("E", "C") + graph.addEdge("B", "A") + graph.addEdge("C", "A") + graph.addEdge("D", "C") + graph.addEdge("E", "D") + + graph.getOrCreateNode("F") + + System.out.println(graph.toString()) + + val backEdges = graph.findBackwardEdges(graph.getOrCreateNode("E")) + + assertTrue(backEdges.isEmpty(), "There should not be any cycle in this graphs") + } + + @Test + fun testCycleGraph() { + val graph = Graph() + + graph.addEdge("E", "C") + graph.addEdge("B", "A") + graph.addEdge("C", "A") + graph.addEdge("D", "C") + graph.addEdge("E", "D") + + graph.getOrCreateNode("F") + + // adding loops + graph.addEdge("C", "E") + graph.addEdge("B", "B") + + System.out.println(graph.toString()) + + val backEdges = graph.findBackwardEdges(graph.getOrCreateNode("E")) + System.out.println(backEdges.joinToString(" | ") { "${it.source.name} -> ${it.destination.name}" }) + + assertTrue(backEdges.size == 2, "There should be 2 backward edges not ${backEdges.size}") + + val edge1 = backEdges.find { it.source.name == "C" } + assertNotNull(edge1, "There should be a back edge from C") + assertEquals("E", edge1.destination.name, "There should be a back edge C -> E") + + val edge2 = backEdges.find { it.source.name == "B" } + assertNotNull(edge2, "There should be a back edge from B") + assertEquals("B", edge2.destination.name, "There should be a back edge C -> C") + + // clean the graph + val acyclicGraph = graph.withoutEdges(backEdges) + System.out.println(acyclicGraph.toString()) + + assertTrue(acyclicGraph.findBackwardEdges(acyclicGraph.getOrCreateNode("E")).isEmpty(), "There should be no backward edges") + + val flatten = acyclicGraph.flattenDestination() + + assertTrue(flatten[acyclicGraph.getOrCreateNode("A")]!!.isEmpty()) + + val flattenParentsB = flatten[acyclicGraph.getOrCreateNode("B")] + assertTrue(flattenParentsB!!.size == 1) + assertTrue(flattenParentsB.contains(acyclicGraph.getOrCreateNode("A"))) + + val flattenParentsE = flatten[acyclicGraph.getOrCreateNode("E")] + assertTrue(flattenParentsE!!.size == 3) + assertTrue(flattenParentsE.contains(acyclicGraph.getOrCreateNode("A"))) + assertTrue(flattenParentsE.contains(acyclicGraph.getOrCreateNode("C"))) + assertTrue(flattenParentsE.contains(acyclicGraph.getOrCreateNode("D"))) + +// System.out.println( +// buildString { +// flatten.entries.forEach { +// append("${it.key.name}: [") +// append(it.value.joinToString(",") { it.name }) +// append("]\n") +// } +// } +// ) + } +} diff --git a/vector/build.gradle b/vector/build.gradle index e09c7dc6f2..589844029d 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -333,6 +333,7 @@ dependencies { implementation "com.squareup.moshi:moshi-adapters:$moshi_version" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" // Log diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index 22816085fb..bb45e97776 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -19,7 +19,12 @@ package im.vector.app import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent +import arrow.core.Option +import im.vector.app.core.utils.BehaviorDataSource +import im.vector.app.features.ui.UiStateRepository import io.reactivex.disposables.CompositeDisposable +import org.matrix.android.sdk.api.MatrixPatterns +import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject import javax.inject.Singleton @@ -29,10 +34,15 @@ import javax.inject.Singleton */ // TODO Keep this class for now, will maybe be used fro Space @Singleton -class AppStateHandler @Inject constructor() : LifecycleObserver { +class AppStateHandler @Inject constructor( + private val sessionDataSource: ActiveSessionDataSource, + private val uiStateRepository: UiStateRepository +) : LifecycleObserver { private val compositeDisposable = CompositeDisposable() + val selectedSpaceDataSource = BehaviorDataSource>(Option.empty()) + init { // restore current space from ui state sessionDataSource.currentValue?.orNull()?.let { session -> @@ -44,6 +54,12 @@ class AppStateHandler @Inject constructor() : LifecycleObserver { } } + fun safeActiveSpaceId() : String? { + return selectedSpaceDataSource.currentValue?.orNull()?.roomId?.takeIf { + MatrixPatterns.isRoomId(it) + } + } + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun entersForeground() { } diff --git a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt index e75f2416eb..c8d5f6dd20 100644 --- a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt @@ -21,6 +21,7 @@ import android.content.res.Resources import dagger.BindsInstance import dagger.Component import im.vector.app.ActiveSessionDataSource +import im.vector.app.AppStateHandler import im.vector.app.EmojiCompatFontProvider import im.vector.app.EmojiCompatWrapper import im.vector.app.VectorApplication @@ -35,10 +36,8 @@ import im.vector.app.features.configuration.VectorConfiguration import im.vector.app.features.crypto.keysrequest.KeyRequestHandler import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler import im.vector.app.features.grouplist.SelectedGroupDataSource -import im.vector.app.features.grouplist.SelectedSpaceDataSource import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.CurrentSpaceSuggestedRoomListDataSource -import im.vector.app.features.home.HomeRoomListDataSource import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHolder @@ -118,7 +117,7 @@ interface VectorComponent { fun selectedGroupStore(): SelectedGroupDataSource - fun selectedSpaceStore(): SelectedSpaceDataSource + fun appStateHandler(): AppStateHandler fun currentSpaceSuggestedRoomListDataSource(): CurrentSpaceSuggestedRoomListDataSource diff --git a/vector/src/main/java/im/vector/app/core/ui/list/GenericFooterItem.kt b/vector/src/main/java/im/vector/app/core/ui/list/GenericFooterItem.kt index 1bde66f6d1..e185a4dbc4 100644 --- a/vector/src/main/java/im/vector/app/core/ui/list/GenericFooterItem.kt +++ b/vector/src/main/java/im/vector/app/core/ui/list/GenericFooterItem.kt @@ -15,7 +15,6 @@ */ package im.vector.app.core.ui.list -import android.graphics.Typeface import android.view.Gravity import android.widget.TextView import androidx.annotation.ColorInt diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index ab0c6e45fe..cfdcc40a37 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -158,7 +158,13 @@ class HomeActivity : is HomeActivitySharedAction.CloseDrawer -> views.drawerLayout.closeDrawer(GravityCompat.START) is HomeActivitySharedAction.OpenGroup -> { views.drawerLayout.closeDrawer(GravityCompat.START) - replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true) + // Temporary + // we might want to delay that to avoid having the drawer animation lagging + // would be probably better to let the drawer do that? in the on closed callback? + views.coordinatorLayout.postDelayed({ + replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true) + }, 200) + Unit } is HomeActivitySharedAction.OpenSpacePreview -> { startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId)) diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index a07a329a57..b2fadae0fb 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -23,12 +23,12 @@ import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.AppStateHandler import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.grouplist.SelectedGroupDataSource -import im.vector.app.features.grouplist.SelectedSpaceDataSource import im.vector.app.features.ui.UiStateRepository import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -49,6 +49,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho private val session: Session, private val uiStateRepository: UiStateRepository, private val selectedGroupStore: SelectedGroupDataSource, + private val appStateHandler: AppStateHandler, private val stringProvider: StringProvider) : VectorViewModel(initialState) { @@ -83,7 +84,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho override fun handle(action: HomeDetailAction) { when (action) { is HomeDetailAction.SwitchDisplayMode -> handleSwitchDisplayMode(action) - HomeDetailAction.MarkAllRoomsRead -> handleMarkAllRoomsRead() + HomeDetailAction.MarkAllRoomsRead -> handleMarkAllRoomsRead() } } @@ -140,7 +141,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho } private fun observeSelectedSpaceStore() { - selectedSpaceStore + appStateHandler.selectedSpaceDataSource .observe() .subscribe { setState { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListListener.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListListener.kt index e9833d1560..0ba265f841 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListListener.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListListener.kt @@ -18,10 +18,12 @@ package im.vector.app.features.home.room.list import im.vector.app.features.home.room.filtered.FilteredRoomFooterItem import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo interface RoomListListener : FilteredRoomFooterItem.FilteredRoomFooterItemListener { fun onRoomClicked(room: RoomSummary) fun onRoomLongClicked(room: RoomSummary): Boolean fun onRejectRoomInvitation(room: RoomSummary) fun onAcceptRoomInvitation(room: RoomSummary) + fun onJoinSuggestedRoom(room: SpaceChildInfo) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 880c3391c9..c171dea863 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.list import androidx.annotation.StringRes +import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext @@ -24,28 +25,31 @@ import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext +import im.vector.app.AppStateHandler import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.utils.DataSource -import im.vector.app.features.grouplist.SelectedSpaceDataSource import im.vector.app.core.resources.StringProvider import im.vector.app.features.home.RoomListDisplayMode +import io.reactivex.Observable import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.query.RoomTagQueryFilter import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams -import org.matrix.android.sdk.api.session.room.UpdatableFilterLivePageResult +import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.tag.RoomTag -import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.state.isPublic +import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.asObservable import org.matrix.android.sdk.rx.rx import timber.log.Timber @@ -54,17 +58,79 @@ import javax.inject.Inject class RoomListViewModel @Inject constructor( initialState: RoomListViewState, private val session: Session, - private val stringProvider: StringProvider + private val stringProvider: StringProvider, + private val appStateHandler: AppStateHandler ) : VectorViewModel(initialState) { interface Factory { fun create(initialState: RoomListViewState): RoomListViewModel } - private var updatableQuery: UpdatableFilterLivePageResult? = null + private var updatableQuery: UpdatableLivePageResult? = null + + private var activeSpaceAwareQueries: List? = null + + interface ActiveSpaceQueryUpdater { + fun updateForSpaceId(roomId: String?) + } + + enum class SpaceFilterStrategy { + NORMAL, + NOT_IF_ALL, + NONE + } init { observeMembershipChanges() + + appStateHandler.selectedSpaceDataSource.observe() +// .observeOn(Schedulers.computation()) + .distinctUntilChanged() + .switchMap { activeSpaceOption -> + val selectedSpace = activeSpaceOption.orNull() + activeSpaceAwareQueries?.onEach { updater -> + updater.updateForSpaceId(selectedSpace?.roomId?.takeIf { MatrixPatterns.isRoomId(it) }) + } +// activeSpaceAwareQueries?.forEach { +// it.updateQuery { +// it.copy( +// activeSpaceId = ActiveSpaceFilter.ActiveSpace(selectedSpace?.roomId?.takeIf { MatrixPatterns.isRoomId(it) }) +// ) +// } +// } + if (selectedSpace == null) { + Observable.just(emptyList()) + } else { + liveData(context = viewModelScope.coroutineContext + Dispatchers.IO) { + val spaceSum = tryOrNull { session.spaceService().querySpaceChildren(selectedSpace.roomId, suggestedOnly = true) } + val value = spaceSum?.second ?: emptyList() + emit(value) + }.asObservable() + } + } + .execute { info -> + copy(asyncSuggestedRooms = info) + } + + appStateHandler.selectedSpaceDataSource.observe() +// .observeOn(Schedulers.computation()) + .distinctUntilChanged() + .map { it.orNull() } + .distinctUntilChanged() + .execute { + copy( + currentSpace = it + ) + } + + session.rx().liveUser(session.myUserId) + .map { it.getOrNull()?.getBestName() } + .distinctUntilChanged() + .execute { + copy( + currentUserName = it.invoke() ?: session.myUserId + ) + } } private fun observeMembershipChanges() { @@ -87,47 +153,93 @@ class RoomListViewModel @Inject constructor( val sections: List by lazy { val sections = mutableListOf() + val activeSpaceAwareQueries = mutableListOf() if (initialState.displayMode == RoomListDisplayMode.PEOPLE) { - addSection(sections, R.string.invitations_header, true) { + addSection(sections, + activeSpaceAwareQueries, + R.string.invitations_header, + true, + SpaceFilterStrategy.NOT_IF_ALL + ) { it.memberships = listOf(Membership.INVITE) it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM } - addSection(sections, R.string.bottom_action_favourites) { + addSection(sections, + activeSpaceAwareQueries, + R.string.bottom_action_favourites, + false, + SpaceFilterStrategy.NOT_IF_ALL + ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) } - addSection(sections, R.string.bottom_action_people_x) { + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_people_x, + false, + SpaceFilterStrategy.NORMAL + ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM } } else if (initialState.displayMode == RoomListDisplayMode.ROOMS) { - addSection(sections, R.string.invitations_header, true) { + addSection( + sections, activeSpaceAwareQueries, + R.string.invitations_header, + true, + SpaceFilterStrategy.NONE + ) { it.memberships = listOf(Membership.INVITE) it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS } - addSection(sections, R.string.bottom_action_favourites) { + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_favourites, + false, + SpaceFilterStrategy.NOT_IF_ALL + ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) } - addSection(sections, R.string.bottom_action_rooms) { + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_rooms, + false, + SpaceFilterStrategy.NORMAL + ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS it.roomTagQueryFilter = RoomTagQueryFilter(false, false, false) } - addSection(sections, R.string.low_priority_header) { + addSection( + sections, + activeSpaceAwareQueries, + R.string.low_priority_header, + false, + SpaceFilterStrategy.NORMAL + ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS it.roomTagQueryFilter = RoomTagQueryFilter(null, true, null) } - addSection(sections, R.string.system_alerts_header) { + addSection( + sections, + activeSpaceAwareQueries, + R.string.system_alerts_header, + false, + SpaceFilterStrategy.NORMAL + ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true) @@ -147,12 +259,24 @@ class RoomListViewModel @Inject constructor( } ) } else if (initialState.displayMode == RoomListDisplayMode.NOTIFICATIONS) { - addSection(sections, R.string.invitations_header, true) { + addSection( + sections, + activeSpaceAwareQueries, + R.string.invitations_header, + true, + SpaceFilterStrategy.NORMAL + ) { it.memberships = listOf(Membership.INVITE) it.roomCategoryFilter = RoomCategoryFilter.ALL } - addSection(sections, R.string.bottom_action_rooms, true) { + addSection( + sections, + activeSpaceAwareQueries, + R.string.bottom_action_rooms, + false, + SpaceFilterStrategy.NORMAL + ) { it.memberships = listOf(Membership.JOIN) it.roomCategoryFilter = RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS } @@ -176,15 +300,67 @@ class RoomListViewModel @Inject constructor( } private fun addSection(sections: MutableList, + activeSpaceUpdaters: MutableList, @StringRes nameRes: Int, notifyOfLocalEcho: Boolean = false, + spaceFilterStrategy: SpaceFilterStrategy = SpaceFilterStrategy.NONE, query: (RoomSummaryQueryParams.Builder) -> Unit) { withQueryParams( { query.invoke(it) }, { roomQueryParams -> val name = stringProvider.getString(nameRes) - session.getPagedRoomSummariesLive(roomQueryParams) +// if (activeSpaceAwareQueries != null) { + session.getFilteredPagedRoomSummariesLive( + when (spaceFilterStrategy) { + SpaceFilterStrategy.NORMAL -> { + roomQueryParams.copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(appStateHandler.safeActiveSpaceId()) + ) + } + SpaceFilterStrategy.NOT_IF_ALL -> { + if (appStateHandler.safeActiveSpaceId() == null) { + roomQueryParams + } else { + roomQueryParams.copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(appStateHandler.safeActiveSpaceId()) + ) + } + } + SpaceFilterStrategy.NONE -> roomQueryParams + } + + ).also { + when (spaceFilterStrategy) { + SpaceFilterStrategy.NORMAL -> { + activeSpaceUpdaters.add(object : ActiveSpaceQueryUpdater { + override fun updateForSpaceId(roomId: String?) { + it.updateQuery { + it.copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(roomId) + ) + } + } + }) + } + SpaceFilterStrategy.NOT_IF_ALL -> { + activeSpaceUpdaters.add(object : ActiveSpaceQueryUpdater { + override fun updateForSpaceId(roomId: String?) { + if (roomId != null) { + it.updateQuery { + it.copy( + activeSpaceId = ActiveSpaceFilter.ActiveSpace(roomId) + ) + } + } + } + }) + } + SpaceFilterStrategy.NONE -> { + // we ignore current space for this one + } + } + }.livePagedList .let { livePagedList -> // use it also as a source to update count @@ -206,6 +382,7 @@ class RoomListViewModel @Inject constructor( ) } } + ) } @@ -242,12 +419,11 @@ class RoomListViewModel @Inject constructor( roomFilter = action.filter ) } - updatableQuery?.updateQuery( - roomSummaryQueryParams { - memberships = Membership.activeMemberships() + updatableQuery?.updateQuery { + it.copy( displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE) - } - ) + ) + } } private fun handleAcceptInvitation(action: RoomListAction.AcceptInvitation) = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt index d36bc45ab6..8377f222bf 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt @@ -16,12 +16,14 @@ package im.vector.app.features.home.room.list +import im.vector.app.AppStateHandler import im.vector.app.core.resources.StringProvider import org.matrix.android.sdk.api.session.Session import javax.inject.Inject import javax.inject.Provider class RoomListViewModelFactory @Inject constructor(private val session: Provider, + private val appStateHandler: AppStateHandler, private val stringProvider: StringProvider) : RoomListViewModel.Factory { @@ -29,7 +31,8 @@ class RoomListViewModelFactory @Inject constructor(private val session: Provider return RoomListViewModel( initialState, session.get(), - stringProvider + stringProvider, + appStateHandler ) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt index e53c3e6bde..6fb7bd05be 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewState.kt @@ -16,16 +16,22 @@ package im.vector.app.features.home.room.list +import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized import im.vector.app.features.home.RoomListDisplayMode 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 data class RoomListViewState( val displayMode: RoomListDisplayMode, val roomFilter: String = "", - val roomMembershipChanges: Map = emptyMap() + val roomMembershipChanges: Map = emptyMap(), val asyncSuggestedRooms: Async> = Uninitialized, - val suggestedRoomJoiningState: Map> = emptyMap() + val suggestedRoomJoiningState: Map> = emptyMap(), + val currentUserName: String? = null, + val currentSpace: Async = Uninitialized ) : MvRxState { constructor(args: RoomListParams) : this(displayMode = args.displayMode) diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 57b690bec2..ce178a1358 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -30,6 +30,7 @@ import androidx.core.app.TaskStackBuilder import androidx.core.util.Pair import androidx.core.view.ViewCompat import arrow.core.Option +import im.vector.app.AppStateHandler import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.error.fatalError @@ -47,7 +48,6 @@ import im.vector.app.features.crypto.verification.SupportedVerificationMethodsPr import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.debug.DebugMenuActivity import im.vector.app.features.devtools.RoomDevToolActivity -import im.vector.app.features.grouplist.SelectedSpaceDataSource import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs import im.vector.app.features.home.room.detail.search.SearchActivity @@ -92,7 +92,7 @@ class DefaultNavigator @Inject constructor( private val sessionHolder: ActiveSessionHolder, private val vectorPreferences: VectorPreferences, private val widgetArgsBuilder: WidgetArgsBuilder, - private val selectedSpaceDataSource: SelectedSpaceDataSource, + private val appStateHandler: AppStateHandler, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider ) : Navigator { @@ -114,7 +114,7 @@ class DefaultNavigator @Inject constructor( sessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(spaceId)?.spaceSummary()?.let { Timber.d("## Nav: Switching to space $spaceId / ${it.name}") - selectedSpaceDataSource.post(Option.just(it)) + appStateHandler.selectedSpaceDataSource.post(Option.just(it)) } ?: kotlin.run { Timber.d("## Nav: Failed to switch to space $spaceId") } @@ -252,7 +252,7 @@ class DefaultNavigator @Inject constructor( } override fun openRoomDirectory(context: Context, initialFilter: String) { - val selectedSpace = selectedSpaceDataSource.currentValue?.orNull()?.let { + val selectedSpace = appStateHandler.selectedSpaceDataSource.currentValue?.orNull()?.let { sessionHolder.getSafeActiveSession()?.getRoomSummary(it.roomId) } if (selectedSpace == null) { @@ -276,7 +276,7 @@ class DefaultNavigator @Inject constructor( } override fun openInviteUsersToRoom(context: Context, roomId: String) { - val selectedSpace = selectedSpaceDataSource.currentValue?.orNull()?.let { + val selectedSpace = appStateHandler.selectedSpaceDataSource.currentValue?.orNull()?.let { sessionHolder.getSafeActiveSession()?.getRoomSummary(it.roomId) } if (vectorPreferences.labSpaces() && selectedSpace != null) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index f0e338f6ad..f82923b570 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -24,10 +24,10 @@ import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.AppStateHandler import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.grouplist.SelectedSpaceDataSource import im.vector.app.features.ui.UiStateRepository import io.reactivex.Observable import io.reactivex.functions.BiFunction @@ -43,7 +43,7 @@ import org.matrix.android.sdk.rx.rx const val ALL_COMMUNITIES_GROUP_ID = "+ALL_COMMUNITIES_GROUP_ID" class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState, - private val selectedSpaceDataSource: SelectedSpaceDataSource, + private val appStateHandler: AppStateHandler, private val session: Session, private val stringProvider: StringProvider, private val uiStateRepository: UiStateRepository @@ -68,7 +68,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp init { observeSpaceSummaries() observeSelectionState() - selectedSpaceDataSource + appStateHandler.selectedSpaceDataSource .observe() .subscribe { if (currentGroupId != it.orNull()?.roomId) { @@ -91,7 +91,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp _viewEvents.post(SpaceListViewEvents.OpenSpace) } val optionGroup = Option.just(spaceSummary) - selectedSpaceDataSource.post(optionGroup) + appStateHandler.selectedSpaceDataSource.post(optionGroup) } else { // If selected group is null we force to default. It can happens when leaving the selected group. setState { diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt index ab71b41c54..7ff13c06fd 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt @@ -26,8 +26,7 @@ import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.databinding.FragmentSpaceCreateChooseTypeBinding import javax.inject.Inject -class ChooseSpaceTypeFragment @Inject constructor( -) : VectorBaseFragment() { +class ChooseSpaceTypeFragment @Inject constructor() : VectorBaseFragment() { private val sharedViewModel: CreateSpaceViewModel by activityViewModel() @@ -47,4 +46,3 @@ class ChooseSpaceTypeFragment @Inject constructor( })) } } -