Basic space join / use tmp msc id / db model update

This commit is contained in:
Valere 2021-01-12 11:40:09 +01:00
parent ab4f2429c4
commit e2578a29ed
28 changed files with 549 additions and 53 deletions

View file

@ -0,0 +1,25 @@
/*
* 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.room.model
data class SpaceChildInfo(
val roomSummary: IRoomSummary?,
val present: Boolean,
val order: String?,
val autoJoin: Boolean,
val viaServers: List<String>
)

View file

@ -21,7 +21,6 @@ import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
// TODO Give a way to include other initial states // TODO Give a way to include other initial states
@ -112,7 +111,7 @@ open class CreateRoomParams {
} }
} }
var roomType: String? = RoomType.MESSAGING var roomType: String? = null // RoomType.MESSAGING
set(value) { set(value) {
field = value field = value
if (value != null) { if (value != null) {

View file

@ -18,9 +18,10 @@ 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.IRoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
data class SpaceSummary( data class SpaceSummary(
val spaceId: String, val spaceId: String,
val roomSummary: RoomSummary, val roomSummary: RoomSummary,
val children: List<IRoomSummary> val children: List<SpaceChildInfo>
) : IRoomSummary by roomSummary ) : IRoomSummary by roomSummary

View file

@ -49,5 +49,5 @@ data class SpaceChildContent(
* The default flag on a child listing allows a space admin to list the "default" sub-spaces and rooms in that space. * The default flag on a child listing allows a space admin to list the "default" sub-spaces and rooms in that space.
* This means that when a user joins the parent space, they will automatically be joined to those default children. * This means that when a user joins the parent space, they will automatically be joined to those default children.
*/ */
@Json(name = "default") val default: Boolean? = true @Json(name = "default") val default: Boolean? = false
) )

View file

@ -26,6 +26,7 @@ import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeout
import timber.log.Timber
internal suspend fun <T> awaitNotEmptyResult(realmConfiguration: RealmConfiguration, internal suspend fun <T> awaitNotEmptyResult(realmConfiguration: RealmConfiguration,
timeoutMillis: Long, timeoutMillis: Long,
@ -40,6 +41,7 @@ internal suspend fun <T> awaitNotEmptyResult(realmConfiguration: RealmConfigurat
val listener = object : RealmChangeListener<RealmResults<T>> { val listener = object : RealmChangeListener<RealmResults<T>> {
override fun onChange(it: RealmResults<T>) { override fun onChange(it: RealmResults<T>) {
Timber.v("## Space: $it")
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
result.removeChangeListener(this) result.removeChangeListener(this)
latch.complete(Unit) latch.complete(Unit)

View file

@ -29,9 +29,9 @@ import org.matrix.android.sdk.internal.database.model.PreviewUrlCacheEntityField
import org.matrix.android.sdk.internal.database.model.RoomEntityFields import org.matrix.android.sdk.internal.database.model.RoomEntityFields
import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType 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.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomTagEntityFields 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.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.SpaceSummaryEntityFields
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -207,9 +207,16 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
obj.setString(RoomSummaryEntityFields.ROOM_TYPE, null) obj.setString(RoomSummaryEntityFields.ROOM_TYPE, null)
} }
val spaceChildInfoSchema = realm.schema.create("SpaceChildInfoEntity")
?.addField(SpaceChildInfoEntityFields.ORDER, String::class.java)
?.addField(SpaceChildInfoEntityFields.PRESENT, Boolean::class.java)
?.setNullable(SpaceChildInfoEntityFields.PRESENT, true)
?.addRealmListField(SpaceChildInfoEntityFields.VIA_SERVERS.`$`, String::class.java)
?.addRealmObjectField(SpaceChildInfoEntityFields.ROOM_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!)
realm.schema.create("SpaceSummaryEntity") realm.schema.create("SpaceSummaryEntity")
?.addField(SpaceSummaryEntityFields.SPACE_ID, String::class.java, FieldAttribute.PRIMARY_KEY) ?.addField(SpaceSummaryEntityFields.SPACE_ID, String::class.java, FieldAttribute.PRIMARY_KEY)
?.addRealmObjectField(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!) ?.addRealmObjectField(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.`$`, realm.schema.get("RoomSummaryEntity")!!)
?.addRealmListField(SpaceSummaryEntityFields.CHILDREN.`$`, realm.schema.get("RoomSummaryEntity")!!) ?.addRealmListField(SpaceSummaryEntityFields.CHILDREN.`$`, spaceChildInfoSchema!!)
} }
} }

View file

@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.database.mapper 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.api.session.space.SpaceSummary
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity
import javax.inject.Inject import javax.inject.Inject
@ -27,7 +28,13 @@ internal class SpaceSummaryMapper @Inject constructor(private val roomSummaryMap
spaceId = spaceSummaryEntity.spaceId, spaceId = spaceSummaryEntity.spaceId,
roomSummary = roomSummaryMapper.map(spaceSummaryEntity.roomSummaryEntity!!), roomSummary = roomSummaryMapper.map(spaceSummaryEntity.roomSummaryEntity!!),
children = spaceSummaryEntity.children.map { children = spaceSummaryEntity.children.map {
roomSummaryMapper.map(it) SpaceChildInfo(
roomSummary = it.roomSummaryEntity?.let { rs -> roomSummaryMapper.map(rs) },
autoJoin = it.autoJoin ?: false,
present = it.present ?: false,
viaServers = it.viaServers.map { it },
order = it.order
)
} }
) )
} }

