Fix space filtering

This commit is contained in:
Valere 2021-04-11 23:17:45 +02:00
parent 8a35a786b4
commit 0d3c2b4bef
33 changed files with 739 additions and 132 deletions

View File

@ -1,5 +1,5 @@
/* /*
* 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,13 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.grouplist package org.matrix.android.sdk.api.query
import arrow.core.Option sealed class ActiveSpaceFilter {
import im.vector.app.core.utils.BehaviorDataSource object None : ActiveSpaceFilter()
import org.matrix.android.sdk.api.session.room.model.RoomSummary data class ActiveSpace(val currentSpaceId: String?) : ActiveSpaceFilter()
import javax.inject.Inject }
import javax.inject.Singleton
@Singleton
class SelectedSpaceDataSource @Inject constructor() : BehaviorDataSource<Option<RoomSummary>>(Option.empty())

View File

@ -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.room.uploads.UploadsService
import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.api.session.search.SearchResult
import org.matrix.android.sdk.api.session.space.Space 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.api.util.Optional
/** /**

View File

@ -184,7 +184,7 @@ interface RoomService {
* TODO Doc * TODO Doc
*/ */
fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams,
pagedListConfig: PagedList.Config = defaultPagedListConfig): UpdatableFilterLivePageResult pagedListConfig: PagedList.Config = defaultPagedListConfig): UpdatableLivePageResult
/** /**
* TODO Doc * TODO Doc
@ -199,7 +199,7 @@ interface RoomService {
.setPrefetchDistance(10) .setPrefetchDistance(10)
.build() .build()
fun getFlattenRoomSummaryChildOf(spaceId: String?, memberships: List<Membership> = Membership.activeMemberships()) : List<RoomSummary> fun getFlattenRoomSummaryChildrenOf(spaceId: String?, memberships: List<Membership> = Membership.activeMemberships()) : List<RoomSummary>
/** /**
* Returns all the children of this space, as LiveData * Returns all the children of this space, as LiveData

View File

@ -16,6 +16,7 @@
package org.matrix.android.sdk.api.session.room 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.QueryStringValue
import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.query.RoomTagQueryFilter import org.matrix.android.sdk.api.query.RoomTagQueryFilter
@ -54,9 +55,10 @@ data class RoomSummaryQueryParams(
val canonicalAlias: QueryStringValue, val canonicalAlias: QueryStringValue,
val memberships: List<Membership>, val memberships: List<Membership>,
val roomCategoryFilter: RoomCategoryFilter?, val roomCategoryFilter: RoomCategoryFilter?,
val roomTagQueryFilter: RoomTagQueryFilter? val roomTagQueryFilter: RoomTagQueryFilter?,
val excludeType: List<String?>?, val excludeType: List<String?>?,
val includeType: List<String?>? val includeType: List<String?>?,
val activeSpaceId: ActiveSpaceFilter?
) { ) {
class Builder { class Builder {
@ -67,8 +69,9 @@ data class RoomSummaryQueryParams(
var memberships: List<Membership> = Membership.all() var memberships: List<Membership> = Membership.all()
var roomCategoryFilter: RoomCategoryFilter? = RoomCategoryFilter.ALL var roomCategoryFilter: RoomCategoryFilter? = RoomCategoryFilter.ALL
var roomTagQueryFilter: RoomTagQueryFilter? = null var roomTagQueryFilter: RoomTagQueryFilter? = null
var excludeType: List<String?> = listOf(RoomType.SPACE) var excludeType: List<String?>? = listOf(RoomType.SPACE)
var includeType: List<String?>? = null var includeType: List<String?>? = null
var activeSpaceId: ActiveSpaceFilter = ActiveSpaceFilter.None
fun build() = RoomSummaryQueryParams( fun build() = RoomSummaryQueryParams(
roomId = roomId, roomId = roomId,
@ -78,7 +81,8 @@ data class RoomSummaryQueryParams(
roomCategoryFilter = roomCategoryFilter, roomCategoryFilter = roomCategoryFilter,
roomTagQueryFilter = roomTagQueryFilter, roomTagQueryFilter = roomTagQueryFilter,
excludeType = excludeType, excludeType = excludeType,
includeType = includeType includeType = includeType,
activeSpaceId = activeSpaceId
) )
} }
} }

View File

@ -20,8 +20,8 @@ import androidx.lifecycle.LiveData
import androidx.paging.PagedList import androidx.paging.PagedList
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
interface UpdatableFilterLivePageResult { interface UpdatableLivePageResult {
val livePagedList: LiveData<PagedList<RoomSummary>> val livePagedList: LiveData<PagedList<RoomSummary>>
fun updateQuery(queryParams: RoomSummaryQueryParams) fun updateQuery(builder: (RoomSummaryQueryParams) -> RoomSummaryQueryParams)
} }

View File

@ -16,10 +16,8 @@
package org.matrix.android.sdk.api.session.space 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.Room
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.util.Cancelable
interface Space { interface Space {

View File

@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.database package org.matrix.android.sdk.internal.database
import io.realm.DynamicRealm import io.realm.DynamicRealm
import io.realm.FieldAttribute
import io.realm.RealmMigration import io.realm.RealmMigration
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields
@ -216,6 +217,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
realm.schema.get("RoomSummaryEntity") realm.schema.get("RoomSummaryEntity")
?.addField(RoomSummaryEntityFields.ROOM_TYPE, String::class.java) ?.addField(RoomSummaryEntityFields.ROOM_TYPE, String::class.java)
?.addField(RoomSummaryEntityFields.FLATTEN_PARENT_IDS, String::class.java)
?.transform { obj -> ?.transform { obj ->
// Should I put messaging type here? // Should I put messaging type here?
obj.setString(RoomSummaryEntityFields.ROOM_TYPE, null) obj.setString(RoomSummaryEntityFields.ROOM_TYPE, null)

View File

@ -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 import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
internal open class RoomSummaryEntity( internal open class RoomSummaryEntity(
@PrimaryKey var roomId: String = "" @PrimaryKey var roomId: String = "",
var roomType: String? = null, var roomType: String? = null,
var parents: RealmList<SpaceParentSummaryEntity> = RealmList(), var parents: RealmList<SpaceParentSummaryEntity> = RealmList(),
var children: RealmList<SpaceChildSummaryEntity> = RealmList() var children: RealmList<SpaceChildSummaryEntity> = RealmList()
@ -207,6 +207,11 @@ internal open class RoomSummaryEntity(
if (value != field) field = value if (value != field) field = value
} }
var flattenParentIds: String? = null
set(value) {
if (value != field) field = value
}
@Index @Index
private var membershipStr: String = Membership.NONE.name private var membershipStr: String = Membership.NONE.name

View File

@ -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.room.uploads.UploadsService
import org.matrix.android.sdk.api.session.search.SearchResult import org.matrix.android.sdk.api.session.search.SearchResult
import org.matrix.android.sdk.api.session.space.Space 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.api.util.Optional
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM 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.state.SendStateTask
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource 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.search.SearchTask
import org.matrix.android.sdk.internal.session.space.DefaultSpace 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 org.matrix.android.sdk.internal.util.awaitCallback
import java.security.InvalidParameterException import java.security.InvalidParameterException
import javax.inject.Inject import javax.inject.Inject

View File

@ -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.Room
import org.matrix.android.sdk.api.session.room.RoomService 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.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.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary 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) override fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config)
: UpdatableFilterLivePageResult { : UpdatableLivePageResult {
return roomSummaryDataSource.getFilteredPagedRoomSummariesLive(queryParams, pagedListConfig) return roomSummaryDataSource.getUpdatablePagedRoomSummariesLive(queryParams, pagedListConfig)
} }
override fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount { override fun getNotificationCountForRooms(queryParams: RoomSummaryQueryParams): RoomAggregateNotificationCount {

View File

@ -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<GraphNode, ArrayList<GraphEdge>> = 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<GraphEdge> {
return adjacencyList[node]?.toList() ?: emptyList()
}
fun withoutEdges(edgesToPrune: List<GraphEdge>): 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<GraphEdge> {
val backwardEdges = mutableSetOf<GraphEdge>()
val visited = mutableMapOf<GraphNode, Int>()
val notVisited = -1
val inPath = 0
val completed = 1
adjacencyList.keys.forEach {
visited[it] = notVisited
}
val stack = LinkedList<GraphNode>()
(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<GraphNode, Set<GraphNode>> {
val result = HashMap<GraphNode, Set<GraphNode>>()
adjacencyList.keys.forEach { vertex ->
result[vertex] = flattenOf(vertex)
}
return result
}
private fun flattenOf(node: GraphNode): Set<GraphNode> {
val result = mutableSetOf<GraphNode>()
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")
}
}
}
}

View File

@ -25,19 +25,18 @@ import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.Sort 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.query.RoomCategoryFilter
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams 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 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.model.Membership 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.RoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomType 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.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.roomSummaryQueryParams
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams 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.session.space.SpaceSummaryQueryParams
import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
@ -174,8 +173,8 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
) )
} }
fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, fun getUpdatablePagedRoomSummariesLive(queryParams: RoomSummaryQueryParams,
pagedListConfig: PagedList.Config): UpdatableFilterLivePageResult { pagedListConfig: PagedList.Config): UpdatableLivePageResult {
val realmDataSourceFactory = monarchy.createDataSourceFactory { realm -> val realmDataSourceFactory = monarchy.createDataSourceFactory { realm ->
roomSummariesQuery(realm, queryParams) roomSummariesQuery(realm, queryParams)
.sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING) .sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING)
@ -189,13 +188,12 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
LivePagedListBuilder(dataSourceFactory, pagedListConfig) LivePagedListBuilder(dataSourceFactory, pagedListConfig)
) )
return object : UpdatableFilterLivePageResult { return object : UpdatableLivePageResult {
override val livePagedList: LiveData<PagedList<RoomSummary>> = mapped override val livePagedList: LiveData<PagedList<RoomSummary>> = mapped
override fun updateQuery(queryParams: RoomSummaryQueryParams) { override fun updateQuery(builder: (RoomSummaryQueryParams) -> RoomSummaryQueryParams) {
realmDataSourceFactory.updateQuery { realmDataSourceFactory.updateQuery {
roomSummariesQuery(it, queryParams) roomSummariesQuery(it, builder.invoke(queryParams))
.sort(RoomSummaryEntityFields.LAST_ACTIVITY_TIME, Sort.DESCENDING)
} }
} }
} }
@ -254,8 +252,26 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
when (queryParams.roomCategoryFilter) { 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.ONLY_ROOMS -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, false)
RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS -> query.greaterThan(RoomSummaryEntityFields.NOTIFICATION_COUNT, 0)
RoomCategoryFilter.ALL -> Unit // nop 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 return query
} }

View File

@ -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.EventEntityFields
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields 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.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.SpaceChildSummaryEntity
import org.matrix.android.sdk.internal.database.model.SpaceParentSummaryEntity import org.matrix.android.sdk.internal.database.model.SpaceParentSummaryEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity 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.database.query.whereType
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.extensions.clearWith 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.RoomAvatarResolver
import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver
import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper 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 org.matrix.android.sdk.internal.session.sync.model.RoomSyncUnreadNotifications
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.system.measureTimeMillis
internal class RoomSummaryUpdater @Inject constructor( internal class RoomSummaryUpdater @Inject constructor(
@UserId private val userId: String, @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 * Should be called at the end of the room sync, to check and validate all parent/child relations
*/ */
fun validateSpaceRelationship(realm: Realm) { fun validateSpaceRelationship(realm: Realm) {
// Do level 0 stuffs measureTimeMillis {
val lookupMap = realm.where(RoomSummaryEntity::class.java)
realm.where(RoomSummaryEntity::class.java).findAll().forEach { roomSummary -> .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
if (roomSummary.roomType == RoomType.SPACE) { .equalTo(RoomSummaryEntityFields.IS_DIRECT, false)
roomSummary.children.clearWith { it.deleteFromRealm() } // we order by roomID to be consistent when breaking parent/child cycles
roomSummary.children.addAll( .sort(RoomSummaryEntityFields.ROOM_ID)
RoomChildRelationInfo(realm, roomSummary.roomId).getDirectChildrenDescriptions() .findAll().map {
.map { it.flattenParentIds = null
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with info $it") it to emptyList<RoomSummaryEntity>().toMutableSet()
realm.createObject<SpaceChildSummaryEntity>().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")
} }
.toMap()
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<SpaceChildSummaryEntity>().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)
} }
) )
}
// check parents RoomSummaryEntity.where(realm, child.roomId)
roomSummary.parents.clearWith { it.deleteFromRealm() } .process(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.activeMemberships())
roomSummary.parents.addAll( .findFirst()
RoomChildRelationInfo(realm, roomSummary.roomId).getParentDescriptions() ?.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 -> .map { parentInfo ->
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with parent info $parentInfo")
lookedUp.parents.add(
realm.createObject<SpaceParentSummaryEntity>().apply { realm.createObject<SpaceParentSummaryEntity>().apply {
this.parentRoomId = parentInfo.roomId this.parentRoomId = parentInfo.roomId
this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst() this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst()
this.canonical = parentInfo.canonical this.canonical = parentInfo.canonical
this.viaServers.addAll(parentInfo.viaServers) this.viaServers.addAll(parentInfo.viaServers)
// this.level = 0
}.also {
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with parent $it")
}
} }
) )
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)
}
}
}
}
}
// 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")
} }
} }

View File

@ -16,7 +16,6 @@
package org.matrix.android.sdk.internal.session.space 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.query.QueryStringValue
import org.matrix.android.sdk.api.session.events.model.EventType 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.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.room.model.RoomSummary
import org.matrix.android.sdk.api.session.space.Space 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.session.space.model.SpaceChildContent
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
internal class DefaultSpace( internal class DefaultSpace(

View File

@ -169,7 +169,7 @@ internal class DefaultSpaceService @Inject constructor(
stateKey = QueryStringValue.NoCondition stateKey = QueryStringValue.NoCondition
) )
val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>() val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>()
?: throw UnsupportedOperationException("Cannot add canonical child, not enough power level") ?: throw UnsupportedOperationException("Cannot add canonical child, missing powerlevel")
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent) val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
if (!powerLevelsHelper.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) { if (!powerLevelsHelper.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) {
throw UnsupportedOperationException("Cannot add canonical child, not enough power level") throw UnsupportedOperationException("Cannot add canonical child, not enough power level")

View File

@ -57,7 +57,7 @@ internal class DefaultResolveSpaceInfoTask @Inject constructor(
suggestedOnly = params.suggestedOnly suggestedOnly = params.suggestedOnly
) )
return executeRequest(globalErrorReceiver) { return executeRequest(globalErrorReceiver) {
apiCall = spaceApi.getSpaces(params.spaceId, body) spaceApi.getSpaces(params.spaceId, body)
} }
} }
} }

