Fix space filtering
This commit is contained in:
parent
8a35a786b4
commit
0d3c2b4bef
|
@ -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");
|
* 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.
|
||||||
* You may obtain a copy of the License at
|
* 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
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@ -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())
|
|
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,10 +223,10 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
|
||||||
|
|
||||||
queryParams.roomCategoryFilter?.let {
|
queryParams.roomCategoryFilter?.let {
|
||||||
when (it) {
|
when (it) {
|
||||||
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.ONLY_WITH_NOTIFICATIONS -> query.greaterThan(RoomSummaryEntityFields.NOTIFICATION_COUNT, 0)
|
||||||
RoomCategoryFilter.ALL -> {
|
RoomCategoryFilter.ALL -> {
|
||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,9 +250,27 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
|
||||||
query.equalTo(RoomSummaryEntityFields.ROOM_TYPE, it)
|
query.equalTo(RoomSummaryEntityFields.ROOM_TYPE, it)
|
||||||
}
|
}
|
||||||
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.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
|
return query
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
.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<RoomSummaryEntity>().toMutableSet()
|
||||||
|
}
|
||||||
|
.toMap()
|
||||||
|
|
||||||
realm.where(RoomSummaryEntity::class.java).findAll().forEach { roomSummary ->
|
lookupMap.keys.forEach { lookedUp ->
|
||||||
if (roomSummary.roomType == RoomType.SPACE) {
|
if (lookedUp.roomType == RoomType.SPACE) {
|
||||||
roomSummary.children.clearWith { it.deleteFromRealm() }
|
// get childrens
|
||||||
roomSummary.children.addAll(
|
|
||||||
RoomChildRelationInfo(realm, roomSummary.roomId).getDirectChildrenDescriptions()
|
lookedUp.children.clearWith { it.deleteFromRealm() }
|
||||||
.map {
|
|
||||||
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with info $it")
|
RoomChildRelationInfo(realm, lookedUp.roomId).getDirectChildrenDescriptions().forEach { child ->
|
||||||
realm.createObject<SpaceChildSummaryEntity>().apply {
|
|
||||||
this.childRoomId = it.roomId
|
lookedUp.children.add(
|
||||||
this.childSummaryEntity = RoomSummaryEntity.where(realm, it.roomId).findFirst()
|
realm.createObject<SpaceChildSummaryEntity>().apply {
|
||||||
this.order = it.order
|
this.childRoomId = child.roomId
|
||||||
this.autoJoin = it.autoJoin
|
this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst()
|
||||||
this.viaServers.addAll(it.viaServers)
|
this.order = child.order
|
||||||
// this.level = 0
|
this.autoJoin = child.autoJoin
|
||||||
}.also {
|
this.viaServers.addAll(child.viaServers)
|
||||||
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with children $it")
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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<SpaceParentSummaryEntity>().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
|
// Simple algorithm to break cycles
|
||||||
roomSummary.parents.clearWith { it.deleteFromRealm() }
|
// Need more work to decide how to break, probably need to be as consistent as possible
|
||||||
roomSummary.parents.addAll(
|
// and also find best way to root the tree
|
||||||
RoomChildRelationInfo(realm, roomSummary.roomId).getParentDescriptions()
|
|
||||||
.map { parentInfo ->
|
val graph = Graph()
|
||||||
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with parent info $parentInfo")
|
lookupMap
|
||||||
realm.createObject<SpaceParentSummaryEntity>().apply {
|
// focus only on spaces, as room are just leaf
|
||||||
this.parentRoomId = parentInfo.roomId
|
.filter { it.key.roomType == RoomType.SPACE }
|
||||||
this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst()
|
.forEach { (sum, children) ->
|
||||||
this.canonical = parentInfo.canonical
|
graph.getOrCreateNode(sum.roomId)
|
||||||
this.viaServers.addAll(parentInfo.viaServers)
|
children.forEach {
|
||||||
// this.level = 0
|
graph.addEdge(it.roomId, sum.roomId)
|
||||||
}.also {
|
}
|
||||||
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with parent $it")
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
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 -> {
|
is HomeActivitySharedAction.OpenSpacePreview -> {
|
||||||
startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId))
|
startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId))
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
||||||
|
@ -83,7 +84,7 @@ class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: Ho
|
||||||
override fun handle(action: HomeDetailAction) {
|
override fun handle(action: HomeDetailAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is HomeDetailAction.SwitchDisplayMode -> handleSwitchDisplayMode(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() {
|
private fun observeSelectedSpaceStore() {
|
||||||
selectedSpaceStore
|
appStateHandler.selectedSpaceDataSource
|
||||||
.observe()
|
.observe()
|
||||||
.subscribe {
|
.subscribe {
|
||||||
setState {
|
setState {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,12 +419,11 @@ 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 ->
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue