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.RoomDirectoryVisibility
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
// 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) {
field = value
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.RoomSummary
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
data class SpaceSummary(
val spaceId: String,
val roomSummary: RoomSummary,
val children: List<IRoomSummary>
val children: List<SpaceChildInfo>
) : 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.
* 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.withContext
import kotlinx.coroutines.withTimeout
import timber.log.Timber
internal suspend fun <T> awaitNotEmptyResult(realmConfiguration: RealmConfiguration,
timeoutMillis: Long,
@ -40,6 +41,7 @@ internal suspend fun <T> awaitNotEmptyResult(realmConfiguration: RealmConfigurat
val listener = object : RealmChangeListener<RealmResults<T>> {
override fun onChange(it: RealmResults<T>) {
Timber.v("## Space: $it")
if (it.isNotEmpty()) {
result.removeChangeListener(this)
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.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 timber.log.Timber
import javax.inject.Inject
@ -207,9 +207,16 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
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")
?.addField(SpaceSummaryEntityFields.SPACE_ID, String::class.java, FieldAttribute.PRIMARY_KEY)
?.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
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
@ -27,7 +28,13 @@ internal class SpaceSummaryMapper @Inject constructor(private val roomSummaryMap
spaceId = spaceSummaryEntity.spaceId,
roomSummary = roomSummaryMapper.map(spaceSummaryEntity.roomSummaryEntity!!),
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,
ScalarTokenEntity::class,
WellknownIntegrationManagerConfigEntity::class,
SpaceSummaryEntity::class
SpaceSummaryEntity::class,
SpaceChildInfoEntity::class
])
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 = "",
var roomSummaryEntity: RoomSummaryEntity? = null,
var children: RealmList<RoomSummaryEntity> = RealmList()
var children: RealmList<SpaceChildInfoEntity> = RealmList()
// TODO public / private .. and more
) : 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.uploads.DefaultGetUploadsTask
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.JoinSpaceTask
import org.matrix.android.sdk.internal.session.space.peeking.DefaultPeekSpaceTask
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
import retrofit2.Retrofit
@ -241,6 +243,9 @@ internal abstract class RoomModule {
@Binds
abstract fun bindPeekSpaceTask(task: DefaultPeekSpaceTask): PeekSpaceTask
@Binds
abstract fun bindJoinSpaceTask(task: DefaultJoinSpaceTask): JoinSpaceTask
@Binds
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.model.CurrentStateEventEntity
import org.matrix.android.sdk.internal.database.query.whereType
import timber.log.Timber
/**
* Relationship between rooms and spaces
@ -38,13 +39,30 @@ internal class RoomRelationshipHelper(private val realm: Realm,
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)
.findAll()
.filter { ContentMapper.map(it.root?.content).toModel<SpaceChildContent>()?.present == true }
// .filter { ContentMapper.map(it.root?.content).toModel<SpaceChildContent>()?.present == true }
.mapNotNull {
// 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
import io.realm.Realm
import io.realm.kotlin.createObject
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.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.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.TimelineEventEntity
import org.matrix.android.sdk.internal.database.query.findAllInRoomWithSendStates
@ -162,13 +164,26 @@ internal class RoomSummaryUpdater @Inject constructor(
}
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.children.clear()
spaceSummaryEntity.children.addAll(
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,
private val createRoomTask: CreateRoomTask,
private val joinRoomTask: JoinRoomTask,
private val joinSpaceTask: JoinSpaceTask,
private val markAllRoomsReadTask: MarkAllRoomsReadTask,
private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
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 {
try {
joinRoomTask.execute(JoinRoomTask.Params(spaceIdOrAlias, reason, viaServers))
val childJoinFailures = mutableMapOf<String, Throwable>()
autoJoinChild.forEach { info ->
// TODO what if the child is it self a subspace with some default children?
try {
joinRoomTask.execute(JoinRoomTask.Params(info.roomIdOrAlias, null, info.viaServers))
} 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 {
SpaceService.JoinSpaceResult.PartialSuccess(childJoinFailures)
}
joinSpaceTask.execute(JoinSpaceTask.Params(spaceIdOrAlias, reason, viaServers))
// TODO partial success
return SpaceService.JoinSpaceResult.Success
// val childJoinFailures = mutableMapOf<String, Throwable>()
// autoJoinChild.forEach { info ->
// // TODO what if the child is it self a subspace with some default children?
// try {
// joinRoomTask.execute(JoinRoomTask.Params(info.roomIdOrAlias, null, info.viaServers))
// } 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 {
// SpaceService.JoinSpaceResult.PartialSuccess(childJoinFailures)
// }
} catch (throwable: 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");
* 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 eventEntity = event.toEntity(roomId, SendState.SYNCED, ageLocalTs).copyToRealmOrIgnore(realm, insertType)
CurrentStateEventEntity.getOrCreate(realm, roomId, event.stateKey, event.type).apply {
Timber.v("## Space state event: $eventEntity")
eventId = event.eventId
root = eventEntity
}

View file

@ -273,6 +273,7 @@
<activity android:name=".features.devtools.RoomDevToolActivity"/>
<activity android:name=".features.spaces.SpacePreviewActivity"/>
<activity android:name=".features.spaces.SpaceExploreActivity"/>
<!-- Services -->
<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.change.SetIdentityServerFragment
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.HomeDrawerFragment
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.share.IncomingShareFragment
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.terms.ReviewTermsFragment
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.databinding.FragmentHomeDrawerBinding
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.VectorSettingsActivity
import im.vector.app.features.spaces.SpaceListFragment
import im.vector.app.features.usercode.UserCodeActivity
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");
* 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,
* 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.grouplist
package im.vector.app.features.spaces
import android.os.Bundle
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.features.home.HomeActivitySharedAction
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 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");
* 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,
* 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.grouplist
package im.vector.app.features.spaces
import com.airbnb.epoxy.EpoxyController
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericFooterItem
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.spaces.SpaceListViewState
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.util.toMatrixItem
@ -87,7 +86,6 @@ class SpaceSummaryController @Inject constructor(
summaries
.filter { it.roomSummary.membership == Membership.JOIN }
.forEach { groupSummary ->
val isSelected = groupSummary.spaceId == selected?.spaceId
if (groupSummary.spaceId == ALL_COMMUNITIES_GROUP_ID) {
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.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.resources.StringProvider
import im.vector.app.features.grouplist.SelectedSpaceDataSource
import im.vector.app.features.grouplist.SpaceListFragment
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
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.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.roomSummaryQueryParams
import org.matrix.android.sdk.api.session.space.SpaceSummary
import org.matrix.android.sdk.rx.rx
@ -117,9 +115,11 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
// PRIVATE METHODS *****************************************************************************
private fun handleSelectSpace(action: SpaceListAction.SelectSpace) = withState { state ->
if (state.selectedSpace?.roomSummary?.membership == Membership.INVITE) {
_viewEvents.post(SpaceListViewEvents.OpenSpaceSummary(state.selectedSpace.roomSummary.roomId))
// get uptodate version of the space
val summary = session.spaceService().getSpaceSummaries(roomSummaryQueryParams { roomId = QueryStringValue.Equals(action.spaceSummary.spaceId) })
.firstOrNull()
if (summary?.roomSummary?.membership == Membership.INVITE) {
_viewEvents.post(SpaceListViewEvents.OpenSpaceSummary(summary.roomSummary.roomId))
// viewModelScope.launch(Dispatchers.IO) {
// tryOrNull { session.spaceService().peekSpace(action.spaceSummary.spaceId) }.let {
// Timber.d("PEEK RESULT/ $it")
@ -139,7 +139,8 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
val roomSummaryQueryParams = roomSummaryQueryParams() {
memberships = listOf(Membership.JOIN, Membership.INVITE)
displayName = QueryStringValue.IsNotEmpty
excludeType = listOf(RoomType.MESSAGING, null)
excludeType = listOf(/**RoomType.MESSAGING,$*/
null)
}
Observable.combineLatest<SpaceSummary, List<SpaceSummary>, List<SpaceSummary>>(
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")
}
}