mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-23 01:45:52 +03:00
Basic space join / use tmp msc id / db model update
This commit is contained in:
parent
ab4f2429c4
commit
e2578a29ed
28 changed files with 549 additions and 53 deletions
|
@ -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>
|
||||
)
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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!!)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ import io.realm.annotations.RealmModule
|
|||
UserAccountDataEntity::class,
|
||||
ScalarTokenEntity::class,
|
||||
WellknownIntegrationManagerConfigEntity::class,
|
||||
SpaceSummaryEntity::class
|
||||
SpaceSummaryEntity::class,
|
||||
SpaceChildInfoEntity::class
|
||||
])
|
||||
internal class SessionRealmModule
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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() {
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// }
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -273,6 +273,7 @@
|
|||
|
||||
<activity android:name=".features.devtools.RoomDevToolActivity"/>
|
||||
<activity android:name=".features.spaces.SpacePreviewActivity"/>
|
||||
<activity android:name=".features.spaces.SpaceExploreActivity"/>
|
||||
<!-- Services -->
|
||||
|
||||
<service
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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 {
|
|
@ -15,7 +15,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
package im.vector.app.features.grouplist
|
||||
package im.vector.app.features.spaces
|
||||
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
|
@ -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
|
||||
|
|
|
@ -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 -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue