Space hierachy SDK updates
This commit is contained in:
parent
80f1c6cb2d
commit
0c5ca9f51b
|
@ -39,7 +39,6 @@ import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.sync.SyncState
|
||||
import org.matrix.android.sdk.api.session.user.model.User
|
||||
|
@ -68,13 +67,20 @@ class RxSession(private val session: Session) {
|
|||
}
|
||||
}
|
||||
|
||||
fun liveSpaceSummaries(queryParams: SpaceSummaryQueryParams): Observable<List<SpaceSummary>> {
|
||||
fun liveSpaceSummaries(queryParams: SpaceSummaryQueryParams): Observable<List<RoomSummary>> {
|
||||
return session.spaceService().getSpaceSummariesLive(queryParams).asObservable()
|
||||
.startWithCallable {
|
||||
session.spaceService().getSpaceSummaries(queryParams)
|
||||
}
|
||||
}
|
||||
|
||||
fun liveFlattenRoomSummaryChildOf(spaceId: String?): Observable<List<RoomSummary>> {
|
||||
return session.getFlattenRoomSummaryChildOfLive(spaceId).asObservable()
|
||||
.startWithCallable {
|
||||
session.getFlattenRoomSummaryChildOf(spaceId)
|
||||
}
|
||||
}
|
||||
|
||||
fun liveBreadcrumbs(queryParams: RoomSummaryQueryParams): Observable<List<RoomSummary>> {
|
||||
return session.getBreadcrumbsLive(queryParams).asObservable()
|
||||
.startWithCallable {
|
||||
|
|
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
* 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.session.space
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.Observer
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.junit.runners.MethodSorters
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
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.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.common.CommonTestHelper
|
||||
import org.matrix.android.sdk.common.SessionTestParams
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class SpaceHierarchyTest : InstrumentedTest {
|
||||
|
||||
private val commonTestHelper = CommonTestHelper(context())
|
||||
|
||||
@Test
|
||||
fun createCanonicalChildRelation() {
|
||||
val session = commonTestHelper.createAccount("Jhon", SessionTestParams(true))
|
||||
val spaceName = "My Space"
|
||||
val topic = "A public space for test"
|
||||
val spaceId: String
|
||||
runBlocking {
|
||||
spaceId = session.spaceService().createSpace(spaceName, topic, null, true)
|
||||
// wait a bit to let the summry update it self :/
|
||||
delay(400)
|
||||
}
|
||||
|
||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||
|
||||
val roomId = commonTestHelper.doSync<String> {
|
||||
session.createRoom(CreateRoomParams().apply { name = "General" }, it)
|
||||
}
|
||||
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
|
||||
runBlocking {
|
||||
syncedSpace!!.addChildren(roomId, viaServers, null, true)
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
session.spaceService().setSpaceParent(roomId, spaceId, true, viaServers)
|
||||
}
|
||||
|
||||
Thread.sleep(9000)
|
||||
|
||||
val parents = session.getRoom(roomId)?.roomSummary()?.spaceParents
|
||||
val canonicalParents = session.getRoom(roomId)?.roomSummary()?.spaceParents?.filter { it.canonical == true }
|
||||
|
||||
parents?.forEach {
|
||||
Log.d("## TEST", "parent : $it")
|
||||
}
|
||||
|
||||
assertNotNull(parents)
|
||||
assertEquals(1, parents.size)
|
||||
assertEquals(spaceName, parents.first().roomSummary?.name)
|
||||
|
||||
assertNotNull(canonicalParents)
|
||||
assertEquals(1, canonicalParents.size)
|
||||
assertEquals(spaceName, canonicalParents.first().roomSummary?.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCreateChildRelations() {
|
||||
val session = commonTestHelper.createAccount("Jhon", SessionTestParams(true))
|
||||
val spaceName = "My Space"
|
||||
val topic = "A public space for test"
|
||||
Log.d("## TEST", "Before")
|
||||
val spaceId = runBlocking {
|
||||
session.spaceService().createSpace(spaceName, topic, null, true)
|
||||
}
|
||||
|
||||
Log.d("## TEST", "created space $spaceId ${Thread.currentThread()}")
|
||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||
|
||||
val children = listOf("General" to true /*canonical*/, "Random" to false)
|
||||
|
||||
val roomIdList = children.map {
|
||||
commonTestHelper.doSync<String> { cb ->
|
||||
session.createRoom(CreateRoomParams().apply { name = it.first }, cb)
|
||||
} to it.second
|
||||
}
|
||||
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
|
||||
runBlocking {
|
||||
roomIdList.forEach { entry ->
|
||||
syncedSpace!!.addChildren(entry.first, viaServers, null, true)
|
||||
}
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
roomIdList.forEach {
|
||||
session.spaceService().setSpaceParent(it.first, spaceId, it.second, viaServers)
|
||||
}
|
||||
delay(400)
|
||||
}
|
||||
|
||||
roomIdList.forEach {
|
||||
val parents = session.getRoom(it.first)?.roomSummary()?.spaceParents
|
||||
val canonicalParents = session.getRoom(it.first)?.roomSummary()?.spaceParents?.filter { it.canonical == true }
|
||||
|
||||
assertNotNull(parents)
|
||||
assertEquals(1, parents.size, "Unexpected number of parent")
|
||||
assertEquals(spaceName, parents.first().roomSummary?.name, "Unexpected parent name ")
|
||||
assertEquals(if (it.second) 1 else 0, canonicalParents?.size ?: 0, "Parent of ${it.first} should be canonical ${it.second}")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFilteringBySpace() {
|
||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
|
||||
val spaceAInfo = createPublicSpace(session, "SpaceA", listOf(
|
||||
Triple("A1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("A2", true, true)
|
||||
))
|
||||
|
||||
val spaceBInfo = createPublicSpace(session, "SpaceB", listOf(
|
||||
Triple("B1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("B2", true, true),
|
||||
Triple("B3", true, true)
|
||||
))
|
||||
|
||||
val spaceCInfo = createPublicSpace(session, "SpaceC", listOf(
|
||||
Triple("C1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("C2", true, true)
|
||||
))
|
||||
|
||||
// add C as a subspace of A
|
||||
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
runBlocking {
|
||||
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||
}
|
||||
|
||||
// Create orphan rooms
|
||||
|
||||
val orphan1 = commonTestHelper.doSync<String> { cb ->
|
||||
session.createRoom(CreateRoomParams().apply { name = "O1" }, cb)
|
||||
}
|
||||
val orphan2 = commonTestHelper.doSync<String> { cb ->
|
||||
session.createRoom(CreateRoomParams().apply { name = "O2" }, cb)
|
||||
}
|
||||
|
||||
val allRooms = session.getRoomSummaries(roomSummaryQueryParams { excludeType = listOf(RoomType.SPACE) })
|
||||
|
||||
assertEquals(9, allRooms.size, "Unexpected number of rooms")
|
||||
|
||||
val orphans = session.getFlattenRoomSummaryChildOf(null)
|
||||
|
||||
assertEquals(2, orphans.size, "Unexpected number of orphan rooms")
|
||||
assertTrue(orphans.indexOfFirst { it.roomId == orphan1 } != -1, "O1 should be an orphan")
|
||||
assertTrue(orphans.indexOfFirst { it.roomId == orphan2 } != -1, "O2 should be an orphan ${orphans.map { it.name }}")
|
||||
|
||||
val aChildren = session.getFlattenRoomSummaryChildOf(spaceAInfo.spaceId)
|
||||
|
||||
assertEquals(4, aChildren.size, "Unexpected number of flatten child rooms")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "A1" } != -1, "A1 should be a child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "A2" } != -1, "A2 should be a child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "C1" } != -1, "CA should be a grand child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "C2" } != -1, "A1 should be a grand child of A")
|
||||
|
||||
// Add a non canonical child and check that it does not appear as orphan
|
||||
val a3 = commonTestHelper.doSync<String> { cb ->
|
||||
session.createRoom(CreateRoomParams().apply { name = "A3" }, cb)
|
||||
}
|
||||
runBlocking {
|
||||
spaceA!!.addChildren(a3, viaServers, null, false)
|
||||
delay(400)
|
||||
// here we do not set the parent!!
|
||||
}
|
||||
|
||||
val orphansUpdate = session.getFlattenRoomSummaryChildOf(null)
|
||||
assertEquals(2, orphansUpdate.size, "Unexpected number of orphan rooms ${orphansUpdate.map { it.name }}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBreakCycle() {
|
||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
|
||||
val spaceAInfo = createPublicSpace(session, "SpaceA", listOf(
|
||||
Triple("A1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("A2", true, true)
|
||||
))
|
||||
|
||||
val spaceCInfo = createPublicSpace(session, "SpaceC", listOf(
|
||||
Triple("C1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("C2", true, true)
|
||||
))
|
||||
|
||||
// add C as a subspace of A
|
||||
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
runBlocking {
|
||||
spaceA!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||
session.spaceService().setSpaceParent(spaceCInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||
}
|
||||
|
||||
// add back A as subspace of C
|
||||
runBlocking {
|
||||
val spaceC = session.spaceService().getSpace(spaceCInfo.spaceId)
|
||||
spaceC!!.addChildren(spaceAInfo.spaceId, viaServers, null, true)
|
||||
}
|
||||
|
||||
Thread.sleep(1000)
|
||||
|
||||
// A -> C -> A
|
||||
|
||||
val aChildren = session.getFlattenRoomSummaryChildOf(spaceAInfo.spaceId)
|
||||
|
||||
assertEquals(4, aChildren.size, "Unexpected number of flatten child rooms ${aChildren.map { it.name }}")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "A1" } != -1, "A1 should be a child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "A2" } != -1, "A2 should be a child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "C1" } != -1, "CA should be a grand child of A")
|
||||
assertTrue(aChildren.indexOfFirst { it.name == "C2" } != -1, "A1 should be a grand child of A")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLiveFlatChildren() {
|
||||
val session = commonTestHelper.createAccount("John", SessionTestParams(true))
|
||||
|
||||
val spaceAInfo = createPublicSpace(session, "SpaceA", listOf(
|
||||
Triple("A1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("A2", true, true)
|
||||
))
|
||||
|
||||
val spaceBInfo = createPublicSpace(session, "SpaceB", listOf(
|
||||
Triple("B1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("B2", true, true),
|
||||
Triple("B3", true, true)
|
||||
))
|
||||
|
||||
// add B as a subspace of A
|
||||
val spaceA = session.spaceService().getSpace(spaceAInfo.spaceId)
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
runBlocking {
|
||||
spaceA!!.addChildren(spaceBInfo.spaceId, viaServers, null, true)
|
||||
session.spaceService().setSpaceParent(spaceBInfo.spaceId, spaceAInfo.spaceId, true, viaServers)
|
||||
}
|
||||
|
||||
val flatAChildren = runBlocking(Dispatchers.Main) {
|
||||
session.getFlattenRoomSummaryChildOfLive(spaceAInfo.spaceId)
|
||||
}
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
|
||||
val childObserver = object : Observer<List<RoomSummary>> {
|
||||
override fun onChanged(children: List<RoomSummary>?) {
|
||||
// Log.d("## TEST", "Space A flat children update : ${children?.map { it.name }}")
|
||||
System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}")
|
||||
if (children?.indexOfFirst { it.name == "C1" } != -1
|
||||
&& children?.indexOfFirst { it.name == "C2" } != -1
|
||||
) {
|
||||
// B1 has been added live!
|
||||
latch.countDown()
|
||||
flatAChildren.removeObserver(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val spaceCInfo = createPublicSpace(session, "SpaceC", listOf(
|
||||
Triple("C1", true /*auto-join*/, true/*canonical*/),
|
||||
Triple("C2", true, true)
|
||||
))
|
||||
|
||||
// add C as subspace of B
|
||||
runBlocking {
|
||||
val spaceB = session.spaceService().getSpace(spaceBInfo.spaceId)
|
||||
spaceB!!.addChildren(spaceCInfo.spaceId, viaServers, null, true)
|
||||
}
|
||||
|
||||
// C1 and C2 should be in flatten child of A now
|
||||
|
||||
GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) }
|
||||
}
|
||||
|
||||
// Test part one of the rooms
|
||||
|
||||
val bRoomId = spaceBInfo.roomIds.first()
|
||||
val bRoom = session.getRoom(bRoomId)
|
||||
|
||||
commonTestHelper.waitWithLatch { latch ->
|
||||
|
||||
val childObserver = object : Observer<List<RoomSummary>> {
|
||||
override fun onChanged(children: List<RoomSummary>?) {
|
||||
System.out.println("## TEST | Space A flat children update : ${children?.map { it.name }}")
|
||||
if (children?.any { it.roomId == bRoomId } == false) {
|
||||
// B1 has been added live!
|
||||
latch.countDown()
|
||||
flatAChildren.removeObserver(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// part from b room
|
||||
commonTestHelper.doSync<Unit> {
|
||||
bRoom!!.leave(null, it)
|
||||
}
|
||||
// The room should have disapear from flat children
|
||||
GlobalScope.launch(Dispatchers.Main) { flatAChildren.observeForever(childObserver) }
|
||||
}
|
||||
}
|
||||
|
||||
data class TestSpaceCreationResult(
|
||||
val spaceId: String,
|
||||
val roomIds: List<String>
|
||||
)
|
||||
|
||||
private fun createPublicSpace(session: Session,
|
||||
spaceName: String,
|
||||
childInfo: List<Triple<String, Boolean, Boolean?>>
|
||||
/** Name, auto-join, canonical*/
|
||||
): TestSpaceCreationResult {
|
||||
val spaceId = runBlocking {
|
||||
session.spaceService().createSpace(spaceName, "Test Topic", null, true)
|
||||
}
|
||||
|
||||
val syncedSpace = session.spaceService().getSpace(spaceId)
|
||||
val viaServers = listOf(session.sessionParams.homeServerHost ?: "")
|
||||
|
||||
val roomIds =
|
||||
childInfo.map { entry ->
|
||||
commonTestHelper.doSync<String> { cb ->
|
||||
session.createRoom(CreateRoomParams().apply { name = entry.first }, cb)
|
||||
}
|
||||
}
|
||||
|
||||
roomIds.forEachIndexed { index, roomId ->
|
||||
runBlocking {
|
||||
syncedSpace!!.addChildren(roomId, viaServers, null, childInfo[index].second)
|
||||
val canonical = childInfo[index].third
|
||||
if (canonical != null) {
|
||||
session.spaceService().setSpaceParent(roomId, spaceId, canonical, viaServers)
|
||||
}
|
||||
}
|
||||
}
|
||||
return TestSpaceCreationResult(spaceId, roomIds)
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineService
|
|||
import org.matrix.android.sdk.api.session.room.typing.TypingService
|
||||
import org.matrix.android.sdk.api.session.room.uploads.UploadsService
|
||||
import org.matrix.android.sdk.api.session.search.SearchResult
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
|
||||
/**
|
||||
|
@ -90,5 +92,6 @@ interface Room :
|
|||
limit: Int,
|
||||
beforeLimit: Int,
|
||||
afterLimit: Int,
|
||||
// fun getSpaceParents(): List<SpaceSummary>
|
||||
includeProfile: Boolean): SearchResult
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.paging.PagedList
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
|
@ -197,4 +198,8 @@ interface RoomService {
|
|||
.setEnablePlaceholders(false)
|
||||
.setPrefetchDistance(10)
|
||||
.build()
|
||||
|
||||
fun getFlattenRoomSummaryChildOf(spaceId: String?, memberships: List<Membership> = Membership.activeMemberships()) : List<RoomSummary>
|
||||
|
||||
fun getFlattenRoomSummaryChildOfLive(spaceId: String?, memberships: List<Membership> = Membership.activeMemberships()): LiveData<List<RoomSummary>>
|
||||
}
|
||||
|
|
|
@ -21,11 +21,27 @@ import org.matrix.android.sdk.api.query.RoomCategoryFilter
|
|||
import org.matrix.android.sdk.api.query.RoomTagQueryFilter
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||
|
||||
fun roomSummaryQueryParams(init: (RoomSummaryQueryParams.Builder.() -> Unit) = {}): RoomSummaryQueryParams {
|
||||
return RoomSummaryQueryParams.Builder().apply(init).build()
|
||||
}
|
||||
|
||||
fun spaceSummaryQueryParams(init: (RoomSummaryQueryParams.Builder.() -> Unit) = {}): SpaceSummaryQueryParams {
|
||||
return RoomSummaryQueryParams.Builder()
|
||||
.apply(init)
|
||||
.apply {
|
||||
this.includeType = listOf(RoomType.SPACE)
|
||||
this.excludeType = null
|
||||
this.roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
|
||||
}.build()
|
||||
}
|
||||
|
||||
enum class RoomCategoryFilter {
|
||||
ONLY_DM,
|
||||
ONLY_ROOMS,
|
||||
ALL
|
||||
}
|
||||
/**
|
||||
* This class can be used to filter room summaries to use with:
|
||||
* [org.matrix.android.sdk.api.session.room.Room] and [org.matrix.android.sdk.api.session.room.RoomService]
|
||||
|
@ -37,7 +53,8 @@ data class RoomSummaryQueryParams(
|
|||
val memberships: List<Membership>,
|
||||
val roomCategoryFilter: RoomCategoryFilter?,
|
||||
val roomTagQueryFilter: RoomTagQueryFilter?
|
||||
val excludeType: List<String?>
|
||||
val excludeType: List<String?>?,
|
||||
val includeType: List<String?>?
|
||||
) {
|
||||
|
||||
class Builder {
|
||||
|
@ -49,6 +66,7 @@ data class RoomSummaryQueryParams(
|
|||
var roomCategoryFilter: RoomCategoryFilter? = RoomCategoryFilter.ALL
|
||||
var roomTagQueryFilter: RoomTagQueryFilter? = null
|
||||
var excludeType: List<String?> = listOf(RoomType.SPACE)
|
||||
var includeType: List<String?>? = null
|
||||
|
||||
fun build() = RoomSummaryQueryParams(
|
||||
roomId = roomId,
|
||||
|
@ -56,8 +74,9 @@ data class RoomSummaryQueryParams(
|
|||
canonicalAlias = canonicalAlias,
|
||||
memberships = memberships,
|
||||
roomCategoryFilter = roomCategoryFilter,
|
||||
roomTagQueryFilter = roomTagQueryFilter
|
||||
excludeType = excludeType
|
||||
roomTagQueryFilter = roomTagQueryFilter,
|
||||
excludeType = excludeType,
|
||||
includeType = includeType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,18 +27,18 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
|||
* It can be retrieved by [org.matrix.android.sdk.api.session.room.Room] and [org.matrix.android.sdk.api.session.room.RoomService]
|
||||
*/
|
||||
data class RoomSummary constructor(
|
||||
override val roomId: String,
|
||||
val roomId: String,
|
||||
// Computed display name
|
||||
override val displayName: String = "",
|
||||
override val name: String = "",
|
||||
override val topic: String = "",
|
||||
override val avatarUrl: String = "",
|
||||
override val canonicalAlias: String? = null,
|
||||
override val aliases: List<String> = emptyList(),
|
||||
override val joinedMembersCount: Int? = 0,
|
||||
override val invitedMembersCount: Int? = 0,
|
||||
val displayName: String = "",
|
||||
val name: String = "",
|
||||
val topic: String = "",
|
||||
val avatarUrl: String = "",
|
||||
val canonicalAlias: String? = null,
|
||||
val aliases: List<String> = emptyList(),
|
||||
val joinedMembersCount: Int? = 0,
|
||||
val invitedMembersCount: Int? = 0,
|
||||
val latestPreviewableEvent: TimelineEvent? = null,
|
||||
override val otherMemberIds: List<String> = emptyList(),
|
||||
val otherMemberIds: List<String> = emptyList(),
|
||||
val isDirect: Boolean = false,
|
||||
val notificationCount: Int = 0,
|
||||
val highlightCount: Int = 0,
|
||||
|
@ -55,8 +55,10 @@ data class RoomSummary constructor(
|
|||
val breadcrumbsIndex: Int = NOT_IN_BREADCRUMBS,
|
||||
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel? = null,
|
||||
val hasFailedSending: Boolean = false,
|
||||
override val roomType: String? = null
|
||||
) : IRoomSummary {
|
||||
val roomType: String? = null,
|
||||
val spaceParents: List<SpaceParentInfo>? = null,
|
||||
val children: List<SpaceChildInfo>? = null
|
||||
) {
|
||||
|
||||
val isVersioned: Boolean
|
||||
get() = versioningState != VersioningState.NONE
|
||||
|
|
|
@ -17,8 +17,16 @@
|
|||
package org.matrix.android.sdk.api.session.room.model
|
||||
|
||||
data class SpaceChildInfo(
|
||||
val roomSummary: IRoomSummary?,
|
||||
val childRoomId: String,
|
||||
// We might not know this child at all,
|
||||
// i.e we just know it exists but no info on type/name/etc..
|
||||
val isKnown: Boolean,
|
||||
val roomType: String?,
|
||||
val name: String?,
|
||||
val topic: String?,
|
||||
val avatarUrl: String?,
|
||||
val order: String?,
|
||||
val activeMemberCount: Int?,
|
||||
val autoJoin: Boolean,
|
||||
val viaServers: List<String>
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -16,16 +16,9 @@
|
|||
|
||||
package org.matrix.android.sdk.api.session.room.model
|
||||
|
||||
interface IRoomSummary {
|
||||
val roomId: String
|
||||
val displayName: String
|
||||
val name: String
|
||||
val topic: String
|
||||
val avatarUrl: String
|
||||
val canonicalAlias: String?
|
||||
val aliases: List<String>
|
||||
val joinedMembersCount: Int?
|
||||
val invitedMembersCount: Int?
|
||||
val otherMemberIds: List<String>
|
||||
val roomType: String?
|
||||
}
|
||||
data class SpaceParentInfo(
|
||||
val parentId: String?,
|
||||
val roomSummary: RoomSummary?,
|
||||
val canonical: Boolean?,
|
||||
val viaServers: List<String>
|
||||
)
|
|
@ -24,9 +24,9 @@ interface Space {
|
|||
fun asRoom() : Room
|
||||
|
||||
/**
|
||||
* A current snapshot of [RoomSummary] associated with the room
|
||||
* A current snapshot of [RoomSummary] associated with the space
|
||||
*/
|
||||
fun spaceSummary(): SpaceSummary?
|
||||
fun spaceSummary(): RoomSummary?
|
||||
|
||||
suspend fun addChildren(roomId: String, viaServers: List<String>, order: String?, autoJoin: Boolean = false)
|
||||
|
||||
|
|
|
@ -60,9 +60,9 @@ interface SpaceService {
|
|||
* Get a live list of space summaries. This list is refreshed as soon as the data changes.
|
||||
* @return the [LiveData] of List[SpaceSummary]
|
||||
*/
|
||||
fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<SpaceSummary>>
|
||||
fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<RoomSummary>>
|
||||
|
||||
fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List<SpaceSummary>
|
||||
fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List<RoomSummary>
|
||||
|
||||
sealed class JoinSpaceResult {
|
||||
object Success : JoinSpaceResult()
|
||||
|
@ -79,4 +79,14 @@ interface SpaceService {
|
|||
viaServers: List<String> = emptyList()): JoinSpaceResult
|
||||
|
||||
suspend fun rejectInvite(spaceId: String, reason: String?)
|
||||
|
||||
// fun getSpaceParentsOfRoom(roomId: String) : List<SpaceSummary>
|
||||
|
||||
/**
|
||||
* Let this room declare that it has a parent.
|
||||
* @param canonical true if it should be the main parent of this room
|
||||
* In practice, well behaved rooms should only have one canonical parent, but given this is not enforced:
|
||||
* if multiple are present the client should select the one with the lowest room ID, as determined via a lexicographic utf-8 ordering.
|
||||
*/
|
||||
suspend fun setSpaceParent(childRoomId: String, parentSpaceId: String, canonical: Boolean, viaServers: List<String>)
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.api.session.space
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.IRoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
|
||||
data class SpaceSummary(
|
||||
val spaceId: String,
|
||||
val roomSummary: RoomSummary,
|
||||
val children: List<SpaceChildInfo>
|
||||
) : IRoomSummary by roomSummary
|
|
@ -26,7 +26,6 @@ import com.squareup.moshi.JsonClass
|
|||
* "state_key": "!space:example.com",
|
||||
* "content": {
|
||||
* "via": ["example.com"],
|
||||
* "present": true,
|
||||
* "canonical": true,
|
||||
* }
|
||||
* }
|
||||
|
@ -38,11 +37,6 @@ data class SpaceParentContent(
|
|||
* Parents where via is not present are ignored.
|
||||
*/
|
||||
@Json(name = "via") val via: List<String>? = null,
|
||||
/**
|
||||
* present: true key is included to distinguish from a deleted state event
|
||||
* Parent where present is not present (sic) or is not set to true are ignored.
|
||||
*/
|
||||
@Json(name = "present") val present: Boolean? = false,
|
||||
/**
|
||||
* Canonical determines whether this is the main parent for the space.
|
||||
* When a user joins a room with a canonical parent, clients may switch to view the room
|
||||
|
|
|
@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom
|
||||
import org.matrix.android.sdk.api.session.room.sender.SenderInfo
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.user.model.User
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -152,8 +151,6 @@ fun RoomSummary.toMatrixItem() = MatrixItem.RoomItem(roomId, displayName, avatar
|
|||
|
||||
fun RoomSummary.toRoomAliasMatrixItem() = MatrixItem.RoomAliasItem(canonicalAlias ?: roomId, displayName, avatarUrl)
|
||||
|
||||
fun SpaceSummary.toMatrixItem() = MatrixItem.RoomItem(spaceId, displayName, avatarUrl)
|
||||
|
||||
// If no name is available, use room alias as Riot-Web does
|
||||
fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name ?: getPrimaryAlias() ?: "", avatarUrl)
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.matrix.android.sdk.internal.database
|
||||
|
||||
import io.realm.DynamicRealm
|
||||
import io.realm.FieldAttribute
|
||||
import io.realm.RealmMigration
|
||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||
import org.matrix.android.sdk.internal.database.model.EditAggregatedSummaryEntityFields
|
||||
|
@ -31,8 +30,8 @@ import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType
|
|||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomTagEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceChildInfoEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceChildSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceParentSummaryEntityFields
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -200,21 +199,34 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
|||
|
||||
fun migrateTo10(realm: DynamicRealm) {
|
||||
Timber.d("Step 9 -> 10")
|
||||
realm.schema.create("SpaceChildSummaryEntity")
|
||||
?.addField(SpaceChildSummaryEntityFields.ORDER, String::class.java)
|
||||
?.addField(SpaceChildSummaryEntityFields.CHILD_ROOM_ID, String::class.java)
|
||||
?.addField(SpaceChildSummaryEntityFields.AUTO_JOIN, Boolean::class.java)
|
||||
?.setNullable(SpaceChildSummaryEntityFields.AUTO_JOIN, true)
|
||||
?.addRealmObjectField(SpaceChildSummaryEntityFields.CHILD_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!)
|
||||
|
||||
// realm.schema.create("SpaceSummaryEntity")
|
||||
// ?.addField(SpaceSummaryEntityFields.SPACE_ID, String::class.java, FieldAttribute.PRIMARY_KEY)
|
||||
// ?.setRequired(SpaceSummaryEntityFields.SPACE_ID, true)
|
||||
// ?.addRealmObjectField(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!)
|
||||
// ?.addRealmListField(SpaceSummaryEntityFields.CHILDREN.`$`, spaceChildInfoSchema!!)
|
||||
|
||||
realm.schema.create("SpaceParentSummaryEntity")
|
||||
?.addField(SpaceParentSummaryEntityFields.PARENT_ROOM_ID, String::class.java)
|
||||
?.addField(SpaceParentSummaryEntityFields.CANONICAL, Boolean::class.java)
|
||||
?.setNullable(SpaceParentSummaryEntityFields.CANONICAL, true)
|
||||
// ?.addRealmListField(RoomParentRelationInfoEntityFields.VIA_SERVERS.`$`, String::class.java)
|
||||
// ?.addRealmObjectField(RoomParentRelationInfoEntityFields.SPACE_SUMMARY_ENTITY.`$`, realm.schema.get("SpaceSummaryEntity")!!)
|
||||
?.addRealmObjectField(SpaceParentSummaryEntityFields.PARENT_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!)
|
||||
|
||||
realm.schema.get("RoomSummaryEntity")
|
||||
?.addField(RoomSummaryEntityFields.ROOM_TYPE, String::class.java)
|
||||
?.transform { obj ->
|
||||
// Should I put messaging type here?
|
||||
obj.setString(RoomSummaryEntityFields.ROOM_TYPE, null)
|
||||
}
|
||||
|
||||
val spaceChildInfoSchema = realm.schema.create("SpaceChildInfoEntity")
|
||||
?.addField(SpaceChildInfoEntityFields.ORDER, String::class.java)
|
||||
?.addRealmListField(SpaceChildInfoEntityFields.VIA_SERVERS.`$`, String::class.java)
|
||||
?.addRealmObjectField(SpaceChildInfoEntityFields.ROOM_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!)
|
||||
|
||||
realm.schema.create("SpaceSummaryEntity")
|
||||
?.addField(SpaceSummaryEntityFields.SPACE_ID, String::class.java, FieldAttribute.PRIMARY_KEY)
|
||||
?.addRealmObjectField(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!)
|
||||
?.addRealmListField(SpaceSummaryEntityFields.CHILDREN.`$`, spaceChildInfoSchema!!)
|
||||
?.addRealmListField(RoomSummaryEntityFields.PARENTS.`$`, realm.schema.get("SpaceParentSummaryEntity")!!)
|
||||
?.addRealmListField(RoomSummaryEntityFields.CHILDREN.`$`, realm.schema.get("SpaceChildSummaryEntity")!!)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package org.matrix.android.sdk.internal.database.mapper
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceParentInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker
|
||||
|
@ -64,7 +66,29 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
|||
roomEncryptionTrustLevel = roomSummaryEntity.roomEncryptionTrustLevel,
|
||||
inviterId = roomSummaryEntity.inviterId,
|
||||
hasFailedSending = roomSummaryEntity.hasFailedSending,
|
||||
roomType = roomSummaryEntity.roomType
|
||||
roomType = roomSummaryEntity.roomType,
|
||||
spaceParents = roomSummaryEntity.parents.map { relationInfoEntity ->
|
||||
SpaceParentInfo(
|
||||
parentId = relationInfoEntity.parentRoomId,
|
||||
roomSummary = relationInfoEntity.parentSummaryEntity?.let { map(it) },
|
||||
canonical = relationInfoEntity.canonical ?: false,
|
||||
viaServers = relationInfoEntity.viaServers.toList()
|
||||
)
|
||||
},
|
||||
children = roomSummaryEntity.children.map {
|
||||
SpaceChildInfo(
|
||||
childRoomId = it.childRoomId ?: "",
|
||||
isKnown = it.childSummaryEntity != null,
|
||||
roomType = it.childSummaryEntity?.roomType,
|
||||
name = it.childSummaryEntity?.name,
|
||||
topic = it.childSummaryEntity?.topic,
|
||||
avatarUrl = it.childSummaryEntity?.avatarUrl,
|
||||
activeMemberCount = it.childSummaryEntity?.joinedMembersCount,
|
||||
order = it.order,
|
||||
autoJoin = it.autoJoin ?: false,
|
||||
viaServers = it.viaServers.toList()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.database.mapper
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class SpaceSummaryMapper @Inject constructor(private val roomSummaryMapper: RoomSummaryMapper) {
|
||||
|
||||
fun map(spaceSummaryEntity: SpaceSummaryEntity): SpaceSummary {
|
||||
return SpaceSummary(
|
||||
spaceId = spaceSummaryEntity.spaceId,
|
||||
roomSummary = roomSummaryMapper.map(spaceSummaryEntity.roomSummaryEntity!!),
|
||||
children = spaceSummaryEntity.children.map {
|
||||
SpaceChildInfo(
|
||||
roomSummary = it.roomSummaryEntity?.let { rs -> roomSummaryMapper.map(rs) },
|
||||
autoJoin = it.autoJoin ?: false,
|
||||
viaServers = it.viaServers.map { it },
|
||||
order = it.order
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -43,6 +43,5 @@ internal open class RoomEntity(@PrimaryKey var roomId: String = "",
|
|||
set(value) {
|
||||
membersLoadStatusStr = value.name
|
||||
}
|
||||
|
||||
companion object
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
|||
|
||||
internal open class RoomSummaryEntity(
|
||||
@PrimaryKey var roomId: String = ""
|
||||
var roomType: String? = null,
|
||||
var parents: RealmList<SpaceParentSummaryEntity> = RealmList(),
|
||||
var children: RealmList<SpaceChildSummaryEntity> = RealmList()
|
||||
) : RealmObject() {
|
||||
|
||||
var displayName: String? = ""
|
||||
|
@ -244,6 +247,5 @@ internal open class RoomSummaryEntity(
|
|||
roomEncryptionTrustLevelStr = value?.name
|
||||
}
|
||||
}
|
||||
|
||||
companion object
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ import io.realm.annotations.RealmModule
|
|||
UserAccountDataEntity::class,
|
||||
ScalarTokenEntity::class,
|
||||
WellknownIntegrationManagerConfigEntity::class,
|
||||
SpaceSummaryEntity::class,
|
||||
SpaceChildInfoEntity::class
|
||||
SpaceChildSummaryEntity::class,
|
||||
SpaceParentSummaryEntity::class
|
||||
])
|
||||
internal class SessionRealmModule
|
||||
|
|
|
@ -22,14 +22,22 @@ import io.realm.RealmObject
|
|||
/**
|
||||
* Decorates room summary with space related information.
|
||||
*/
|
||||
internal open class SpaceChildInfoEntity(
|
||||
var viaServers: RealmList<String> = RealmList(),
|
||||
// Use for alphabetic ordering of this child
|
||||
internal open class SpaceChildSummaryEntity(
|
||||
// var isSpace: Boolean = false,
|
||||
|
||||
var order: String? = null,
|
||||
// If true, this child should be join when parent is joined
|
||||
|
||||
var autoJoin: Boolean? = null,
|
||||
// link to the actual room (check type to see if it's a subspace)
|
||||
var roomSummaryEntity: RoomSummaryEntity? = null
|
||||
|
||||
var childRoomId: String? = null,
|
||||
// Link to the actual space summary if it is known locally
|
||||
var childSummaryEntity: RoomSummaryEntity? = null,
|
||||
|
||||
var viaServers: RealmList<String> = RealmList()
|
||||
// var owner: RoomSummaryEntity? = null,
|
||||
|
||||
// var level: Int = 0
|
||||
|
||||
) : RealmObject() {
|
||||
|
||||
companion object
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.database.model
|
||||
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
|
||||
/**
|
||||
* Decorates room summary with space related information.
|
||||
*/
|
||||
internal open class SpaceParentSummaryEntity(
|
||||
/**
|
||||
* Determines whether this is the main parent for the space
|
||||
* When a user joins a room with a canonical parent, clients may switch to view the room in the context of that space,
|
||||
* peeking into it in order to find other rooms and group them together.
|
||||
* In practice, well behaved rooms should only have one canonical parent, but given this is not enforced:
|
||||
* if multiple are present the client should select the one with the lowest room ID,
|
||||
* as determined via a lexicographic utf-8 ordering.
|
||||
*/
|
||||
var canonical: Boolean? = null,
|
||||
|
||||
var parentRoomId: String? = null,
|
||||
// Link to the actual space summary if it is known locally
|
||||
var parentSummaryEntity: RoomSummaryEntity? = null,
|
||||
|
||||
var viaServers: RealmList<String> = RealmList()
|
||||
|
||||
// var child: RoomSummaryEntity? = null,
|
||||
|
||||
// var level: Int = 0
|
||||
|
||||
) : RealmObject() {
|
||||
|
||||
companion object
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.database.model
|
||||
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
internal open class SpaceSummaryEntity(@PrimaryKey var spaceId: String = "",
|
||||
var roomSummaryEntity: RoomSummaryEntity? = null,
|
||||
var children: RealmList<SpaceChildInfoEntity> = RealmList()
|
||||
// TODO public / private .. and more
|
||||
) : RealmObject() {
|
||||
|
||||
// Do we want to denormalize that ?
|
||||
|
||||
// private var membershipStr: String = Membership.NONE.name
|
||||
// var membership: Membership
|
||||
// get() {
|
||||
// return Membership.valueOf(membershipStr)
|
||||
// }
|
||||
// set(value) {
|
||||
// membershipStr = value.name
|
||||
// }
|
||||
|
||||
companion object
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.database.query
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.kotlin.createObject
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntityFields
|
||||
|
||||
internal fun SpaceSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<SpaceSummaryEntity> {
|
||||
val query = realm.where<SpaceSummaryEntity>()
|
||||
query.isNotNull(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.`$`)
|
||||
if (roomId != null) {
|
||||
query.equalTo(SpaceSummaryEntityFields.SPACE_ID, roomId)
|
||||
}
|
||||
query.sort(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.DISPLAY_NAME)
|
||||
return query
|
||||
}
|
||||
|
||||
internal fun SpaceSummaryEntity.Companion.findByAlias(realm: Realm, roomAlias: String): SpaceSummaryEntity? {
|
||||
val spaceSummary = realm.where<SpaceSummaryEntity>()
|
||||
.isNotNull(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.`$`)
|
||||
.equalTo(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.CANONICAL_ALIAS, roomAlias)
|
||||
.findFirst()
|
||||
if (spaceSummary != null) {
|
||||
return spaceSummary
|
||||
}
|
||||
return realm.where<SpaceSummaryEntity>()
|
||||
.isNotNull(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.`$`)
|
||||
.contains(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.FLAT_ALIASES, "|$roomAlias")
|
||||
.findFirst()
|
||||
}
|
||||
|
||||
internal fun SpaceSummaryEntity.Companion.getOrCreate(realm: Realm, roomId: String): SpaceSummaryEntity {
|
||||
return where(realm, roomId).findFirst() ?: realm.createObject<SpaceSummaryEntity>(roomId).also {
|
||||
it.roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import org.matrix.android.sdk.api.session.room.alias.AliasService
|
|||
import org.matrix.android.sdk.api.session.room.call.RoomCallService
|
||||
import org.matrix.android.sdk.api.session.room.members.MembershipService
|
||||
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.relation.RelationService
|
||||
import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService
|
||||
import org.matrix.android.sdk.api.session.room.read.ReadService
|
||||
|
@ -36,11 +37,16 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineService
|
|||
import org.matrix.android.sdk.api.session.room.typing.TypingService
|
||||
import org.matrix.android.sdk.api.session.room.uploads.UploadsService
|
||||
import org.matrix.android.sdk.api.session.search.SearchResult
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.util.Cancelable
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
import org.matrix.android.sdk.internal.session.room.state.SendStateTask
|
||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||
import org.matrix.android.sdk.internal.session.search.SearchTask
|
||||
import org.matrix.android.sdk.internal.session.space.DefaultSpace
|
||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||
import org.matrix.android.sdk.internal.task.configureWith
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
import java.security.InvalidParameterException
|
||||
import javax.inject.Inject
|
||||
|
@ -148,4 +154,9 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun asSpace(): Space? {
|
||||
if (roomSummary()?.roomType != RoomType.SPACE) return null
|
||||
return DefaultSpace(this, roomSummaryDataSource)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.session.room.RoomService
|
|||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.UpdatableFilterLivePageResult
|
||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
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.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
|
@ -33,6 +34,7 @@ import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
|||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper
|
||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
|
@ -63,7 +65,9 @@ internal class DefaultRoomService @Inject constructor(
|
|||
private val peekRoomTask: PeekRoomTask,
|
||||
private val roomGetter: RoomGetter,
|
||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource
|
||||
private val roomSummaryMapper: RoomSummaryMapper,
|
||||
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||
private val taskExecutor: TaskExecutor
|
||||
) : RoomService {
|
||||
|
||||
override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback<String>): Cancelable {
|
||||
|
@ -168,4 +172,18 @@ internal class DefaultRoomService @Inject constructor(
|
|||
override suspend fun peekRoom(roomIdOrAlias: String): PeekResult {
|
||||
return peekRoomTask.execute(PeekRoomTask.Params(roomIdOrAlias))
|
||||
}
|
||||
|
||||
override fun getFlattenRoomSummaryChildOf(spaceId: String?, memberships: List<Membership>): List<RoomSummary> {
|
||||
if (spaceId == null) {
|
||||
return roomSummaryDataSource.getFlattenOrphanRooms()
|
||||
}
|
||||
return roomSummaryDataSource.getAllRoomSummaryChildOf(spaceId, memberships)
|
||||
}
|
||||
|
||||
override fun getFlattenRoomSummaryChildOfLive(spaceId: String?, memberships: List<Membership>): LiveData<List<RoomSummary>> {
|
||||
if (spaceId == null) {
|
||||
return roomSummaryDataSource.getFlattenOrphanRoomsLive()
|
||||
}
|
||||
return roomSummaryDataSource.getAllRoomSummaryChildOfLive(spaceId, memberships)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ package org.matrix.android.sdk.internal.session.room
|
|||
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||
import org.matrix.android.sdk.internal.session.space.DefaultSpace
|
||||
import org.matrix.android.sdk.internal.session.space.SpaceSummaryDataSource
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface SpaceGetter {
|
||||
|
@ -28,7 +28,7 @@ internal interface SpaceGetter {
|
|||
|
||||
internal class DefaultSpaceGetter @Inject constructor(
|
||||
private val roomGetter: RoomGetter,
|
||||
private val spaceSummaryDataSource: SpaceSummaryDataSource
|
||||
private val spaceSummaryDataSource: RoomSummaryDataSource
|
||||
) : SpaceGetter {
|
||||
|
||||
override fun get(spaceId: String): Space? {
|
||||
|
|
|
@ -20,6 +20,7 @@ import io.realm.Realm
|
|||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
|
||||
import org.matrix.android.sdk.api.session.space.model.SpaceParentContent
|
||||
import org.matrix.android.sdk.internal.database.mapper.ContentMapper
|
||||
import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
|
||||
import org.matrix.android.sdk.internal.database.query.whereType
|
||||
|
@ -35,8 +36,8 @@ import timber.log.Timber
|
|||
*
|
||||
* - Separately, rooms can claim parents via the m.room.parent state event:
|
||||
*/
|
||||
internal class RoomRelationshipHelper(private val realm: Realm,
|
||||
private val roomId: String
|
||||
internal class RoomChildRelationInfo(private val realm: Realm,
|
||||
private val roomId: String
|
||||
) {
|
||||
|
||||
data class SpaceChildInfo(
|
||||
|
@ -46,15 +47,24 @@ internal class RoomRelationshipHelper(private val realm: Realm,
|
|||
val viaServers: List<String>
|
||||
)
|
||||
|
||||
data class SpaceParentInfo(
|
||||
val roomId: String,
|
||||
val canonical: Boolean,
|
||||
val viaServers: List<String>,
|
||||
val stateEventSender: String
|
||||
)
|
||||
|
||||
/**
|
||||
* Gets the ordered list of valid child description.
|
||||
*/
|
||||
fun getDirectChildrenDescriptions(): List<SpaceChildInfo> {
|
||||
return CurrentStateEventEntity.whereType(realm, roomId, EventType.STATE_SPACE_CHILD)
|
||||
.findAll()
|
||||
.findAll().also {
|
||||
Timber.v("## Space: Found ${it.count()} m.space.child state events for $roomId")
|
||||
}
|
||||
.mapNotNull {
|
||||
ContentMapper.map(it.root?.content).toModel<SpaceChildContent>()?.let { scc ->
|
||||
Timber.d("## Space child desc state event $scc")
|
||||
Timber.v("## Space child desc state event $scc")
|
||||
// Children where via is not present are ignored.
|
||||
scc.via?.let { via ->
|
||||
SpaceChildInfo(
|
||||
|
@ -68,4 +78,25 @@ internal class RoomRelationshipHelper(private val realm: Realm,
|
|||
}
|
||||
.sortedBy { it.order }
|
||||
}
|
||||
|
||||
fun getParentDescriptions(): List<SpaceParentInfo> {
|
||||
return CurrentStateEventEntity.whereType(realm, roomId, EventType.STATE_SPACE_PARENT)
|
||||
.findAll().also {
|
||||
Timber.v("## Space: Found ${it.count()} m.space.parent state events for $roomId")
|
||||
}
|
||||
.mapNotNull {
|
||||
ContentMapper.map(it.root?.content).toModel<SpaceParentContent>()?.let { scc ->
|
||||
Timber.v("## Space parent desc state event $scc")
|
||||
// Parent where via is not present are ignored.
|
||||
scc.via?.let { via ->
|
||||
SpaceParentInfo(
|
||||
roomId = it.stateKey,
|
||||
canonical = scc.canonical ?: false,
|
||||
viaServers = via,
|
||||
stateEventSender = it.root?.sender ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.session.room.summary
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
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.util.Optional
|
||||
|
||||
internal class HierarchyLiveDataHelper(
|
||||
val spaceId: String,
|
||||
val memberships: List<Membership>,
|
||||
val roomSummaryDataSource: RoomSummaryDataSource) {
|
||||
|
||||
private val sources = HashMap<String, LiveData<Optional<RoomSummary>>>()
|
||||
private val mediatorLiveData = MediatorLiveData<List<String>>()
|
||||
|
||||
fun liveData() = mediatorLiveData
|
||||
|
||||
init {
|
||||
onChange()
|
||||
}
|
||||
|
||||
private fun parentsToCheck(): List<RoomSummary> {
|
||||
val spaces = ArrayList<RoomSummary>()
|
||||
roomSummaryDataSource.getSpaceSummary(spaceId)?.let {
|
||||
roomSummaryDataSource.flattenSubSpace(it, emptyList(), spaces, memberships)
|
||||
}
|
||||
return spaces
|
||||
}
|
||||
|
||||
private fun onChange() {
|
||||
val existingSources = sources.keys.toList()
|
||||
val newSources = parentsToCheck().map { it.roomId }
|
||||
val addedSources = newSources.filter { !existingSources.contains(it) }
|
||||
val removedSource = existingSources.filter { !newSources.contains(it) }
|
||||
addedSources.forEach {
|
||||
val liveData = roomSummaryDataSource.getSpaceSummaryLive(it)
|
||||
mediatorLiveData.addSource(liveData) { onChange() }
|
||||
sources[it] = liveData
|
||||
}
|
||||
|
||||
removedSource.forEach {
|
||||
sources[it]?.let { mediatorLiveData.removeSource(it) }
|
||||
}
|
||||
|
||||
sources[spaceId]?.value?.getOrNull()?.let { spaceSummary ->
|
||||
val results = ArrayList<RoomSummary>()
|
||||
roomSummaryDataSource.flattenChild(spaceSummary, emptyList(), results, memberships)
|
||||
mediatorLiveData.postValue(results.map { it.roomId })
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
* 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.
|
||||
|
@ -27,9 +28,17 @@ import io.realm.Sort
|
|||
import org.matrix.android.sdk.api.query.RoomCategoryFilter
|
||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.UpdatableFilterLivePageResult
|
||||
import io.realm.kotlin.where
|
||||
import org.matrix.android.sdk.api.session.room.RoomCategoryFilter
|
||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
import org.matrix.android.sdk.internal.database.mapper.RoomSummaryMapper
|
||||
|
@ -84,6 +93,36 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
|
|||
)
|
||||
}
|
||||
|
||||
fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<RoomSummary>> {
|
||||
return getRoomSummariesLive(queryParams)
|
||||
}
|
||||
|
||||
fun getSpaceSummary(roomIdOrAlias: String): RoomSummary? {
|
||||
return getRoomSummary(roomIdOrAlias).let {
|
||||
it?.takeIf { it.roomType == RoomType.SPACE }
|
||||
}
|
||||
}
|
||||
|
||||
fun getSpaceSummaryLive(roomId: String): LiveData<Optional<RoomSummary>> {
|
||||
val liveData = monarchy.findAllMappedWithChanges(
|
||||
{ realm ->
|
||||
RoomSummaryEntity.where(realm, roomId)
|
||||
.isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
|
||||
.equalTo(RoomSummaryEntityFields.ROOM_TYPE, RoomType.SPACE)
|
||||
},
|
||||
{
|
||||
roomSummaryMapper.map(it)
|
||||
}
|
||||
)
|
||||
return Transformations.map(liveData) { results ->
|
||||
results.firstOrNull().toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List<RoomSummary> {
|
||||
return getRoomSummaries(spaceSummaryQueryParams)
|
||||
}
|
||||
|
||||
fun getBreadcrumbs(queryParams: RoomSummaryQueryParams): List<RoomSummary> {
|
||||
return monarchy.fetchAllMappedSync(
|
||||
{ breadcrumbsQuery(it, queryParams) },
|
||||
|
@ -190,9 +229,134 @@ internal class RoomSummaryDataSource @Inject constructor(@SessionDatabase privat
|
|||
}
|
||||
}
|
||||
|
||||
queryParams.excludeType.forEach {
|
||||
queryParams.excludeType?.forEach {
|
||||
query.notEqualTo(RoomSummaryEntityFields.ROOM_TYPE, it)
|
||||
}
|
||||
queryParams.includeType?.forEach {
|
||||
query.equalTo(RoomSummaryEntityFields.ROOM_TYPE, it)
|
||||
}
|
||||
queryParams.roomCategoryFilter?.let {
|
||||
when (it) {
|
||||
RoomCategoryFilter.ONLY_DM -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, true)
|
||||
RoomCategoryFilter.ONLY_ROOMS -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, false)
|
||||
RoomCategoryFilter.ALL -> {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
fun getAllRoomSummaryChildOf(spaceAliasOrId: String, memberShips: List<Membership>): List<RoomSummary> {
|
||||
val space = getSpaceSummary(spaceAliasOrId) ?: return emptyList()
|
||||
val result = ArrayList<RoomSummary>()
|
||||
flattenChild(space, emptyList(), result, memberShips)
|
||||
return result
|
||||
}
|
||||
|
||||
fun getAllRoomSummaryChildOfLive(spaceId: String, memberShips: List<Membership>): LiveData<List<RoomSummary>> {
|
||||
// we want to listen to all spaces in hierarchy and on change compute back all childs
|
||||
// and switch map to listen thoose?
|
||||
val mediatorLiveData = HierarchyLiveDataHelper(spaceId, memberShips, this).liveData()
|
||||
|
||||
return Transformations.switchMap(mediatorLiveData) { allIds ->
|
||||
monarchy.findAllMappedWithChanges(
|
||||
{
|
||||
it.where<RoomSummaryEntity>()
|
||||
.`in`(RoomSummaryEntityFields.ROOM_ID, allIds.toTypedArray())
|
||||
.`in`(RoomSummaryEntityFields.MEMBERSHIP_STR, memberShips.map { it.name }.toTypedArray())
|
||||
.equalTo(RoomSummaryEntityFields.IS_DIRECT, false)
|
||||
},
|
||||
{
|
||||
roomSummaryMapper.map(it)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun getFlattenOrphanRooms(): List<RoomSummary> {
|
||||
return getRoomSummaries(roomSummaryQueryParams {
|
||||
memberships = Membership.activeMemberships()
|
||||
excludeType = listOf(RoomType.SPACE)
|
||||
roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
|
||||
}).filter {
|
||||
// we need to check if orphan
|
||||
isOrphan(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun getFlattenOrphanRoomsLive(): LiveData<List<RoomSummary>> {
|
||||
return Transformations.map(
|
||||
getRoomSummariesLive(roomSummaryQueryParams {
|
||||
memberships = Membership.activeMemberships()
|
||||
excludeType = listOf(RoomType.SPACE)
|
||||
roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
|
||||
})) {
|
||||
it.filter {
|
||||
isOrphan(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isOrphan(roomSummary: RoomSummary): Boolean {
|
||||
if (roomSummary.roomType == RoomType.SPACE && roomSummary.membership.isActive()) {
|
||||
return false
|
||||
}
|
||||
// all parents line should be orphan
|
||||
roomSummary.spaceParents?.forEach { info ->
|
||||
if (info.roomSummary != null && !info.roomSummary.membership.isLeft()) {
|
||||
if (!isOrphan(info.roomSummary)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// it may not have a parent relation but could be a child of some other....
|
||||
for (spaceSummary in getSpaceSummaries(spaceSummaryQueryParams { memberships = Membership.activeMemberships() })) {
|
||||
if (spaceSummary.children?.any { it.childRoomId == roomSummary.roomId } == true) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun flattenChild(current: RoomSummary, parenting: List<String>, output: MutableList<RoomSummary>, memberShips: List<Membership>) {
|
||||
current.children?.sortedBy { it.order ?: it.name }?.forEach { childInfo ->
|
||||
if (childInfo.roomType == RoomType.SPACE) {
|
||||
// Add recursive
|
||||
if (!parenting.contains(childInfo.childRoomId)) { // avoid cycles!
|
||||
getSpaceSummary(childInfo.childRoomId)?.let { subSpace ->
|
||||
if (memberShips.isEmpty() || memberShips.contains(subSpace.membership)) {
|
||||
flattenChild(subSpace, parenting + listOf(current.roomId), output, memberShips)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (childInfo.isKnown) {
|
||||
getRoomSummary(childInfo.childRoomId)?.let {
|
||||
if (memberShips.isEmpty() || memberShips.contains(it.membership)) {
|
||||
if (!it.isDirect) {
|
||||
output.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun flattenSubSpace(current: RoomSummary, parenting: List<String>, output: MutableList<RoomSummary>, memberShips: List<Membership>) {
|
||||
output.add(current)
|
||||
current.children?.sortedBy { it.order ?: it.name }?.forEach {
|
||||
if (it.roomType == RoomType.SPACE) {
|
||||
// Add recursive
|
||||
if (!parenting.contains(it.childRoomId)) { // avoid cycles!
|
||||
getSpaceSummary(it.childRoomId)?.let { subSpace ->
|
||||
if (memberShips.isEmpty() || memberShips.contains(subSpace.membership)) {
|
||||
output.add(subSpace)
|
||||
flattenSubSpace(subSpace, parenting + listOf(current.roomId), output, memberShips)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,19 +39,22 @@ import org.matrix.android.sdk.internal.database.model.EventEntity
|
|||
import org.matrix.android.sdk.internal.database.model.EventEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceChildInfoEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceChildSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceParentSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
||||
import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendStates
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.database.query.getOrNull
|
||||
import org.matrix.android.sdk.internal.database.query.isEventRead
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.database.query.whereType
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.extensions.clearWith
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver
|
||||
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.relationship.RoomRelationshipHelper
|
||||
import org.matrix.android.sdk.internal.session.room.relationship.RoomChildRelationInfo
|
||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncSummary
|
||||
import org.matrix.android.sdk.internal.session.sync.model.RoomSyncUnreadNotifications
|
||||
import timber.log.Timber
|
||||
|
@ -62,7 +65,8 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
private val roomDisplayNameResolver: RoomDisplayNameResolver,
|
||||
private val roomAvatarResolver: RoomAvatarResolver,
|
||||
private val eventDecryptor: EventDecryptor,
|
||||
private val crossSigningService: DefaultCrossSigningService) {
|
||||
private val crossSigningService: DefaultCrossSigningService,
|
||||
private val stateEventDataSource: StateEventDataSource) {
|
||||
|
||||
fun update(realm: Realm,
|
||||
roomId: String,
|
||||
|
@ -163,28 +167,6 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
crossSigningService.onUsersDeviceUpdate(otherRoomMembers)
|
||||
}
|
||||
}
|
||||
|
||||
if (roomType == RoomType.SPACE) {
|
||||
Timber.v("## Space: Updating summary for Space $roomId membership: ${roomSummaryEntity.membership}")
|
||||
val spaceSummaryEntity = SpaceSummaryEntity()
|
||||
spaceSummaryEntity.spaceId = roomId
|
||||
spaceSummaryEntity.roomSummaryEntity = roomSummaryEntity
|
||||
spaceSummaryEntity.children.clear()
|
||||
spaceSummaryEntity.children.addAll(
|
||||
RoomRelationshipHelper(realm, roomId).getDirectChildrenDescriptions()
|
||||
.map {
|
||||
Timber.v("## Space: Updating summary for room $roomId with info $it")
|
||||
realm.createObject<SpaceChildInfoEntity>().apply {
|
||||
this.roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, it.roomId)
|
||||
this.order = it.order
|
||||
this.autoJoin = it.autoJoin
|
||||
}.also {
|
||||
Timber.v("## Space: Updating summary for room $roomId with children $it")
|
||||
}
|
||||
}
|
||||
)
|
||||
realm.insertOrUpdate(spaceSummaryEntity)
|
||||
}
|
||||
}
|
||||
|
||||
private fun RoomSummaryEntity.updateHasFailedSending() {
|
||||
|
@ -196,4 +178,55 @@ internal class RoomSummaryUpdater @Inject constructor(
|
|||
roomSummaryEntity.updateHasFailedSending()
|
||||
roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called at the end of the room sync, to check and validate all parent/child relations
|
||||
*/
|
||||
fun validateSpaceRelationship(realm: Realm) {
|
||||
// Do level 0 stuffs
|
||||
|
||||
realm.where(RoomSummaryEntity::class.java).findAll().forEach { roomSummary ->
|
||||
if (roomSummary.roomType == RoomType.SPACE) {
|
||||
roomSummary.children.clearWith { it.deleteFromRealm() }
|
||||
roomSummary.children.addAll(
|
||||
RoomChildRelationInfo(realm, roomSummary.roomId).getDirectChildrenDescriptions()
|
||||
.map {
|
||||
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with info $it")
|
||||
realm.createObject<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")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// check parents
|
||||
roomSummary.parents.clearWith { it.deleteFromRealm() }
|
||||
roomSummary.parents.addAll(
|
||||
RoomChildRelationInfo(realm, roomSummary.roomId).getParentDescriptions()
|
||||
.map { parentInfo ->
|
||||
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with parent info $parentInfo")
|
||||
realm.createObject<SpaceParentSummaryEntity>().apply {
|
||||
this.parentRoomId = parentInfo.roomId
|
||||
this.parentSummaryEntity = RoomSummaryEntity.where(realm, parentInfo.roomId).findFirst()
|
||||
this.canonical = parentInfo.canonical
|
||||
this.viaServers.addAll(parentInfo.viaServers)
|
||||
// this.level = 0
|
||||
}.also {
|
||||
Timber.v("## Space: Updating summary for room ${roomSummary.roomId} with parent $it")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// private fun isValidCanonical() : Boolean {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ import kotlinx.coroutines.TimeoutCancellationException
|
|||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
|
@ -44,8 +44,8 @@ internal class DefaultCreateSpaceTask @Inject constructor(
|
|||
|
||||
try {
|
||||
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
|
||||
realm.where(SpaceSummaryEntity::class.java)
|
||||
.equalTo(SpaceSummaryEntityFields.SPACE_ID, spaceId)
|
||||
realm.where(RoomSummaryEntity::class.java)
|
||||
.equalTo(RoomSummaryEntityFields.ROOM_ID, spaceId)
|
||||
}
|
||||
} catch (exception: TimeoutCancellationException) {
|
||||
throw CreateRoomFailure.CreatedWithTimeout(spaceId)
|
||||
|
|
|
@ -21,17 +21,18 @@ import org.matrix.android.sdk.api.session.events.model.EventType
|
|||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
|
||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||
|
||||
internal class DefaultSpace(private val room: Room, private val spaceSummaryDataSource: SpaceSummaryDataSource) : Space {
|
||||
internal class DefaultSpace(private val room: Room, private val spaceSummaryDataSource: RoomSummaryDataSource) : Space {
|
||||
|
||||
override fun asRoom(): Room {
|
||||
return room
|
||||
}
|
||||
|
||||
override fun spaceSummary(): SpaceSummary? {
|
||||
override fun spaceSummary(): RoomSummary? {
|
||||
return spaceSummaryDataSource.getSpaceSummary(asRoom().roomId)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,26 +19,37 @@ package org.matrix.android.sdk.internal.session.space
|
|||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||
import org.matrix.android.sdk.api.session.space.CreateSpaceParams
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.session.space.SpaceService
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
|
||||
import org.matrix.android.sdk.api.session.space.model.SpaceParentContent
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
||||
import org.matrix.android.sdk.internal.session.room.SpaceGetter
|
||||
import org.matrix.android.sdk.internal.session.room.membership.leaving.LeaveRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
|
||||
import java.lang.IllegalArgumentException
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultSpaceService @Inject constructor(
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
@UserId private val userId: String,
|
||||
private val createSpaceTask: CreateSpaceTask,
|
||||
// private val joinRoomTask: JoinRoomTask,
|
||||
private val joinSpaceTask: JoinSpaceTask,
|
||||
|
@ -47,8 +58,9 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
// private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
|
||||
// private val roomIdByAliasTask: GetRoomIdByAliasTask,
|
||||
// private val deleteRoomAliasTask: DeleteRoomAliasTask,
|
||||
// private val roomGetter: RoomGetter,
|
||||
private val spaceSummaryDataSource: SpaceSummaryDataSource,
|
||||
private val roomGetter: RoomGetter,
|
||||
private val roomSummaryDataSource: RoomSummaryDataSource,
|
||||
private val stateEventDataSource: StateEventDataSource,
|
||||
private val peekSpaceTask: PeekSpaceTask,
|
||||
private val resolveSpaceInfoTask: ResolveSpaceInfoTask,
|
||||
private val leaveRoomTask: LeaveRoomTask
|
||||
|
@ -73,12 +85,12 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
return spaceGetter.get(spaceId)
|
||||
}
|
||||
|
||||
override fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<SpaceSummary>> {
|
||||
return spaceSummaryDataSource.getRoomSummariesLive(queryParams)
|
||||
override fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<RoomSummary>> {
|
||||
return roomSummaryDataSource.getSpaceSummariesLive(queryParams)
|
||||
}
|
||||
|
||||
override fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List<SpaceSummary> {
|
||||
return spaceSummaryDataSource.getSpaceSummaries(spaceSummaryQueryParams)
|
||||
override fun getSpaceSummaries(spaceSummaryQueryParams: SpaceSummaryQueryParams): List<RoomSummary> {
|
||||
return roomSummaryDataSource.getSpaceSummaries(spaceSummaryQueryParams)
|
||||
}
|
||||
|
||||
override suspend fun peekSpace(spaceId: String): SpacePeekResult {
|
||||
|
@ -108,21 +120,16 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
?.firstOrNull { it.stateKey == childSummary.roomId && it.type == EventType.STATE_SPACE_CHILD }
|
||||
?.content.toModel<SpaceChildContent>()
|
||||
SpaceChildInfo(
|
||||
roomSummary = RoomSummary(
|
||||
roomId = childSummary.roomId,
|
||||
roomType = childSummary.roomType,
|
||||
name = childSummary.name ?: "",
|
||||
displayName = childSummary.name ?: "",
|
||||
topic = childSummary.topic ?: "",
|
||||
joinedMembersCount = childSummary.numJoinedMembers,
|
||||
avatarUrl = childSummary.avatarUrl ?: "",
|
||||
encryptionEventTs = null,
|
||||
typingUsers = emptyList(),
|
||||
isEncrypted = false
|
||||
),
|
||||
childRoomId = childSummary.roomId,
|
||||
isKnown = true,
|
||||
roomType = childSummary.roomType,
|
||||
name = childSummary.name,
|
||||
topic = childSummary.topic,
|
||||
avatarUrl = childSummary.avatarUrl,
|
||||
order = childStateEv?.order,
|
||||
autoJoin = childStateEv?.autoJoin ?: false,
|
||||
viaServers = childStateEv?.via ?: emptyList()
|
||||
viaServers = childStateEv?.via ?: emptyList(),
|
||||
activeMemberCount = childSummary.numJoinedMembers
|
||||
)
|
||||
} ?: emptyList()
|
||||
)
|
||||
|
@ -138,4 +145,42 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
override suspend fun rejectInvite(spaceId: String, reason: String?) {
|
||||
leaveRoomTask.execute(LeaveRoomTask.Params(spaceId, reason))
|
||||
}
|
||||
|
||||
// override fun getSpaceParentsOfRoom(roomId: String): List<SpaceSummary> {
|
||||
// return spaceSummaryDataSource.getParentsOfRoom(roomId)
|
||||
// }
|
||||
|
||||
override suspend fun setSpaceParent(childRoomId: String, parentSpaceId: String, canonical: Boolean, viaServers: List<String>) {
|
||||
// Should we perform some validation here?,
|
||||
// and if client want to bypass, it could use sendStateEvent directly?
|
||||
if (canonical) {
|
||||
// check that we can send m.child in the parent room
|
||||
if (roomSummaryDataSource.getRoomSummary(parentSpaceId)?.membership != Membership.JOIN) {
|
||||
throw UnsupportedOperationException("Cannot add canonical child if not member of parent")
|
||||
}
|
||||
val powerLevelsEvent = stateEventDataSource.getStateEvent(
|
||||
roomId = parentSpaceId,
|
||||
eventType = EventType.STATE_ROOM_POWER_LEVELS,
|
||||
stateKey = QueryStringValue.NoCondition
|
||||
)
|
||||
val powerLevelsContent = powerLevelsEvent?.content?.toModel<PowerLevelsContent>()
|
||||
?: throw UnsupportedOperationException("Cannot add canonical child, not enough power level")
|
||||
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
|
||||
if (!powerLevelsHelper.isUserAllowedToSend(userId, true, EventType.STATE_SPACE_CHILD)) {
|
||||
throw UnsupportedOperationException("Cannot add canonical child, not enough power level")
|
||||
}
|
||||
}
|
||||
|
||||
val room = roomGetter.getRoom(childRoomId)
|
||||
?: throw IllegalArgumentException("Unknown Room $childRoomId")
|
||||
|
||||
room.sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_PARENT,
|
||||
stateKey = parentSpaceId,
|
||||
body = SpaceParentContent(
|
||||
via = viaServers,
|
||||
canonical = canonical
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,12 @@ import org.matrix.android.sdk.api.session.room.model.Membership
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.space.SpaceService
|
||||
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.session.room.membership.joining.JoinRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -45,7 +46,7 @@ internal class DefaultJoinSpaceTask @Inject constructor(
|
|||
private val joinRoomTask: JoinRoomTask,
|
||||
@SessionDatabase
|
||||
private val realmConfiguration: RealmConfiguration,
|
||||
private val spaceSummaryDataSource: SpaceSummaryDataSource
|
||||
private val roomSummaryDataSource: RoomSummaryDataSource
|
||||
) : JoinSpaceTask {
|
||||
|
||||
override suspend fun execute(params: JoinSpaceTask.Params): SpaceService.JoinSpaceResult {
|
||||
|
@ -65,15 +66,15 @@ internal class DefaultJoinSpaceTask @Inject constructor(
|
|||
Timber.v("## Space: > Wait for post joined sync ${params.roomIdOrAlias} ...")
|
||||
try {
|
||||
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(2L)) { realm ->
|
||||
realm.where(SpaceSummaryEntity::class.java)
|
||||
realm.where(RoomSummaryEntity::class.java)
|
||||
.apply {
|
||||
if (params.roomIdOrAlias.startsWith("!")) {
|
||||
equalTo(SpaceSummaryEntityFields.SPACE_ID, params.roomIdOrAlias)
|
||||
equalTo(RoomSummaryEntityFields.ROOM_ID, params.roomIdOrAlias)
|
||||
} else {
|
||||
equalTo(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.CANONICAL_ALIAS, params.roomIdOrAlias)
|
||||
equalTo(RoomSummaryEntityFields.CANONICAL_ALIAS, params.roomIdOrAlias)
|
||||
}
|
||||
}
|
||||
.equalTo(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.MEMBERSHIP_STR, Membership.JOIN.name)
|
||||
.equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)
|
||||
}
|
||||
} catch (exception: TimeoutCancellationException) {
|
||||
Timber.w("## Space: > Error created with timeout")
|
||||
|
@ -83,21 +84,21 @@ internal class DefaultJoinSpaceTask @Inject constructor(
|
|||
val errors = HashMap<String, Throwable>()
|
||||
Timber.v("## Space: > Sync done ...")
|
||||
// after that i should have the children (? do I need to paginate to get state)
|
||||
val summary = spaceSummaryDataSource.getSpaceSummary(params.roomIdOrAlias)
|
||||
Timber.v("## Space: Found space summary Name:[${summary?.roomSummary?.name}] children: ${summary?.children?.size}")
|
||||
val summary = roomSummaryDataSource.getSpaceSummary(params.roomIdOrAlias)
|
||||
Timber.v("## Space: Found space summary Name:[${summary?.name}] children: ${summary?.children?.size}")
|
||||
summary?.children?.forEach {
|
||||
val childRoomSummary = it.roomSummary ?: return@forEach
|
||||
Timber.v("## Space: Processing child :[${childRoomSummary.roomId}] autoJoin:${it.autoJoin}")
|
||||
// val childRoomSummary = it.roomSummary ?: return@forEach
|
||||
Timber.v("## Space: Processing child :[${it.childRoomId}] autoJoin:${it.autoJoin}")
|
||||
if (it.autoJoin) {
|
||||
// I should try to join as well
|
||||
if (childRoomSummary.roomType == RoomType.SPACE) {
|
||||
if (it.roomType == RoomType.SPACE) {
|
||||
// recursively join auto-joined child of this space?
|
||||
when (val subspaceJoinResult = this.execute(JoinSpaceTask.Params(it.roomSummary.roomId, null, it.viaServers))) {
|
||||
when (val subspaceJoinResult = this.execute(JoinSpaceTask.Params(it.childRoomId, null, it.viaServers))) {
|
||||
SpaceService.JoinSpaceResult.Success -> {
|
||||
// nop
|
||||
}
|
||||
is SpaceService.JoinSpaceResult.Fail -> {
|
||||
errors[it.roomSummary.roomId] = subspaceJoinResult.error
|
||||
errors[it.childRoomId] = subspaceJoinResult.error
|
||||
}
|
||||
is SpaceService.JoinSpaceResult.PartialSuccess -> {
|
||||
errors.putAll(subspaceJoinResult.failedRooms)
|
||||
|
@ -105,15 +106,15 @@ internal class DefaultJoinSpaceTask @Inject constructor(
|
|||
}
|
||||
} else {
|
||||
try {
|
||||
Timber.v("## Space: Joining room child ${childRoomSummary.roomId}")
|
||||
Timber.v("## Space: Joining room child ${it.childRoomId}")
|
||||
joinRoomTask.execute(JoinRoomTask.Params(
|
||||
roomIdOrAlias = childRoomSummary.roomId,
|
||||
roomIdOrAlias = it.childRoomId,
|
||||
reason = "Auto-join parent space",
|
||||
viaServers = it.viaServers
|
||||
))
|
||||
} catch (failure: Throwable) {
|
||||
errors[it.roomSummary.roomId] = failure
|
||||
Timber.e("## Space: Failed to join room child ${childRoomSummary.roomId}")
|
||||
errors[it.childRoomId] = failure
|
||||
Timber.e("## Space: Failed to join room child ${it.childRoomId}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.space
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.VersioningState
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.api.util.toOptional
|
||||
import org.matrix.android.sdk.internal.database.mapper.SpaceSummaryMapper
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.database.query.findByAlias
|
||||
import org.matrix.android.sdk.internal.database.query.where
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.query.process
|
||||
import org.matrix.android.sdk.internal.util.fetchCopyMap
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class SpaceSummaryDataSource @Inject constructor(
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val spaceSummaryMapper: SpaceSummaryMapper
|
||||
) {
|
||||
|
||||
fun getSpaceSummary(roomIdOrAlias: String): SpaceSummary? {
|
||||
return monarchy
|
||||
.fetchCopyMap({
|
||||
if (roomIdOrAlias.startsWith("!")) {
|
||||
// It's a roomId
|
||||
SpaceSummaryEntity.where(it, roomId = roomIdOrAlias).findFirst()
|
||||
} else {
|
||||
// Assume it's a room alias
|
||||
SpaceSummaryEntity.findByAlias(it, roomIdOrAlias)
|
||||
}
|
||||
}, { entity, _ ->
|
||||
spaceSummaryMapper.map(entity)
|
||||
})
|
||||
}
|
||||
|
||||
fun getSpaceSummaryLive(roomId: String): LiveData<Optional<SpaceSummary>> {
|
||||
val liveData = monarchy.findAllMappedWithChanges(
|
||||
{ realm -> SpaceSummaryEntity.where(realm, roomId).isNotEmpty(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.DISPLAY_NAME) },
|
||||
{ spaceSummaryMapper.map(it) }
|
||||
)
|
||||
return Transformations.map(liveData) { results ->
|
||||
results.firstOrNull().toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun getSpaceSummaries(queryParams: SpaceSummaryQueryParams): List<SpaceSummary> {
|
||||
return monarchy.fetchAllMappedSync(
|
||||
{ spaceSummariesQuery(it, queryParams) },
|
||||
{ spaceSummaryMapper.map(it) }
|
||||
)
|
||||
}
|
||||
|
||||
fun getRoomSummariesLive(queryParams: RoomSummaryQueryParams): LiveData<List<SpaceSummary>> {
|
||||
return monarchy.findAllMappedWithChanges(
|
||||
{ spaceSummariesQuery(it, queryParams) },
|
||||
{ spaceSummaryMapper.map(it) }
|
||||
)
|
||||
}
|
||||
|
||||
private fun spaceSummariesQuery(realm: Realm, queryParams: SpaceSummaryQueryParams): RealmQuery<SpaceSummaryEntity> {
|
||||
val query = SpaceSummaryEntity.where(realm)
|
||||
query.process(SpaceSummaryEntityFields.SPACE_ID, queryParams.roomId)
|
||||
query.process(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.DISPLAY_NAME, queryParams.displayName)
|
||||
query.process(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.CANONICAL_ALIAS, queryParams.canonicalAlias)
|
||||
query.process(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.MEMBERSHIP_STR, queryParams.memberships)
|
||||
query.notEqualTo(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.VERSIONING_STATE_STR, VersioningState.UPGRADED_ROOM_JOINED.name)
|
||||
return query
|
||||
}
|
||||
}
|
|
@ -95,6 +95,9 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
|||
handleRoomSync(realm, HandlingStrategy.JOINED(roomsSyncResponse.join), isInitialSync, aggregator, reporter)
|
||||
handleRoomSync(realm, HandlingStrategy.INVITED(roomsSyncResponse.invite), isInitialSync, aggregator, reporter)
|
||||
handleRoomSync(realm, HandlingStrategy.LEFT(roomsSyncResponse.leave), isInitialSync, aggregator, reporter)
|
||||
|
||||
// post room sync validation
|
||||
roomSummaryUpdater.validateSpaceRelationship(realm)
|
||||
}
|
||||
|
||||
// PRIVATE METHODS *****************************************************************************
|
||||
|
|
|
@ -18,9 +18,9 @@ package im.vector.app.features.grouplist
|
|||
|
||||
import arrow.core.Option
|
||||
import im.vector.app.core.utils.BehaviorDataSource
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class SelectedSpaceDataSource @Inject constructor() : BehaviorDataSource<Option<SpaceSummary>>(Option.empty())
|
||||
class SelectedSpaceDataSource @Inject constructor() : BehaviorDataSource<Option<RoomSummary>>(Option.empty())
|
||||
|
|
|
@ -55,7 +55,7 @@ import im.vector.app.features.workers.signout.BannerState
|
|||
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
|
||||
import im.vector.app.features.workers.signout.ServerBackupStatusViewState
|
||||
import org.matrix.android.sdk.api.session.group.model.GroupSummary
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
|
||||
import timber.log.Timber
|
||||
|
@ -252,10 +252,10 @@ class HomeDetailFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun onSpaceChange(spaceSummary: SpaceSummary?) {
|
||||
private fun onSpaceChange(spaceSummary: RoomSummary?) {
|
||||
spaceSummary?.let {
|
||||
// Use GlideApp with activity context to avoid the glideRequests to be paused
|
||||
if (spaceSummary.spaceId == ALL_COMMUNITIES_GROUP_ID) {
|
||||
if (spaceSummary.roomId == ALL_COMMUNITIES_GROUP_ID) {
|
||||
// Special case
|
||||
views.groupToolbarAvatarImageView.background = ContextCompat.getDrawable(requireContext(), R.drawable.space_home_background)
|
||||
views.groupToolbarAvatarImageView.scaleType = ImageView.ScaleType.CENTER_INSIDE
|
||||
|
|
|
@ -22,12 +22,11 @@ import com.airbnb.mvrx.MvRxState
|
|||
import com.airbnb.mvrx.Uninitialized
|
||||
import org.matrix.android.sdk.api.session.group.model.GroupSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.sync.SyncState
|
||||
|
||||
data class HomeDetailViewState(
|
||||
val groupSummary: Option<GroupSummary> = Option.empty(),
|
||||
val spaceSummary: Option<SpaceSummary> = Option.empty(),
|
||||
val spaceSummary: Option<RoomSummary> = Option.empty(),
|
||||
val asyncRooms: Async<List<RoomSummary>> = Uninitialized,
|
||||
val displayMode: RoomListDisplayMode = RoomListDisplayMode.PEOPLE,
|
||||
val notificationCountCatchup: Int = 0,
|
||||
|
|
|
@ -60,19 +60,21 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
|
|||
EventType.STATE_ROOM_HISTORY_VISIBILITY,
|
||||
EventType.STATE_ROOM_SERVER_ACL,
|
||||
EventType.STATE_ROOM_GUEST_ACCESS,
|
||||
EventType.REDACTION,
|
||||
EventType.STATE_ROOM_ALIASES,
|
||||
EventType.KEY_VERIFICATION_ACCEPT,
|
||||
EventType.KEY_VERIFICATION_START,
|
||||
EventType.KEY_VERIFICATION_ACCEPT,
|
||||
EventType.KEY_VERIFICATION_KEY,
|
||||
EventType.KEY_VERIFICATION_READY,
|
||||
EventType.KEY_VERIFICATION_MAC,
|
||||
EventType.CALL_CANDIDATES,
|
||||
EventType.KEY_VERIFICATION_MAC,
|
||||
EventType.CALL_REPLACES,
|
||||
EventType.CALL_SELECT_ANSWER,
|
||||
EventType.CALL_NEGOTIATE,
|
||||
EventType.REACTION,
|
||||
EventType.STATE_ROOM_POWER_LEVELS -> noticeItemFactory.create(params)
|
||||
EventType.STATE_ROOM_POWER_LEVELS,
|
||||
EventType.STATE_SPACE_CHILD,
|
||||
EventType.STATE_SPACE_PARENT,
|
||||
EventType.REDACTION -> noticeItemFactory.create(event, highlight, callback)
|
||||
EventType.STATE_ROOM_WIDGET_LEGACY,
|
||||
EventType.STATE_ROOM_WIDGET -> widgetItemFactory.create(params)
|
||||
EventType.STATE_ROOM_ENCRYPTION -> encryptionItemFactory.create(params)
|
||||
|
|
|
@ -107,6 +107,8 @@ class NoticeEventFormatter @Inject constructor(
|
|||
EventType.KEY_VERIFICATION_DONE,
|
||||
EventType.KEY_VERIFICATION_KEY,
|
||||
EventType.KEY_VERIFICATION_READY,
|
||||
EventType.STATE_SPACE_CHILD,
|
||||
EventType.STATE_SPACE_PARENT,
|
||||
EventType.REDACTION -> formatDebug(timelineEvent.root)
|
||||
else -> {
|
||||
Timber.v("Type $type not handled by this formatter")
|
||||
|
|
|
@ -212,6 +212,6 @@ class MatrixToRoomSpaceFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun secondaryButtonClicked() = withState(sharedViewModel) { state ->
|
||||
private fun secondaryButtonClicked() = withState(sharedViewModel) { _ ->
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ class DefaultNavigator @Inject constructor(
|
|||
}
|
||||
|
||||
sessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(spaceId)?.spaceSummary()?.let {
|
||||
Timber.d("## Nav: Switching to space $spaceId / ${it.roomSummary.name}")
|
||||
Timber.d("## Nav: Switching to space $spaceId / ${it.name}")
|
||||
selectedSpaceDataSource.post(Option.just(it))
|
||||
} ?: kotlin.run {
|
||||
Timber.d("## Nav: Failed to switch to space $spaceId")
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.settings
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.lifecycle.LiveData
|
||||
|
||||
abstract class SharedPreferenceLiveData<T>(protected val sharedPrefs: SharedPreferences,
|
||||
protected val key: String,
|
||||
private val defValue: T) : LiveData<T>() {
|
||||
|
||||
private val preferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
||||
if (key == this.key) {
|
||||
value = getValueFromPreferences(key, defValue)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun getValueFromPreferences(key: String, defValue: T): T
|
||||
|
||||
override fun onActive() {
|
||||
super.onActive()
|
||||
value = getValueFromPreferences(key, defValue)
|
||||
sharedPrefs.registerOnSharedPreferenceChangeListener(preferenceChangeListener)
|
||||
}
|
||||
|
||||
override fun onInactive() {
|
||||
sharedPrefs.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener)
|
||||
super.onInactive()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun booleanLiveData(sharedPrefs: SharedPreferences, key: String, defaultValue: Boolean): SharedPreferenceLiveData<Boolean> {
|
||||
return object : SharedPreferenceLiveData<Boolean>(sharedPrefs, key, defaultValue) {
|
||||
override fun getValueFromPreferences(key: String, defValue: Boolean): Boolean {
|
||||
return this.sharedPrefs.getBoolean(key, defValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import android.media.RingtoneManager
|
|||
import android.net.Uri
|
||||
import android.provider.MediaStore
|
||||
import androidx.core.content.edit
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.squareup.seismic.ShakeDetector
|
||||
import im.vector.app.BuildConfig
|
||||
import im.vector.app.R
|
||||
|
@ -312,6 +313,14 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
|||
return defaultPrefs.getBoolean(SETTINGS_LABS_USE_SPACES, false)
|
||||
}
|
||||
|
||||
fun labSpacesLive(): LiveData<Boolean> {
|
||||
return SharedPreferenceLiveData.booleanLiveData(
|
||||
defaultPrefs,
|
||||
SETTINGS_LABS_USE_SPACES,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
fun failFast(): Boolean {
|
||||
return BuildConfig.DEBUG || (developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY, false))
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
package im.vector.app.features.settings
|
||||
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.preference.VectorSwitchPreference
|
||||
import im.vector.app.features.MainActivity
|
||||
import im.vector.app.features.MainActivityArgs
|
||||
import javax.inject.Inject
|
||||
|
||||
class VectorSettingsLabsFragment @Inject constructor(
|
||||
|
@ -27,6 +30,11 @@ class VectorSettingsLabsFragment @Inject constructor(
|
|||
override val preferenceXmlRes = R.xml.vector_settings_labs
|
||||
|
||||
override fun bindPref() {
|
||||
// Nothing to do
|
||||
findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_USE_SPACES)!!.let { pref ->
|
||||
pref.setOnPreferenceChangeListener { _, _ ->
|
||||
MainActivity.restartApp(requireActivity(), MainActivityArgs(clearCache = false))
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ class ShareSpaceBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetSpa
|
|||
?: return Unit.also { dismiss() }
|
||||
val summary = activeSessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(args.spaceId)?.spaceSummary()
|
||||
|
||||
val spaceName = summary?.roomSummary?.name
|
||||
val spaceName = summary?.name
|
||||
views.descriptionText.text = getString(R.string.invite_people_to_your_space_desc, spaceName)
|
||||
|
||||
views.inviteByMailButton.debouncedClicks {
|
||||
|
|
|
@ -32,7 +32,7 @@ import im.vector.app.core.platform.VectorBaseFragment
|
|||
import im.vector.app.databinding.FragmentGroupListBinding
|
||||
import im.vector.app.features.home.HomeActivitySharedAction
|
||||
import im.vector.app.features.home.HomeSharedActionViewModel
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import javax.inject.Inject
|
||||
|
||||
class SpaceListFragment @Inject constructor(
|
||||
|
@ -76,11 +76,11 @@ class SpaceListFragment @Inject constructor(
|
|||
spaceController.update(state)
|
||||
}
|
||||
|
||||
override fun onSpaceSelected(spaceSummary: SpaceSummary) {
|
||||
override fun onSpaceSelected(spaceSummary: RoomSummary) {
|
||||
viewModel.handle(SpaceListAction.SelectSpace(spaceSummary))
|
||||
}
|
||||
|
||||
override fun onLeaveSpace(spaceSummary: SpaceSummary) {
|
||||
override fun onLeaveSpace(spaceSummary: RoomSummary) {
|
||||
viewModel.handle(SpaceListAction.LeaveSpace(spaceSummary))
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import im.vector.app.core.utils.DebouncedClickListener
|
|||
import im.vector.app.features.grouplist.homeSpaceSummaryItem
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -51,13 +51,13 @@ class SpaceSummaryController @Inject constructor(
|
|||
buildGroupModels(nonNullViewState.asyncSpaces(), nonNullViewState.selectedSpace)
|
||||
}
|
||||
|
||||
private fun buildGroupModels(summaries: List<SpaceSummary>?, selected: SpaceSummary?) {
|
||||
private fun buildGroupModels(summaries: List<RoomSummary>?, selected: RoomSummary?) {
|
||||
if (summaries.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
// show invites on top
|
||||
|
||||
summaries.filter { it.roomSummary.membership == Membership.INVITE }
|
||||
summaries.filter { it.membership == Membership.INVITE }
|
||||
.let { invites ->
|
||||
if (invites.isNotEmpty()) {
|
||||
genericItemHeader {
|
||||
|
@ -67,7 +67,7 @@ class SpaceSummaryController @Inject constructor(
|
|||
invites.forEach {
|
||||
spaceSummaryItem {
|
||||
avatarRenderer(avatarRenderer)
|
||||
id(it.spaceId)
|
||||
id(it.roomId)
|
||||
matrixItem(it.toMatrixItem())
|
||||
selected(false)
|
||||
listener { callback?.onSpaceSelected(it) }
|
||||
|
@ -87,19 +87,19 @@ class SpaceSummaryController @Inject constructor(
|
|||
}
|
||||
|
||||
summaries
|
||||
.filter { it.roomSummary.membership == Membership.JOIN }
|
||||
.filter { it.membership == Membership.JOIN }
|
||||
.forEach { groupSummary ->
|
||||
val isSelected = groupSummary.spaceId == selected?.spaceId
|
||||
if (groupSummary.spaceId == ALL_COMMUNITIES_GROUP_ID) {
|
||||
val isSelected = groupSummary.roomId == selected?.roomId
|
||||
if (groupSummary.roomId == ALL_COMMUNITIES_GROUP_ID) {
|
||||
homeSpaceSummaryItem {
|
||||
id(groupSummary.spaceId)
|
||||
id(groupSummary.roomId)
|
||||
selected(isSelected)
|
||||
listener { callback?.onSpaceSelected(groupSummary) }
|
||||
}
|
||||
} else {
|
||||
spaceSummaryItem {
|
||||
avatarRenderer(avatarRenderer)
|
||||
id(groupSummary.spaceId)
|
||||
id(groupSummary.roomId)
|
||||
matrixItem(groupSummary.toMatrixItem())
|
||||
selected(isSelected)
|
||||
onLeave { callback?.onLeaveSpace(groupSummary) }
|
||||
|
@ -119,8 +119,8 @@ class SpaceSummaryController @Inject constructor(
|
|||
}
|
||||
|
||||
interface Callback {
|
||||
fun onSpaceSelected(spaceSummary: SpaceSummary)
|
||||
fun onLeaveSpace(spaceSummary: SpaceSummary)
|
||||
fun onSpaceSelected(spaceSummary: RoomSummary)
|
||||
fun onLeaveSpace(spaceSummary: RoomSummary)
|
||||
fun onAddSpaceSelected()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package im.vector.app.features.spaces
|
|||
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
|
@ -37,6 +39,8 @@ abstract class SpaceSummaryItem : VectorEpoxyModel<SpaceSummaryItem.Holder>() {
|
|||
@EpoxyAttribute var selected: Boolean = false
|
||||
@EpoxyAttribute var listener: (() -> Unit)? = null
|
||||
@EpoxyAttribute var onLeave: (() -> Unit)? = null
|
||||
@EpoxyAttribute var toggleExpand: (() -> Unit)? = null
|
||||
@EpoxyAttribute var expanded: Boolean? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
|
@ -52,6 +56,26 @@ abstract class SpaceSummaryItem : VectorEpoxyModel<SpaceSummaryItem.Holder>() {
|
|||
} else {
|
||||
holder.leaveView.isVisible = false
|
||||
}
|
||||
|
||||
when (expanded) {
|
||||
null -> {
|
||||
holder.collapseIndicator.isGone = true
|
||||
}
|
||||
else -> {
|
||||
holder.collapseIndicator.isVisible = true
|
||||
holder.collapseIndicator.setImageDrawable(
|
||||
ContextCompat.getDrawable(holder.view.context,
|
||||
if (expanded!!) R.drawable.ic_expand_less else R.drawable.ic_expand_more
|
||||
)
|
||||
)
|
||||
holder.collapseIndicator.setOnClickListener(
|
||||
DebouncedClickListener({ _ ->
|
||||
toggleExpand?.invoke()
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
avatarRenderer.renderSpace(matrixItem, holder.avatarImageView)
|
||||
}
|
||||
|
||||
|
@ -65,5 +89,6 @@ abstract class SpaceSummaryItem : VectorEpoxyModel<SpaceSummaryItem.Holder>() {
|
|||
val groupNameView by bind<TextView>(R.id.groupNameView)
|
||||
val rootView by bind<CheckableConstraintLayout>(R.id.itemGroupLayout)
|
||||
val leaveView by bind<ImageView>(R.id.groupTmpLeave)
|
||||
val collapseIndicator by bind<ImageView>(R.id.groupChildrenCollapse)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,15 +42,14 @@ import org.matrix.android.sdk.api.session.Session
|
|||
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.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
import org.matrix.android.sdk.rx.rx
|
||||
|
||||
const val ALL_COMMUNITIES_GROUP_ID = "+ALL_COMMUNITIES_GROUP_ID"
|
||||
|
||||
sealed class SpaceListAction : VectorViewModelAction {
|
||||
data class SelectSpace(val spaceSummary: SpaceSummary) : SpaceListAction()
|
||||
data class LeaveSpace(val spaceSummary: SpaceSummary) : SpaceListAction()
|
||||
data class SelectSpace(val spaceSummary: RoomSummary) : SpaceListAction()
|
||||
data class LeaveSpace(val spaceSummary: RoomSummary) : SpaceListAction()
|
||||
object AddSpace : SpaceListAction()
|
||||
}
|
||||
|
||||
|
@ -64,8 +63,8 @@ sealed class SpaceListViewEvents : VectorViewEvents {
|
|||
}
|
||||
|
||||
data class SpaceListViewState(
|
||||
val asyncSpaces: Async<List<SpaceSummary>> = Uninitialized,
|
||||
val selectedSpace: SpaceSummary? = null
|
||||
val asyncSpaces: Async<List<RoomSummary>> = Uninitialized,
|
||||
val selectedSpace: RoomSummary? = null
|
||||
) : MvRxState
|
||||
|
||||
class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState,
|
||||
|
@ -96,7 +95,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
|
|||
selectedSpaceDataSource
|
||||
.observe()
|
||||
.subscribe {
|
||||
if (currentGroupId != it.orNull()?.spaceId) {
|
||||
if (currentGroupId != it.orNull()?.roomId) {
|
||||
setState {
|
||||
copy(
|
||||
selectedSpace = it.orNull()
|
||||
|
@ -111,8 +110,8 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
|
|||
selectSubscribe(SpaceListViewState::selectedSpace) { spaceSummary ->
|
||||
if (spaceSummary != null) {
|
||||
// We only want to open group if the updated selectedGroup is a different one.
|
||||
if (currentGroupId != spaceSummary.spaceId) {
|
||||
currentGroupId = spaceSummary.spaceId
|
||||
if (currentGroupId != spaceSummary.roomId) {
|
||||
currentGroupId = spaceSummary.roomId
|
||||
_viewEvents.post(SpaceListViewEvents.OpenSpace)
|
||||
}
|
||||
val optionGroup = Option.just(spaceSummary)
|
||||
|
@ -120,7 +119,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
|
|||
} else {
|
||||
// If selected group is null we force to default. It can happens when leaving the selected group.
|
||||
setState {
|
||||
copy(selectedSpace = this.asyncSpaces()?.find { it.spaceId == ALL_COMMUNITIES_GROUP_ID })
|
||||
copy(selectedSpace = this.asyncSpaces()?.find { it.roomId == ALL_COMMUNITIES_GROUP_ID })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,17 +137,17 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
|
|||
|
||||
private fun handleSelectSpace(action: SpaceListAction.SelectSpace) = withState { state ->
|
||||
// get uptodate version of the space
|
||||
val summary = session.spaceService().getSpaceSummaries(roomSummaryQueryParams { roomId = QueryStringValue.Equals(action.spaceSummary.spaceId) })
|
||||
val summary = session.spaceService().getSpaceSummaries(roomSummaryQueryParams { roomId = QueryStringValue.Equals(action.spaceSummary.roomId) })
|
||||
.firstOrNull()
|
||||
if (summary?.roomSummary?.membership == Membership.INVITE) {
|
||||
_viewEvents.post(SpaceListViewEvents.OpenSpaceSummary(summary.roomSummary.roomId))
|
||||
if (summary?.membership == Membership.INVITE) {
|
||||
_viewEvents.post(SpaceListViewEvents.OpenSpaceSummary(summary.roomId))
|
||||
// viewModelScope.launch(Dispatchers.IO) {
|
||||
// tryOrNull { session.spaceService().peekSpace(action.spaceSummary.spaceId) }.let {
|
||||
// Timber.d("PEEK RESULT/ $it")
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
if (state.selectedSpace?.spaceId != action.spaceSummary.spaceId) {
|
||||
if (state.selectedSpace?.roomId != action.spaceSummary.roomId) {
|
||||
// state.selectedSpace?.let {
|
||||
// selectedSpaceDataSource.post(Option.just(state.selectedSpace))
|
||||
// }
|
||||
|
@ -160,8 +159,8 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
|
|||
private fun handleLeaveSpace(action: SpaceListAction.LeaveSpace) {
|
||||
viewModelScope.launch {
|
||||
awaitCallback {
|
||||
tryOrNull("Failed to leave space ${action.spaceSummary.spaceId}") {
|
||||
session.spaceService().getSpace(action.spaceSummary.spaceId)?.asRoom()?.leave(null, it)
|
||||
tryOrNull("Failed to leave space ${action.spaceSummary.roomId}") {
|
||||
session.spaceService().getSpace(action.spaceSummary.roomId)?.asRoom()?.leave(null, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,23 +177,19 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
|
|||
excludeType = listOf(/**RoomType.MESSAGING,$*/
|
||||
null)
|
||||
}
|
||||
Observable.combineLatest<SpaceSummary, List<SpaceSummary>, List<SpaceSummary>>(
|
||||
Observable.combineLatest<RoomSummary, List<RoomSummary>, List<RoomSummary>>(
|
||||
session
|
||||
.rx()
|
||||
.liveUser(session.myUserId)
|
||||
.map { optionalUser ->
|
||||
SpaceSummary(
|
||||
spaceId = ALL_COMMUNITIES_GROUP_ID,
|
||||
roomSummary = RoomSummary(
|
||||
roomId = ALL_COMMUNITIES_GROUP_ID,
|
||||
membership = Membership.JOIN,
|
||||
displayName = stringProvider.getString(R.string.group_all_communities),
|
||||
avatarUrl = optionalUser.getOrNull()?.avatarUrl ?: "",
|
||||
encryptionEventTs = 0,
|
||||
isEncrypted = false,
|
||||
typingUsers = emptyList()
|
||||
),
|
||||
children = emptyList()
|
||||
RoomSummary(
|
||||
roomId = ALL_COMMUNITIES_GROUP_ID,
|
||||
membership = Membership.JOIN,
|
||||
displayName = stringProvider.getString(R.string.group_all_communities),
|
||||
avatarUrl = optionalUser.getOrNull()?.avatarUrl ?: "",
|
||||
encryptionEventTs = 0,
|
||||
isEncrypted = false,
|
||||
typingUsers = emptyList()
|
||||
)
|
||||
},
|
||||
session
|
||||
|
@ -205,9 +200,9 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
|
|||
}
|
||||
)
|
||||
.execute { async ->
|
||||
val currentSelectedGroupId = selectedSpace?.spaceId
|
||||
val currentSelectedGroupId = selectedSpace?.roomId
|
||||
val newSelectedGroup = if (currentSelectedGroupId != null) {
|
||||
async()?.find { it.spaceId == currentSelectedGroupId }
|
||||
async()?.find { it.roomId == currentSelectedGroupId }
|
||||
} else {
|
||||
async()?.firstOrNull()
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.rx.rx
|
||||
import org.matrix.android.sdk.rx.unwrap
|
||||
|
@ -45,7 +45,7 @@ data class SpaceDirectoryState(
|
|||
// The current filter
|
||||
val spaceId: String,
|
||||
val currentFilter: String = "",
|
||||
val summary: Async<SpaceSummary> = Uninitialized,
|
||||
val summary: Async<RoomSummary> = Uninitialized,
|
||||
// True if more result are available server side
|
||||
val hasMore: Boolean = false,
|
||||
// Set of joined roomId / spaces,
|
||||
|
|
|
@ -156,12 +156,12 @@ class SpacePreviewViewModel @AssistedInject constructor(
|
|||
childInfoList = Success(
|
||||
resolveResult.second.map {
|
||||
ChildInfo(
|
||||
roomId = it.roomSummary?.roomId ?: "",
|
||||
avatarUrl = it.roomSummary?.avatarUrl,
|
||||
name = it.roomSummary?.name,
|
||||
topic = it.roomSummary?.topic,
|
||||
memberCount = it.roomSummary?.joinedMembersCount,
|
||||
isSubSpace = it.roomSummary?.roomType == RoomType.SPACE,
|
||||
roomId = it.childRoomId,
|
||||
avatarUrl = it.avatarUrl,
|
||||
name = it.name,
|
||||
topic = it.topic,
|
||||
memberCount = it.activeMemberCount,
|
||||
isSubSpace = it.roomType == RoomType.SPACE,
|
||||
children = Uninitialized,
|
||||
viaServers = null
|
||||
)
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
android:id="@+id/itemGroupLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="65dp"
|
||||
android:background="@drawable/bg_group_item"
|
||||
android:background="@drawable/bg_space_item"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="?attr/selectableItemBackground">
|
||||
|
|
|
@ -35,11 +35,28 @@
|
|||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/groupBottomSeparator"
|
||||
app:layout_constraintEnd_toStartOf="@+id/groupTmpLeave"
|
||||
app:layout_constraintEnd_toStartOf="@+id/groupChildrenCollapse"
|
||||
app:layout_constraintStart_toEndOf="@+id/groupAvatarImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/groupChildrenCollapse"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:importantForAccessibility="no"
|
||||
tools:src="@drawable/ic_expand_more_white"
|
||||
android:src="@drawable/ic_expand_less_white"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintBottom_toTopOf="@+id/groupBottomSeparator"
|
||||
app:layout_constraintEnd_toStartOf="@+id/groupTmpLeave"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tint="?riotx_text_primary"
|
||||
tools:ignore="MissingPrefix" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/groupTmpLeave"
|
||||
android:clickable="true"
|
||||
|
@ -61,6 +78,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="21dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
app:layout_constraintBottom_toTopOf="@+id/groupBottomSeparator"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
|
@ -2186,7 +2186,8 @@
|
|||
<string name="labs_swipe_to_reply_in_timeline">Enable swipe to reply in timeline</string>
|
||||
<string name="labs_show_unread_notifications_as_tab">Add a dedicated tab for unread notifications on main screen.</string>
|
||||
|
||||
<string name="labs_experimental_spaces">Enable Spaces (formerly known as ‘groups as rooms’) to allow users to organise rooms into more useful groups.</string>
|
||||
<string name="labs_experimental_spaces">Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.</string>
|
||||
<string name="labs_experimental_spaces_desc">Warning: This will trigger a clear cache and initial sync</string>
|
||||
|
||||
<string name="link_copied_to_clipboard">Link copied to clipboard</string>
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
<im.vector.app.core.preference.VectorSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="SETTINGS_LABS_USE_SPACES"
|
||||
android:title="@string/labs_experimental_spaces" />
|
||||
android:title="@string/labs_experimental_spaces"
|
||||
android:summary="@string/labs_experimental_spaces_desc"/>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in New Issue