View File

@ -17,7 +17,6 @@
package org.matrix.android.sdk.internal.session.space package org.matrix.android.sdk.internal.session.space
import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.network.NetworkConstants
import retrofit2.Call
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Path import retrofit2.http.Path
@ -39,6 +38,6 @@ internal interface SpaceApi {
* - https://hackmd.io/fNYh4tjUT5mQfR1uuRzWDA * - https://hackmd.io/fNYh4tjUT5mQfR1uuRzWDA
*/ */
@POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/spaces") @POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/spaces")
fun getSpaces(@Path("roomId") spaceId: String, suspend fun getSpaces(@Path("roomId") spaceId: String,
@Body params: SpaceSummaryParams): Call<SpacesResponse> @Body params: SpaceSummaryParams): SpacesResponse
} }

View File

@ -97,9 +97,12 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), isInitialSync, aggregator, reporter) handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), isInitialSync, aggregator, reporter)
// post room sync validation // post room sync validation
roomSummaryUpdater.validateSpaceRelationship(realm) // roomSummaryUpdater.validateSpaceRelationship(realm)
} }
fun postSyncSpaceHierarchyHandle(realm: Realm) {
roomSummaryUpdater.validateSpaceRelationship(realm)
}
// PRIVATE METHODS ***************************************************************************** // PRIVATE METHODS *****************************************************************************
private fun handleRoomSync(realm: Realm, private fun handleRoomSync(realm: Realm,

View File

@ -132,6 +132,11 @@ internal class SyncResponseHandler @Inject constructor(
Timber.v("On sync completed") Timber.v("On sync completed")
cryptoSyncHandler.onSyncCompleted(syncResponse) cryptoSyncHandler.onSyncCompleted(syncResponse)
// post sync stuffs
monarchy.writeAsync {
roomSyncHandler.postSyncSpaceHierarchyHandle(it)
}
} }
/** /**

View File

@ -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")
// }
// }
// )
}
}

View File

@ -333,6 +333,7 @@ dependencies {
implementation "com.squareup.moshi:moshi-adapters:$moshi_version" implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_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" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
// Log // Log

View File

@ -19,7 +19,12 @@ package im.vector.app
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent 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 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.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -29,10 +34,15 @@ import javax.inject.Singleton
*/ */
// TODO Keep this class for now, will maybe be used fro Space // TODO Keep this class for now, will maybe be used fro Space
@Singleton @Singleton
class AppStateHandler @Inject constructor() : LifecycleObserver { class AppStateHandler @Inject constructor(
private val sessionDataSource: ActiveSessionDataSource,
private val uiStateRepository: UiStateRepository
) : LifecycleObserver {
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
val selectedSpaceDataSource = BehaviorDataSource<Option<RoomSummary>>(Option.empty())
init { init {
// restore current space from ui state // restore current space from ui state
sessionDataSource.currentValue?.orNull()?.let { session -> 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) @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun entersForeground() { fun entersForeground() {
} }

View File

@ -21,6 +21,7 @@ import android.content.res.Resources
import dagger.BindsInstance import dagger.BindsInstance
import dagger.Component import dagger.Component
import im.vector.app.ActiveSessionDataSource import im.vector.app.ActiveSessionDataSource
import im.vector.app.AppStateHandler
import im.vector.app.EmojiCompatFontProvider import im.vector.app.EmojiCompatFontProvider
import im.vector.app.EmojiCompatWrapper import im.vector.app.EmojiCompatWrapper
import im.vector.app.VectorApplication 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.keysrequest.KeyRequestHandler
import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler
import im.vector.app.features.grouplist.SelectedGroupDataSource 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.AvatarRenderer
import im.vector.app.features.home.CurrentSpaceSuggestedRoomListDataSource 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.RoomDetailPendingActionStore
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHolder import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHolder
@ -118,7 +117,7 @@ interface VectorComponent {
fun selectedGroupStore(): SelectedGroupDataSource fun selectedGroupStore(): SelectedGroupDataSource
fun selectedSpaceStore(): SelectedSpaceDataSource fun appStateHandler(): AppStateHandler
fun currentSpaceSuggestedRoomListDataSource(): CurrentSpaceSuggestedRoomListDataSource fun currentSpaceSuggestedRoomListDataSource(): CurrentSpaceSuggestedRoomListDataSource

View File

@ -15,7 +15,6 @@
*/ */
package im.vector.app.core.ui.list package im.vector.app.core.ui.list
import android.graphics.Typeface
import android.view.Gravity import android.view.Gravity
import android.widget.TextView import android.widget.TextView
import androidx.annotation.ColorInt import androidx.annotation.ColorInt

View File

@ -158,7 +158,13 @@ class HomeActivity :
is HomeActivitySharedAction.CloseDrawer -> views.drawerLayout.closeDrawer(GravityCompat.START) is HomeActivitySharedAction.CloseDrawer -> views.drawerLayout.closeDrawer(GravityCompat.START)
is HomeActivitySharedAction.OpenGroup -> { is HomeActivitySharedAction.OpenGroup -> {
views.drawerLayout.closeDrawer(GravityCompat.START) views.drawerLayout.closeDrawer(GravityCompat.START)
// 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) replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
}, 200)
Unit
} }
is HomeActivitySharedAction.OpenSpacePreview -> { is HomeActivitySharedAction.OpenSpacePreview -> {
startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId)) startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId))

View File

@ -23,12 +23,12 @@ import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler
import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.di.HasScreenInjector
import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.grouplist.SelectedGroupDataSource
import im.vector.app.features.grouplist.SelectedSpaceDataSource
import im.vector.app.features.ui.UiStateRepository import im.vector.app.features.ui.UiStateRepository
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -49,6 +49,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
private val session: Session, private val session: Session,
private val uiStateRepository: UiStateRepository, private val uiStateRepository: UiStateRepository,
private val selectedGroupStore: SelectedGroupDataSource, private val selectedGroupStore: SelectedGroupDataSource,
private val appStateHandler: AppStateHandler,
private val stringProvider: StringProvider) private val stringProvider: StringProvider)
: VectorViewModel<HomeDetailViewState, HomeDetailAction, EmptyViewEvents>(initialState) { : VectorViewModel<HomeDetailViewState, HomeDetailAction, EmptyViewEvents>(initialState) {
@ -140,7 +141,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
} }
private fun observeSelectedSpaceStore() { private fun observeSelectedSpaceStore() {
selectedSpaceStore appStateHandler.selectedSpaceDataSource
.observe() .observe()
.subscribe { .subscribe {
setState { setState {

View File

@ -18,10 +18,12 @@ package im.vector.app.features.home.room.list
import im.vector.app.features.home.room.filtered.FilteredRoomFooterItem 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.RoomSummary
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
interface RoomListListener : FilteredRoomFooterItem.FilteredRoomFooterItemListener { interface RoomListListener : FilteredRoomFooterItem.FilteredRoomFooterItemListener {
fun onRoomClicked(room: RoomSummary) fun onRoomClicked(room: RoomSummary)
fun onRoomLongClicked(room: RoomSummary): Boolean fun onRoomLongClicked(room: RoomSummary): Boolean
fun onRejectRoomInvitation(room: RoomSummary) fun onRejectRoomInvitation(room: RoomSummary)
fun onAcceptRoomInvitation(room: RoomSummary) fun onAcceptRoomInvitation(room: RoomSummary)
fun onJoinSuggestedRoom(room: SpaceChildInfo)
} }

View File

@ -17,6 +17,7 @@
package im.vector.app.features.home.room.list package im.vector.app.features.home.room.list
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.FragmentViewModelContext
@ -24,28 +25,31 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel 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.core.resources.StringProvider
import im.vector.app.features.home.RoomListDisplayMode import im.vector.app.features.home.RoomListDisplayMode
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch 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.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.QueryStringValue
import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter
import org.matrix.android.sdk.api.query.RoomTagQueryFilter import org.matrix.android.sdk.api.query.RoomTagQueryFilter
import org.matrix.android.sdk.api.session.Session 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.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.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.Membership 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.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.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.asObservable
import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.rx
import timber.log.Timber import timber.log.Timber
@ -54,17 +58,79 @@ import javax.inject.Inject
class RoomListViewModel @Inject constructor( class RoomListViewModel @Inject constructor(
initialState: RoomListViewState, initialState: RoomListViewState,
private val session: Session, private val session: Session,
private val stringProvider: StringProvider private val stringProvider: StringProvider,
private val appStateHandler: AppStateHandler
) : VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) { ) : VectorViewModel<RoomListViewState, RoomListAction, RoomListViewEvents>(initialState) {
interface Factory { interface Factory {
fun create(initialState: RoomListViewState): RoomListViewModel fun create(initialState: RoomListViewState): RoomListViewModel
} }
private var updatableQuery: UpdatableFilterLivePageResult? = null private var updatableQuery: UpdatableLivePageResult? = null
private var activeSpaceAwareQueries: List<ActiveSpaceQueryUpdater>? = null
interface ActiveSpaceQueryUpdater {
fun updateForSpaceId(roomId: String?)
}
enum class SpaceFilterStrategy {
NORMAL,
NOT_IF_ALL,
NONE
}
init { init {
observeMembershipChanges() 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() { private fun observeMembershipChanges() {
@ -87,47 +153,93 @@ class RoomListViewModel @Inject constructor(
val sections: List<RoomsSection> by lazy { val sections: List<RoomsSection> by lazy {
val sections = mutableListOf<RoomsSection>() val sections = mutableListOf<RoomsSection>()
val activeSpaceAwareQueries = mutableListOf<ActiveSpaceQueryUpdater>()
if (initialState.displayMode == RoomListDisplayMode.PEOPLE) { 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.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM 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.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) 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.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM it.roomCategoryFilter = RoomCategoryFilter.ONLY_DM
} }
} else if (initialState.displayMode == RoomListDisplayMode.ROOMS) { } 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.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS 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.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
it.roomTagQueryFilter = RoomTagQueryFilter(true, null, null) 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.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
it.roomTagQueryFilter = RoomTagQueryFilter(false, false, false) 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.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
it.roomTagQueryFilter = RoomTagQueryFilter(null, true, null) 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.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS it.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true) it.roomTagQueryFilter = RoomTagQueryFilter(null, null, true)
@ -147,12 +259,24 @@ class RoomListViewModel @Inject constructor(
} }
) )
} else if (initialState.displayMode == RoomListDisplayMode.NOTIFICATIONS) { } 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.memberships = listOf(Membership.INVITE)
it.roomCategoryFilter = RoomCategoryFilter.ALL 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.memberships = listOf(Membership.JOIN)
it.roomCategoryFilter = RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS it.roomCategoryFilter = RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS
} }
@ -176,15 +300,67 @@ class RoomListViewModel @Inject constructor(
} }
private fun addSection(sections: MutableList<RoomsSection>, private fun addSection(sections: MutableList<RoomsSection>,
activeSpaceUpdaters: MutableList<ActiveSpaceQueryUpdater>,
@StringRes nameRes: Int, @StringRes nameRes: Int,
notifyOfLocalEcho: Boolean = false, notifyOfLocalEcho: Boolean = false,
spaceFilterStrategy: SpaceFilterStrategy = SpaceFilterStrategy.NONE,
query: (RoomSummaryQueryParams.Builder) -> Unit) { query: (RoomSummaryQueryParams.Builder) -> Unit) {
withQueryParams( withQueryParams(
{ query.invoke(it) }, { query.invoke(it) },
{ roomQueryParams -> { roomQueryParams ->
val name = stringProvider.getString(nameRes) 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 -> .let { livePagedList ->
// use it also as a source to update count // use it also as a source to update count
@ -206,6 +382,7 @@ class RoomListViewModel @Inject constructor(
) )
} }
} }
) )
} }
@ -242,13 +419,12 @@ class RoomListViewModel @Inject constructor(
roomFilter = action.filter roomFilter = action.filter
) )
} }
updatableQuery?.updateQuery( updatableQuery?.updateQuery {
roomSummaryQueryParams { it.copy(
memberships = Membership.activeMemberships()
displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE) displayName = QueryStringValue.Contains(action.filter, QueryStringValue.Case.INSENSITIVE)
}
) )
} }
}
private fun handleAcceptInvitation(action: RoomListAction.AcceptInvitation) = withState { state -> private fun handleAcceptInvitation(action: RoomListAction.AcceptInvitation) = withState { state ->
val roomId = action.roomSummary.roomId val roomId = action.roomSummary.roomId

View File

@ -16,12 +16,14 @@
package im.vector.app.features.home.room.list package im.vector.app.features.home.room.list
import im.vector.app.AppStateHandler
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Provider import javax.inject.Provider
class RoomListViewModelFactory @Inject constructor(private val session: Provider<Session>, class RoomListViewModelFactory @Inject constructor(private val session: Provider<Session>,
private val appStateHandler: AppStateHandler,
private val stringProvider: StringProvider) private val stringProvider: StringProvider)
: RoomListViewModel.Factory { : RoomListViewModel.Factory {
@ -29,7 +31,8 @@ class RoomListViewModelFactory @Inject constructor(private val session: Provider
return RoomListViewModel( return RoomListViewModel(
initialState, initialState,
session.get(), session.get(),
stringProvider stringProvider,
appStateHandler
) )
} }
} }

View File

@ -16,16 +16,22 @@
package im.vector.app.features.home.room.list package im.vector.app.features.home.room.list
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.app.features.home.RoomListDisplayMode 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.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( data class RoomListViewState(
val displayMode: RoomListDisplayMode, val displayMode: RoomListDisplayMode,
val roomFilter: String = "", val roomFilter: String = "",
val roomMembershipChanges: Map<String, ChangeMembershipState> = emptyMap() val roomMembershipChanges: Map<String, ChangeMembershipState> = emptyMap(),
val asyncSuggestedRooms: Async<List<SpaceChildInfo>> = Uninitialized, val asyncSuggestedRooms: Async<List<SpaceChildInfo>> = Uninitialized,
val suggestedRoomJoiningState: Map<String, Async<Unit>> = emptyMap() val suggestedRoomJoiningState: Map<String, Async<Unit>> = emptyMap(),
val currentUserName: String? = null,
val currentSpace: Async<RoomSummary?> = Uninitialized
) : MvRxState { ) : MvRxState {
constructor(args: RoomListParams) : this(displayMode = args.displayMode) constructor(args: RoomListParams) : this(displayMode = args.displayMode)

View File

@ -30,6 +30,7 @@ import androidx.core.app.TaskStackBuilder
import androidx.core.util.Pair import androidx.core.util.Pair
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import arrow.core.Option import arrow.core.Option
import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.fatalError 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.crypto.verification.VerificationBottomSheet
import im.vector.app.features.debug.DebugMenuActivity import im.vector.app.features.debug.DebugMenuActivity
import im.vector.app.features.devtools.RoomDevToolActivity 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.RoomDetailActivity
import im.vector.app.features.home.room.detail.RoomDetailArgs import im.vector.app.features.home.room.detail.RoomDetailArgs
import im.vector.app.features.home.room.detail.search.SearchActivity import im.vector.app.features.home.room.detail.search.SearchActivity
@ -92,7 +92,7 @@ class DefaultNavigator @Inject constructor(
private val sessionHolder: ActiveSessionHolder, private val sessionHolder: ActiveSessionHolder,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val widgetArgsBuilder: WidgetArgsBuilder, private val widgetArgsBuilder: WidgetArgsBuilder,
private val selectedSpaceDataSource: SelectedSpaceDataSource, private val appStateHandler: AppStateHandler,
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider
) : Navigator { ) : Navigator {
@ -114,7 +114,7 @@ class DefaultNavigator @Inject constructor(
sessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(spaceId)?.spaceSummary()?.let { sessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(spaceId)?.spaceSummary()?.let {
Timber.d("## Nav: Switching to space $spaceId / ${it.name}") Timber.d("## Nav: Switching to space $spaceId / ${it.name}")
selectedSpaceDataSource.post(Option.just(it)) appStateHandler.selectedSpaceDataSource.post(Option.just(it))
} ?: kotlin.run { } ?: kotlin.run {
Timber.d("## Nav: Failed to switch to space $spaceId") Timber.d("## Nav: Failed to switch to space $spaceId")
} }
@ -252,7 +252,7 @@ class DefaultNavigator @Inject constructor(
} }
override fun openRoomDirectory(context: Context, initialFilter: String) { 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) sessionHolder.getSafeActiveSession()?.getRoomSummary(it.roomId)
} }
if (selectedSpace == null) { if (selectedSpace == null) {
@ -276,7 +276,7 @@ class DefaultNavigator @Inject constructor(
} }
override fun openInviteUsersToRoom(context: Context, roomId: String) { 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) sessionHolder.getSafeActiveSession()?.getRoomSummary(it.roomId)
} }
if (vectorPreferences.labSpaces() && selectedSpace != null) { if (vectorPreferences.labSpaces() && selectedSpace != null) {

View File

@ -24,10 +24,10 @@ import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.grouplist.SelectedSpaceDataSource
import im.vector.app.features.ui.UiStateRepository import im.vector.app.features.ui.UiStateRepository
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.functions.BiFunction 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" const val ALL_COMMUNITIES_GROUP_ID = "+ALL_COMMUNITIES_GROUP_ID"
class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState, class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState,
private val selectedSpaceDataSource: SelectedSpaceDataSource, private val appStateHandler: AppStateHandler,
private val session: Session, private val session: Session,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val uiStateRepository: UiStateRepository private val uiStateRepository: UiStateRepository
@ -68,7 +68,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
init { init {
observeSpaceSummaries() observeSpaceSummaries()
observeSelectionState() observeSelectionState()
selectedSpaceDataSource appStateHandler.selectedSpaceDataSource
.observe() .observe()
.subscribe { .subscribe {
if (currentGroupId != it.orNull()?.roomId) { if (currentGroupId != it.orNull()?.roomId) {
@ -91,7 +91,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
_viewEvents.post(SpaceListViewEvents.OpenSpace) _viewEvents.post(SpaceListViewEvents.OpenSpace)
} }
val optionGroup = Option.just(spaceSummary) val optionGroup = Option.just(spaceSummary)
selectedSpaceDataSource.post(optionGroup) appStateHandler.selectedSpaceDataSource.post(optionGroup)
} else { } else {
// If selected group is null we force to default. It can happens when leaving the selected group. // If selected group is null we force to default. It can happens when leaving the selected group.
setState { setState {

View File

@ -26,8 +26,7 @@ import im.vector.app.core.utils.DebouncedClickListener
import im.vector.app.databinding.FragmentSpaceCreateChooseTypeBinding import im.vector.app.databinding.FragmentSpaceCreateChooseTypeBinding
import javax.inject.Inject import javax.inject.Inject
class ChooseSpaceTypeFragment @Inject constructor( class ChooseSpaceTypeFragment @Inject constructor() : VectorBaseFragment<FragmentSpaceCreateChooseTypeBinding>() {
) : VectorBaseFragment<FragmentSpaceCreateChooseTypeBinding>() {
private val sharedViewModel: CreateSpaceViewModel by activityViewModel() private val sharedViewModel: CreateSpaceViewModel by activityViewModel()
@ -47,4 +46,3 @@ class ChooseSpaceTypeFragment @Inject constructor(
})) }))
} }
} }