View file

@ -62,6 +62,7 @@ import io.realm.annotations.RealmModule
UserAccountDataEntity::class, UserAccountDataEntity::class,
ScalarTokenEntity::class, ScalarTokenEntity::class,
WellknownIntegrationManagerConfigEntity::class, WellknownIntegrationManagerConfigEntity::class,
SpaceSummaryEntity::class SpaceSummaryEntity::class,
SpaceChildInfoEntity::class
]) ])
internal class SessionRealmModule internal class SessionRealmModule

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database.model
import io.realm.RealmList
import io.realm.RealmObject
/**
* Decorates room summary with space related information.
*/
internal open class SpaceChildInfoEntity(
var viaServers: RealmList<String> = RealmList(),
// it's an active child of the space if and only if present is not null and true
var present: Boolean? = null,
// Use for alphabetic ordering of this child
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
) : RealmObject() {
companion object
}

View file

@ -22,7 +22,7 @@ import io.realm.annotations.PrimaryKey
internal open class SpaceSummaryEntity(@PrimaryKey var spaceId: String = "", internal open class SpaceSummaryEntity(@PrimaryKey var spaceId: String = "",
var roomSummaryEntity: RoomSummaryEntity? = null, var roomSummaryEntity: RoomSummaryEntity? = null,
var children: RealmList<RoomSummaryEntity> = RealmList() var children: RealmList<SpaceChildInfoEntity> = RealmList()
// TODO public / private .. and more // TODO public / private .. and more
) : RealmObject() { ) : RealmObject() {

View file

@ -90,7 +90,9 @@ import org.matrix.android.sdk.internal.session.room.typing.DefaultSendTypingTask
import org.matrix.android.sdk.internal.session.room.typing.SendTypingTask import org.matrix.android.sdk.internal.session.room.typing.SendTypingTask
import org.matrix.android.sdk.internal.session.room.uploads.DefaultGetUploadsTask import org.matrix.android.sdk.internal.session.room.uploads.DefaultGetUploadsTask
import org.matrix.android.sdk.internal.session.room.uploads.GetUploadsTask import org.matrix.android.sdk.internal.session.room.uploads.GetUploadsTask
import org.matrix.android.sdk.internal.session.space.DefaultJoinSpaceTask
import org.matrix.android.sdk.internal.session.space.DefaultSpaceService import org.matrix.android.sdk.internal.session.space.DefaultSpaceService
import org.matrix.android.sdk.internal.session.space.JoinSpaceTask
import org.matrix.android.sdk.internal.session.space.peeking.DefaultPeekSpaceTask import org.matrix.android.sdk.internal.session.space.peeking.DefaultPeekSpaceTask
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
import retrofit2.Retrofit import retrofit2.Retrofit
@ -241,6 +243,9 @@ internal abstract class RoomModule {
@Binds @Binds
abstract fun bindPeekSpaceTask(task: DefaultPeekSpaceTask): PeekSpaceTask abstract fun bindPeekSpaceTask(task: DefaultPeekSpaceTask): PeekSpaceTask
@Binds
abstract fun bindJoinSpaceTask(task: DefaultJoinSpaceTask): JoinSpaceTask
@Binds @Binds
abstract fun bindGetEventTask(task: DefaultGetEventTask): GetEventTask abstract fun bindGetEventTask(task: DefaultGetEventTask): GetEventTask
} }

View file

@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
import org.matrix.android.sdk.internal.database.mapper.ContentMapper 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.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.query.whereType import org.matrix.android.sdk.internal.database.query.whereType
import timber.log.Timber
/** /**
* Relationship between rooms and spaces * Relationship between rooms and spaces
@ -38,13 +39,30 @@ internal class RoomRelationshipHelper(private val realm: Realm,
private val roomId: String private val roomId: String
) { ) {
fun getDirectChildrenDescriptions(): List<String> { data class SpaceChildInfo(
val roomId: String,
val present: Boolean,
val order: String?,
val autoJoin: Boolean,
val viaServers: List<String>
)
fun getDirectChildrenDescriptions(): List<SpaceChildInfo> {
return CurrentStateEventEntity.whereType(realm, roomId, EventType.STATE_SPACE_CHILD) return CurrentStateEventEntity.whereType(realm, roomId, EventType.STATE_SPACE_CHILD)
.findAll() .findAll()
.filter { ContentMapper.map(it.root?.content).toModel<SpaceChildContent>()?.present == true } // .filter { ContentMapper.map(it.root?.content).toModel<SpaceChildContent>()?.present == true }
.mapNotNull { .mapNotNull {
// ContentMapper.map(it.root?.content).toModel<SpaceChildContent>() // ContentMapper.map(it.root?.content).toModel<SpaceChildContent>()
it.stateKey ContentMapper.map(it.root?.content).toModel<SpaceChildContent>()?.let { scc ->
Timber.d("## Space child desc state event $scc")
SpaceChildInfo(
roomId = it.stateKey,
present = scc.present ?: false,
order = scc.order,
autoJoin = scc.default ?: false,
viaServers = scc.via ?: emptyList()
)
}
} }
} }
} }

View file

@ -17,6 +17,7 @@
package org.matrix.android.sdk.internal.session.room.summary package org.matrix.android.sdk.internal.session.room.summary
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.createObject
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
@ -38,6 +39,7 @@ import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
import org.matrix.android.sdk.internal.database.model.SpaceChildInfoEntity
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity 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.findAllInRoomWithSendStates
@ -162,13 +164,26 @@ internal class RoomSummaryUpdater @Inject constructor(
} }
if (roomType == RoomType.SPACE) { if (roomType == RoomType.SPACE) {
val spaceSummaryEntity = SpaceSummaryEntity.getOrCreate(realm, roomId) Timber.v("## Space: Updating summary for Space $roomId membership: ${roomSummaryEntity.membership}")
val spaceSummaryEntity = SpaceSummaryEntity()
spaceSummaryEntity.spaceId = roomId
spaceSummaryEntity.roomSummaryEntity = roomSummaryEntity spaceSummaryEntity.roomSummaryEntity = roomSummaryEntity
spaceSummaryEntity.children.clear() spaceSummaryEntity.children.clear()
spaceSummaryEntity.children.addAll( spaceSummaryEntity.children.addAll(
RoomRelationshipHelper(realm, roomId).getDirectChildrenDescriptions() RoomRelationshipHelper(realm, roomId).getDirectChildrenDescriptions()
.map { RoomSummaryEntity.getOrCreate(realm, it) } .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.present = it.present
this.autoJoin = it.autoJoin
}.also {
Timber.v("## Space: Updating summary for room $roomId with children $it")
}
}
) )
realm.insertOrUpdate(spaceSummaryEntity)
} }
} }

View file

@ -42,6 +42,7 @@ internal class DefaultSpaceService @Inject constructor(
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
private val createRoomTask: CreateRoomTask, private val createRoomTask: CreateRoomTask,
private val joinRoomTask: JoinRoomTask, private val joinRoomTask: JoinRoomTask,
private val joinSpaceTask: JoinSpaceTask,
private val markAllRoomsReadTask: MarkAllRoomsReadTask, private val markAllRoomsReadTask: MarkAllRoomsReadTask,
private val updateBreadcrumbsTask: UpdateBreadcrumbsTask, private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
private val roomIdByAliasTask: GetRoomIdByAliasTask, private val roomIdByAliasTask: GetRoomIdByAliasTask,
@ -77,22 +78,24 @@ internal class DefaultSpaceService @Inject constructor(
override suspend fun joinSpace(spaceIdOrAlias: String, reason: String?, viaServers: List<String>, autoJoinChild: List<SpaceService.ChildAutoJoinInfo>): SpaceService.JoinSpaceResult { override suspend fun joinSpace(spaceIdOrAlias: String, reason: String?, viaServers: List<String>, autoJoinChild: List<SpaceService.ChildAutoJoinInfo>): SpaceService.JoinSpaceResult {
try { try {
joinRoomTask.execute(JoinRoomTask.Params(spaceIdOrAlias, reason, viaServers)) joinSpaceTask.execute(JoinSpaceTask.Params(spaceIdOrAlias, reason, viaServers))
val childJoinFailures = mutableMapOf<String, Throwable>() // TODO partial success
autoJoinChild.forEach { info -> return SpaceService.JoinSpaceResult.Success
// TODO what if the child is it self a subspace with some default children? // val childJoinFailures = mutableMapOf<String, Throwable>()
try { // autoJoinChild.forEach { info ->
joinRoomTask.execute(JoinRoomTask.Params(info.roomIdOrAlias, null, info.viaServers)) // // TODO what if the child is it self a subspace with some default children?
} catch (failure: Throwable) { // try {
// TODO, i could already be a member of this room, handle that as it should not be an error in this context // joinRoomTask.execute(JoinRoomTask.Params(info.roomIdOrAlias, null, info.viaServers))
childJoinFailures[info.roomIdOrAlias] = failure // } catch (failure: Throwable) {
} // // TODO, i could already be a member of this room, handle that as it should not be an error in this context
} // childJoinFailures[info.roomIdOrAlias] = failure
return if (childJoinFailures.isEmpty()) { // }
SpaceService.JoinSpaceResult.Success // }
} else { // return if (childJoinFailures.isEmpty()) {
SpaceService.JoinSpaceResult.PartialSuccess(childJoinFailures) // SpaceService.JoinSpaceResult.Success
} // } else {
// SpaceService.JoinSpaceResult.PartialSuccess(childJoinFailures)
// }
} catch (throwable: Throwable) { } catch (throwable: Throwable) {
return SpaceService.JoinSpaceResult.Fail(throwable) return SpaceService.JoinSpaceResult.Fail(throwable)
} }

View file

@ -0,0 +1,116 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.space
import io.realm.RealmConfiguration
import kotlinx.coroutines.TimeoutCancellationException
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
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.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.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.task.Task
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.inject.Inject
internal interface JoinSpaceTask : Task<JoinSpaceTask.Params, Unit> {
data class Params(
val roomIdOrAlias: String,
val reason: String?,
val viaServers: List<String> = emptyList()
)
}
internal class DefaultJoinSpaceTask @Inject constructor(
private val roomAPI: RoomAPI,
private val joinRoomTask: JoinRoomTask,
@SessionDatabase
private val realmConfiguration: RealmConfiguration,
private val spaceSummaryDataSource: SpaceSummaryDataSource,
private val eventBus: EventBus
) : JoinSpaceTask {
override suspend fun execute(params: JoinSpaceTask.Params) {
Timber.v("## Space: > Joining root space ${params.roomIdOrAlias} ...")
joinRoomTask.execute(JoinRoomTask.Params(
params.roomIdOrAlias,
params.reason,
params.viaServers
))
Timber.v("## Space: < Joining root space done for ${params.roomIdOrAlias}")
// we want to wait for sync result to check for auto join rooms
Timber.v("## Space: > Wait for post joined sync ${params.roomIdOrAlias} ...")
try {
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(2L)) { realm ->
realm.where(SpaceSummaryEntity::class.java)
.apply {
if (params.roomIdOrAlias.startsWith("!")) {
equalTo(SpaceSummaryEntityFields.SPACE_ID, params.roomIdOrAlias)
} else {
equalTo(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.CANONICAL_ALIAS, params.roomIdOrAlias)
}
}
.equalTo(SpaceSummaryEntityFields.ROOM_SUMMARY_ENTITY.MEMBERSHIP_STR, Membership.JOIN.name)
}
} catch (exception: TimeoutCancellationException) {
Timber.w("## Space: > Error created with timeout")
throw CreateRoomFailure.CreatedWithTimeout
}
Timber.v("## Space: > Sync done ...")
// after that i should have the children (? do i nead 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}")
summary?.children?.forEach {
val childRoomSummary = it.roomSummary ?: return@forEach
Timber.v("## Space: Processing child :[${childRoomSummary.roomId}] present: ${it.present} autoJoin:${it.autoJoin}")
if (it.present && it.autoJoin) {
// I should try to join as well
if (childRoomSummary.roomType == RoomType.SPACE) {
} else {
try {
Timber.v("## Space: Joining room child ${childRoomSummary.roomId}")
joinRoomTask.execute(JoinRoomTask.Params(
roomIdOrAlias = childRoomSummary.roomId,
reason = "Auto-join parent space",
viaServers = it.viaServers
))
} catch (failure: Throwable) {
// todo keep track for partial success
Timber.e("## Space: Failed to join room child ${childRoomSummary.roomId}")
}
}
}
}
}
}
// try {
// awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
// realm.where(RoomEntity::class.java)
// .equalTo(RoomEntityFields.ROOM_ID, roomId)
// }
// } catch (exception: TimeoutCancellationException) {
// throw CreateRoomFailure.CreatedWithTimeout
// }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020 New Vector Ltd * Copyright 2020 The Matrix.org Foundation C.I.C.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -212,6 +212,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it } val ageLocalTs = event.unsignedData?.age?.let { syncLocalTimestampMillis - it }
val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, insertType) val eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, insertType)
CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply { CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply {
Timber.v("## Space state event: $eventEntity")
eventId = event.eventId eventId = event.eventId
root = eventEntity root = eventEntity
} }

View file

@ -273,6 +273,7 @@
<activity android:name=".features.devtools.RoomDevToolActivity"/> <activity android:name=".features.devtools.RoomDevToolActivity"/>
<activity android:name=".features.spaces.SpacePreviewActivity"/> <activity android:name=".features.spaces.SpacePreviewActivity"/>
<activity android:name=".features.spaces.SpaceExploreActivity"/>
<!-- Services --> <!-- Services -->
<service <service

View file

@ -52,7 +52,6 @@ import im.vector.app.features.devtools.RoomDevToolStateEventListFragment
import im.vector.app.features.discovery.DiscoverySettingsFragment import im.vector.app.features.discovery.DiscoverySettingsFragment
import im.vector.app.features.discovery.change.SetIdentityServerFragment import im.vector.app.features.discovery.change.SetIdentityServerFragment
import im.vector.app.features.grouplist.GroupListFragment import im.vector.app.features.grouplist.GroupListFragment
import im.vector.app.features.grouplist.SpaceListFragment
import im.vector.app.features.home.HomeDetailFragment import im.vector.app.features.home.HomeDetailFragment
import im.vector.app.features.home.HomeDrawerFragment import im.vector.app.features.home.HomeDrawerFragment
import im.vector.app.features.home.LoadingFragment import im.vector.app.features.home.LoadingFragment
@ -118,6 +117,7 @@ import im.vector.app.features.settings.push.PushRulesFragment
import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment
import im.vector.app.features.share.IncomingShareFragment import im.vector.app.features.share.IncomingShareFragment
import im.vector.app.features.signout.soft.SoftLogoutFragment import im.vector.app.features.signout.soft.SoftLogoutFragment
import im.vector.app.features.spaces.SpaceListFragment
import im.vector.app.features.spaces.preview.SpacePreviewFragment import im.vector.app.features.spaces.preview.SpacePreviewFragment
import im.vector.app.features.terms.ReviewTermsFragment import im.vector.app.features.terms.ReviewTermsFragment
import im.vector.app.features.usercode.ShowUserCodeFragment import im.vector.app.features.usercode.ShowUserCodeFragment

View file

@ -31,9 +31,9 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.FragmentHomeDrawerBinding import im.vector.app.databinding.FragmentHomeDrawerBinding
import im.vector.app.features.grouplist.GroupListFragment import im.vector.app.features.grouplist.GroupListFragment
import im.vector.app.features.grouplist.SpaceListFragment
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.VectorSettingsActivity
import im.vector.app.features.spaces.SpaceListFragment
import im.vector.app.features.usercode.UserCodeActivity import im.vector.app.features.usercode.UserCodeActivity
import im.vector.app.features.workers.signout.SignOutUiWorker import im.vector.app.features.workers.signout.SignOutUiWorker

View file

@ -0,0 +1,73 @@
/*
* 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.spaces
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.airbnb.mvrx.MvRx
import im.vector.app.R
import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.app.features.spaces.explore.SpaceDirectoryArgs
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
import im.vector.app.features.spaces.preview.SpacePreviewArgs
import im.vector.app.features.spaces.preview.SpacePreviewFragment
class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>() {
override fun getBinding(): ActivitySimpleBinding = ActivitySimpleBinding.inflate(layoutInflater)
// lateinit var sharedActionViewModel: SpacePreviewSharedActionViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// sharedActionViewModel = viewModelProvider.get(SpacePreviewSharedActionViewModel::class.java)
// sharedActionViewModel
// .observe()
// .subscribe { action ->
// when (action) {
// SpacePreviewSharedAction.DismissAction -> finish()
// SpacePreviewSharedAction.ShowModalLoading -> showWaitingView()
// SpacePreviewSharedAction.HideModalLoading -> hideWaitingView()
// is SpacePreviewSharedAction.ShowErrorMessage -> action.error?.let { showSnackbar(it) }
// }
// }.disposeOnDestroy()
if (isFirstCreation()) {
val simpleName = SpaceDirectoryFragment::class.java.simpleName
val args = intent?.getParcelableExtra<SpacePreviewArgs>(MvRx.KEY_ARG)
if (supportFragmentManager.findFragmentByTag(simpleName) == null) {
supportFragmentManager.commitTransaction {
replace(R.id.simpleFragmentContainer,
SpacePreviewFragment::class.java,
Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) },
simpleName
)
}
}
}
}
companion object {
fun newIntent(context: Context, spaceId: String): Intent {
return Intent(context, SpaceExploreActivity::class.java).apply {
putExtra(MvRx.KEY_ARG, SpaceDirectoryArgs(spaceId))
}
}
}
}

View file

@ -1,21 +1,20 @@
/* /*
* Copyright 2019 New Vector Ltd * Copyright (c) 2021 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
*/ */
package im.vector.app.features.grouplist package im.vector.app.features.spaces
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -33,9 +32,6 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentGroupListBinding import im.vector.app.databinding.FragmentGroupListBinding
import im.vector.app.features.home.HomeActivitySharedAction import im.vector.app.features.home.HomeActivitySharedAction
import im.vector.app.features.home.HomeSharedActionViewModel import im.vector.app.features.home.HomeSharedActionViewModel
import im.vector.app.features.spaces.SpaceListAction
import im.vector.app.features.spaces.SpaceListViewEvents
import im.vector.app.features.spaces.SpacesListViewModel
import org.matrix.android.sdk.api.session.space.SpaceSummary import org.matrix.android.sdk.api.session.space.SpaceSummary
import javax.inject.Inject import javax.inject.Inject

View file

@ -1,29 +1,28 @@
/* /*
* Copyright 2019 New Vector Ltd * Copyright (c) 2021 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*
*/ */
package im.vector.app.features.grouplist package im.vector.app.features.spaces
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericFooterItem
import im.vector.app.core.ui.list.genericItemHeader import im.vector.app.core.ui.list.genericItemHeader
import im.vector.app.features.grouplist.homeSpaceSummaryItem
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.spaces.SpaceListViewState
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.space.SpaceSummary import org.matrix.android.sdk.api.session.space.SpaceSummary
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
@ -87,7 +86,6 @@ class SpaceSummaryController @Inject constructor(
summaries summaries
.filter { it.roomSummary.membership == Membership.JOIN } .filter { it.roomSummary.membership == Membership.JOIN }
.forEach { groupSummary -> .forEach { groupSummary ->
val isSelected = groupSummary.spaceId == selected?.spaceId val isSelected = groupSummary.spaceId == selected?.spaceId
if (groupSummary.spaceId == ALL_COMMUNITIES_GROUP_ID) { if (groupSummary.spaceId == ALL_COMMUNITIES_GROUP_ID) {
homeSpaceSummaryItem { homeSpaceSummaryItem {

View file

@ -15,7 +15,7 @@
* *
*/ */
package im.vector.app.features.grouplist package im.vector.app.features.spaces
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView

View file

@ -31,14 +31,12 @@ import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.grouplist.SelectedSpaceDataSource import im.vector.app.features.grouplist.SelectedSpaceDataSource
import im.vector.app.features.grouplist.SpaceListFragment
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.functions.BiFunction import io.reactivex.functions.BiFunction
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session 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.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.session.space.SpaceSummary import org.matrix.android.sdk.api.session.space.SpaceSummary
import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.rx
@ -117,9 +115,11 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
// PRIVATE METHODS ***************************************************************************** // PRIVATE METHODS *****************************************************************************
private fun handleSelectSpace(action: SpaceListAction.SelectSpace) = withState { state -> private fun handleSelectSpace(action: SpaceListAction.SelectSpace) = withState { state ->
// get uptodate version of the space
if (state.selectedSpace?.roomSummary?.membership == Membership.INVITE) { val summary = session.spaceService().getSpaceSummaries(roomSummaryQueryParams { roomId = QueryStringValue.Equals(action.spaceSummary.spaceId) })
_viewEvents.post(SpaceListViewEvents.OpenSpaceSummary(state.selectedSpace.roomSummary.roomId)) .firstOrNull()
if (summary?.roomSummary?.membership == Membership.INVITE) {
_viewEvents.post(SpaceListViewEvents.OpenSpaceSummary(summary.roomSummary.roomId))
// viewModelScope.launch(Dispatchers.IO) { // viewModelScope.launch(Dispatchers.IO) {
// tryOrNull { session.spaceService().peekSpace(action.spaceSummary.spaceId) }.let { // tryOrNull { session.spaceService().peekSpace(action.spaceSummary.spaceId) }.let {
// Timber.d("PEEK RESULT/ $it") // Timber.d("PEEK RESULT/ $it")
@ -139,7 +139,8 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
val roomSummaryQueryParams = roomSummaryQueryParams() { val roomSummaryQueryParams = roomSummaryQueryParams() {
memberships = listOf(Membership.JOIN, Membership.INVITE) memberships = listOf(Membership.JOIN, Membership.INVITE)
displayName = QueryStringValue.IsNotEmpty displayName = QueryStringValue.IsNotEmpty
excludeType = listOf(RoomType.MESSAGING, null) excludeType = listOf(/**RoomType.MESSAGING,$*/
null)
} }
Observable.combineLatest<SpaceSummary, List<SpaceSummary>, List<SpaceSummary>>( Observable.combineLatest<SpaceSummary, List<SpaceSummary>, List<SpaceSummary>>(
session session

View file

@ -0,0 +1,53 @@
/*
* 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.spaces.explore
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success
import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.epoxy.loadingItem
class SpaceDirectoryController : TypedEpoxyController<SpaceDirectoryState>() {
override fun buildModels(data: SpaceDirectoryState?) {
when (data?.summary) {
is Success -> {
// val directories = roomDirectoryListCreator.computeDirectories(asyncThirdPartyProtocol())
//
// directories.forEach {
// buildDirectory(it)
// }
}
is Incomplete -> {
loadingItem {
id("loading")
}
}
is Fail -> {
errorWithRetryItem {
id("error")
// text(errorFormatter.toHumanReadable(asyncThirdPartyProtocol.error))
// listener { callback?.retry() }
}
}
else -> {
}
}
}
}

View file

@ -0,0 +1,35 @@
/*
* 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.spaces.explore
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.ViewGroup
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentRoomDirectoryPickerBinding
import kotlinx.android.parcel.Parcelize
@Parcelize
data class SpaceDirectoryArgs(
val spaceId: String
) : Parcelable
class SpaceDirectoryFragment : VectorBaseFragment<FragmentRoomDirectoryPickerBinding>() {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
FragmentRoomDirectoryPickerBinding.inflate(layoutInflater, container, false)
}

View file

@ -0,0 +1,101 @@
/*
* 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.spaces.explore
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction
import io.reactivex.schedulers.Schedulers
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.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
data class SpaceDirectoryState(
// The current filter
val spaceId: String,
val currentFilter: String = "",
val summary: Async<SpaceSummary> = Uninitialized,
// True if more result are available server side
val hasMore: Boolean = false,
// Set of joined roomId / spaces,
val joinedRoomsIds: Set<String> = emptySet()
) : MvRxState {
constructor(args: SpaceDirectoryArgs) : this(spaceId = args.spaceId)
}
sealed class SpaceDirectoryViewAction : VectorViewModelAction
sealed class SpaceDirectoryViewEvents : VectorViewEvents
class SpaceDirectoryViewModel @AssistedInject constructor(
@Assisted initialState: SpaceDirectoryState,
private val session: Session
) : VectorViewModel<SpaceDirectoryState, VectorViewModelAction, SpaceDirectoryViewEvents>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: SpaceDirectoryState): SpaceDirectoryViewModel
}
companion object : MvRxViewModelFactory<SpaceDirectoryViewModel, SpaceDirectoryState> {
override fun create(viewModelContext: ViewModelContext, state: SpaceDirectoryState): SpaceDirectoryViewModel? {
val factory = when (viewModelContext) {
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
is ActivityViewModelContext -> viewModelContext.activity as? Factory
}
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
}
}
init {
val queryParams = roomSummaryQueryParams {
roomId = QueryStringValue.Equals(initialState.spaceId)
}
viewModelScope.launch(Dispatchers.IO) {
session
.rx()
.liveSpaceSummaries(queryParams)
.observeOn(Schedulers.computation())
.map { sum -> Optional.from(sum.firstOrNull()) }
.unwrap()
.execute { async ->
copy(summary = async)
}
}
}
override fun handle(action: VectorViewModelAction) {
TODO("Not yet implemented")
}
}