MSC 2946 WIP
This commit is contained in:
parent
57f17620b5
commit
186024b271
@ -18,6 +18,8 @@ package org.matrix.android.sdk.api.session.space
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import org.matrix.android.sdk.api.session.room.RoomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
|
||||
|
||||
typealias SpaceSummaryQueryParams = RoomSummaryQueryParams
|
||||
@ -43,6 +45,11 @@ interface SpaceService {
|
||||
*/
|
||||
suspend fun peekSpace(spaceId: String) : SpacePeekResult
|
||||
|
||||
/**
|
||||
* Get's information of a space by querying the server
|
||||
*/
|
||||
suspend fun querySpaceChildren(spaceId: String) : Pair<RoomSummary, List<SpaceChildInfo>>
|
||||
|
||||
/**
|
||||
* Get a live list of space summaries. This list is refreshed as soon as the data changes.
|
||||
* @return the [LiveData] of List[SpaceSummary]
|
||||
@ -69,4 +76,6 @@ interface SpaceService {
|
||||
reason: String? = null,
|
||||
viaServers: List<String> = emptyList(),
|
||||
autoJoinChild: List<ChildAutoJoinInfo>) : JoinSpaceResult
|
||||
|
||||
suspend fun rejectInvite(spaceId: String, reason: String?)
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ import org.matrix.android.sdk.internal.session.room.send.RedactEventWorker
|
||||
import org.matrix.android.sdk.internal.session.room.send.SendEventWorker
|
||||
import org.matrix.android.sdk.internal.session.search.SearchModule
|
||||
import org.matrix.android.sdk.internal.session.signout.SignOutModule
|
||||
import org.matrix.android.sdk.internal.session.space.SpaceModule
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncModule
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncTask
|
||||
import org.matrix.android.sdk.internal.session.sync.SyncTokenStore
|
||||
@ -91,7 +92,8 @@ import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
|
||||
FederationModule::class,
|
||||
CallModule::class,
|
||||
SearchModule::class,
|
||||
ThirdPartyModule::class
|
||||
ThirdPartyModule::class,
|
||||
SpaceModule::class
|
||||
]
|
||||
)
|
||||
@SessionScope
|
||||
|
@ -90,11 +90,7 @@ 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
|
||||
|
||||
@Module
|
||||
|
@ -18,12 +18,17 @@ package org.matrix.android.sdk.internal.session.space
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
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.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.space.CreateSpaceParams
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.session.space.SpaceService
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummary
|
||||
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.room.RoomGetter
|
||||
import org.matrix.android.sdk.internal.session.room.alias.DeleteRoomAliasTask
|
||||
@ -31,6 +36,7 @@ import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
|
||||
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
|
||||
import org.matrix.android.sdk.internal.session.room.membership.joining.JoinRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.membership.leaving.LeaveRoomTask
|
||||
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
|
||||
@ -50,6 +56,8 @@ internal class DefaultSpaceService @Inject constructor(
|
||||
private val roomGetter: RoomGetter,
|
||||
private val spaceSummaryDataSource: SpaceSummaryDataSource,
|
||||
private val peekSpaceTask: PeekSpaceTask,
|
||||
private val resolveSpaceInfoTask: ResolveSpaceInfoTask,
|
||||
private val leaveRoomTask: LeaveRoomTask,
|
||||
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
|
||||
private val taskExecutor: TaskExecutor
|
||||
) : SpaceService {
|
||||
@ -76,7 +84,55 @@ internal class DefaultSpaceService @Inject constructor(
|
||||
return peekSpaceTask.execute(PeekSpaceTask.Params(spaceId))
|
||||
}
|
||||
|
||||
override suspend fun joinSpace(spaceIdOrAlias: String, reason: String?, viaServers: List<String>, autoJoinChild: List<SpaceService.ChildAutoJoinInfo>): SpaceService.JoinSpaceResult {
|
||||
override suspend fun querySpaceChildren(spaceId: String): Pair<RoomSummary, List<SpaceChildInfo>> {
|
||||
return resolveSpaceInfoTask.execute(ResolveSpaceInfoTask.Params.withId(spaceId)).let { response ->
|
||||
val spaceDesc = response.rooms?.firstOrNull { it.roomId == spaceId }
|
||||
Pair(
|
||||
first = RoomSummary(
|
||||
roomId = spaceDesc?.roomId ?: spaceId,
|
||||
roomType = spaceDesc?.roomType,
|
||||
name = spaceDesc?.name ?: "",
|
||||
displayName = spaceDesc?.name ?: "",
|
||||
topic = spaceDesc?.topic ?: "",
|
||||
joinedMembersCount = spaceDesc?.numJoinedMembers,
|
||||
avatarUrl = spaceDesc?.avatarUrl ?: "",
|
||||
encryptionEventTs = null,
|
||||
typingUsers = emptyList(),
|
||||
isEncrypted = false
|
||||
),
|
||||
second = response.rooms
|
||||
?.filter { it.roomId != spaceId }
|
||||
?.map { childSummary ->
|
||||
val childStateEv = response.events
|
||||
?.firstOrNull { it.stateKey == childSummary.roomId && it.type == EventType.STATE_SPACE_CHILD }
|
||||
?.content.toModel<SpaceChildContent>()
|
||||
SpaceChildInfo(
|
||||
roomSummary = RoomSummary(
|
||||
roomId = childSummary.roomId,
|
||||
roomType = childSummary.roomType,
|
||||
name = childSummary.name ?: "",
|
||||
displayName = childSummary.name ?: "",
|
||||
topic = childSummary.topic ?: "",
|
||||
joinedMembersCount = childSummary.numJoinedMembers,
|
||||
avatarUrl = childSummary.avatarUrl ?: "",
|
||||
encryptionEventTs = null,
|
||||
typingUsers = emptyList(),
|
||||
isEncrypted = false
|
||||
),
|
||||
order = childStateEv?.order,
|
||||
present = childStateEv?.present ?: false,
|
||||
autoJoin = childStateEv?.default ?: false,
|
||||
viaServers = childStateEv?.via ?: emptyList()
|
||||
)
|
||||
} ?: emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun joinSpace(spaceIdOrAlias: String,
|
||||
reason: String?,
|
||||
viaServers: List<String>,
|
||||
autoJoinChild: List<SpaceService.ChildAutoJoinInfo>): SpaceService.JoinSpaceResult {
|
||||
try {
|
||||
joinSpaceTask.execute(JoinSpaceTask.Params(spaceIdOrAlias, reason, viaServers))
|
||||
// TODO partial success
|
||||
@ -100,4 +156,8 @@ internal class DefaultSpaceService @Inject constructor(
|
||||
return SpaceService.JoinSpaceResult.Fail(throwable)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun rejectInvite(spaceId: String, reason: String?) {
|
||||
leaveRoomTask.execute(LeaveRoomTask.Params(spaceId, reason))
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021 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.
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.space
|
||||
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface ResolveSpaceInfoTask : Task<ResolveSpaceInfoTask.Params, SpacesResponse> {
|
||||
data class Params(
|
||||
val spaceId: String,
|
||||
val maxRoomPerSpace: Int,
|
||||
val limit: Int,
|
||||
val batchToken: String?
|
||||
) {
|
||||
companion object {
|
||||
fun withId(spaceId: String) = Params(spaceId, 10, 20, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class DefaultResolveSpaceInfoTask @Inject constructor(
|
||||
private val spaceApi: SpaceApi
|
||||
) : ResolveSpaceInfoTask {
|
||||
override suspend fun execute(params: ResolveSpaceInfoTask.Params): SpacesResponse {
|
||||
val body = SpaceSummaryParams(maxRoomPerSpace = params.maxRoomPerSpace, limit = params.limit, batch = params.batchToken ?: "")
|
||||
return executeRequest<SpacesResponse>(null) {
|
||||
apiCall = spaceApi.getSpaces(params.spaceId, body)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.space
|
||||
|
||||
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Path
|
||||
|
||||
internal interface SpaceApi {
|
||||
|
||||
/**
|
||||
*
|
||||
* POST /_matrix/client/r0/rooms/{roomID}/spaces
|
||||
* {
|
||||
* "max_rooms_per_space": 5, // The maximum number of rooms/subspaces to return for a given space, if negative unbounded. default: -1.
|
||||
* "limit": 100, // The maximum number of rooms/subspaces to return, server can override this, default: 100.
|
||||
* "batch": "opaque_string" // A token to use if this is a subsequent HTTP hit, default: "".
|
||||
* }
|
||||
*
|
||||
* MSC 2946 https://github.com/matrix-org/matrix-doc/blob/kegan/spaces-summary/proposals/2946-spaces-summary.md
|
||||
*/
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/spaces")
|
||||
fun getSpaces(@Path("roomId") spaceId: String,
|
||||
@Body params: SpaceSummaryParams
|
||||
): Call<SpacesResponse>
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.space
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SpaceChildSummaryResponse(
|
||||
/**
|
||||
* The total number of state events which point to or from this room (inbound/outbound edges).
|
||||
* This includes all m.space.child events in the room, in addition to m.room.parent events which point to this room as a parent.
|
||||
*/
|
||||
@Json(name = "num_refs") val numRefs: Int? = null,
|
||||
|
||||
/**
|
||||
* The room type, which is m.space for subspaces.
|
||||
* It can be omitted if there is no room type in which case it should be interpreted as a normal room.
|
||||
*/
|
||||
@Json(name = "room_type") val roomType: String? = null,
|
||||
|
||||
/**
|
||||
* Aliases of the room. May be empty.
|
||||
*/
|
||||
@Json(name = "aliases")
|
||||
val aliases: List<String>? = null,
|
||||
|
||||
/**
|
||||
* The canonical alias of the room, if any.
|
||||
*/
|
||||
@Json(name = "canonical_alias")
|
||||
val canonicalAlias: String? = null,
|
||||
|
||||
/**
|
||||
* The name of the room, if any.
|
||||
*/
|
||||
@Json(name = "name")
|
||||
val name: String? = null,
|
||||
|
||||
/**
|
||||
* Required. The number of members joined to the room.
|
||||
*/
|
||||
@Json(name = "num_joined_members")
|
||||
val numJoinedMembers: Int = 0,
|
||||
|
||||
/**
|
||||
* Required. The ID of the room.
|
||||
*/
|
||||
@Json(name = "room_id")
|
||||
val roomId: String,
|
||||
|
||||
/**
|
||||
* The topic of the room, if any.
|
||||
*/
|
||||
@Json(name = "topic")
|
||||
val topic: String? = null,
|
||||
|
||||
/**
|
||||
* Required. Whether the room may be viewed by guest users without joining.
|
||||
*/
|
||||
@Json(name = "world_readable")
|
||||
val worldReadable: Boolean = false,
|
||||
|
||||
/**
|
||||
* Required. Whether guest users may join the room and participate in it. If they can,
|
||||
* they will be subject to ordinary power level rules like any other user.
|
||||
*/
|
||||
@Json(name = "guest_can_join")
|
||||
val guestCanJoin: Boolean = false,
|
||||
|
||||
/**
|
||||
* The URL for the room's avatar, if one is set.
|
||||
*/
|
||||
@Json(name = "avatar_url")
|
||||
val avatarUrl: String? = null,
|
||||
|
||||
/**
|
||||
* Undocumented item
|
||||
*/
|
||||
@Json(name = "m.federate")
|
||||
val isFederated: Boolean = false
|
||||
)
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.DefaultPeekSpaceTask
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
|
||||
import retrofit2.Retrofit
|
||||
|
||||
@Module
|
||||
internal abstract class SpaceModule {
|
||||
|
||||
@Module
|
||||
companion object {
|
||||
@Provides
|
||||
@JvmStatic
|
||||
@SessionScope
|
||||
fun providesSpacesAPI(retrofit: Retrofit): SpaceApi {
|
||||
return retrofit.create(SpaceApi::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract fun bindResolveSpaceTask(task: DefaultResolveSpaceInfoTask): ResolveSpaceInfoTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindPeekSpaceTask(task: DefaultPeekSpaceTask): PeekSpaceTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindJoinSpaceTask(task: DefaultJoinSpaceTask): JoinSpaceTask
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.space
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SpaceSummaryParams(
|
||||
/** The maximum number of rooms/subspaces to return for a given space, if negative unbounded. default: -1*/
|
||||
@Json(name = "max_rooms_per_space") val maxRoomPerSpace: Int = 100,
|
||||
/** The maximum number of rooms/subspaces to return, server can override this, default: 100 */
|
||||
@Json(name = "limit") val limit: Int = 100,
|
||||
/** A token to use if this is a subsequent HTTP hit, default: "".*/
|
||||
@Json(name = "batch") val batch: String = ""
|
||||
)
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.session.space
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class SpacesResponse(
|
||||
/** Its presence indicates that there are more results to return. */
|
||||
@Json(name = "next_batch") val nextBatch: String? = null,
|
||||
/** Rooms information like name/avatar/type ... */
|
||||
@Json(name = "rooms") val rooms: List<SpaceChildSummaryResponse>? = null,
|
||||
/** These are the edges of the graph. The objects in the array are complete (or stripped?) m.room.parent or m.space.child events. */
|
||||
@Json(name = "events") val events: List<Event>? = null
|
||||
)
|
@ -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.
|
||||
|
@ -17,17 +17,14 @@
|
||||
package im.vector.app.features.spaces.preview
|
||||
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.loadingItem
|
||||
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.core.utils.TextUtils
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.ISpaceChild
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpaceChildPeekResult
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpaceSubChildPeekResult
|
||||
import javax.inject.Inject
|
||||
|
||||
class SpacePreviewController @Inject constructor(
|
||||
@ -40,78 +37,100 @@ class SpacePreviewController @Inject constructor(
|
||||
var interactionListener: InteractionListener? = null
|
||||
|
||||
override fun buildModels(data: SpacePreviewState?) {
|
||||
val result: SpacePeekResult = data?.peekResult?.invoke() ?: return
|
||||
val result = data?.childInfoList?.invoke() ?: return
|
||||
|
||||
when (result) {
|
||||
is SpacePeekResult.SpacePeekError -> {
|
||||
genericFooterItem {
|
||||
id("failed")
|
||||
// TODO
|
||||
text("Failed to resolve")
|
||||
}
|
||||
val memberCount = data.spaceInfo.invoke()?.memberCount ?: 0
|
||||
|
||||
spaceTopSummaryItem {
|
||||
id("info")
|
||||
formattedMemberCount(stringProvider.getQuantityString(R.plurals.room_title_members, memberCount, memberCount))
|
||||
topic(data.spaceInfo.invoke()?.topic ?: data.topic ?: "")
|
||||
}
|
||||
|
||||
if (result.isNotEmpty()) {
|
||||
genericItemHeader {
|
||||
id("header_rooms")
|
||||
text(stringProvider.getString(R.string.rooms))
|
||||
}
|
||||
is SpacePeekResult.Success -> {
|
||||
// add summary info
|
||||
val memberCount = result.summary.roomPeekResult.numJoinedMembers ?: 0
|
||||
|
||||
spaceTopSummaryItem {
|
||||
id("info")
|
||||
formattedMemberCount(stringProvider.getQuantityString(R.plurals.room_title_members, memberCount, memberCount))
|
||||
topic(result.summary.roomPeekResult.topic ?: "")
|
||||
}
|
||||
|
||||
genericItemHeader {
|
||||
id("header_rooms")
|
||||
text(stringProvider.getString(R.string.rooms))
|
||||
}
|
||||
|
||||
buildChildren(result.summary.children, 0)
|
||||
}
|
||||
buildChildren(result, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildChildren(children: List<ISpaceChild>, depth: Int) {
|
||||
private fun buildChildren(children: List<ChildInfo>, depth: Int) {
|
||||
children.forEach { child ->
|
||||
when (child) {
|
||||
is SpaceSubChildPeekResult -> {
|
||||
when (val roomPeekResult = child.roomPeekResult) {
|
||||
is PeekResult.Success -> {
|
||||
subSpaceItem {
|
||||
id(roomPeekResult.roomId)
|
||||
roomId(roomPeekResult.roomId)
|
||||
title(roomPeekResult.name)
|
||||
depth(depth)
|
||||
avatarUrl(roomPeekResult.avatarUrl)
|
||||
avatarRenderer(avatarRenderer)
|
||||
}
|
||||
buildChildren(child.children, depth + 1)
|
||||
}
|
||||
else -> {
|
||||
// ?? TODO
|
||||
}
|
||||
|
||||
if (child.isSubSpace == true) {
|
||||
subSpaceItem {
|
||||
id(child.roomId)
|
||||
roomId(child.roomId)
|
||||
title(child.name)
|
||||
depth(depth)
|
||||
avatarUrl(child.avatarUrl)
|
||||
avatarRenderer(avatarRenderer)
|
||||
}
|
||||
when (child.children) {
|
||||
is Loading -> {
|
||||
loadingItem { id("loading_children_${child.roomId}") }
|
||||
}
|
||||
is Success -> {
|
||||
buildChildren(child.children.invoke(), depth + 1)
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
is SpaceChildPeekResult -> {
|
||||
// We have to check if the peek result was success
|
||||
when (val roomPeekResult = child.roomPeekResult) {
|
||||
is PeekResult.Success -> {
|
||||
roomChildItem {
|
||||
id(child.id)
|
||||
depth(depth)
|
||||
roomId(roomPeekResult.roomId)
|
||||
title(roomPeekResult.name ?: "")
|
||||
topic(roomPeekResult.topic ?: "")
|
||||
avatarUrl(roomPeekResult.avatarUrl)
|
||||
memberCount(TextUtils.formatCountToShortDecimal(roomPeekResult.numJoinedMembers ?: 0))
|
||||
avatarRenderer(avatarRenderer)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// What to do here?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
roomChildItem {
|
||||
id(child.roomId)
|
||||
depth(depth)
|
||||
roomId(child.roomId)
|
||||
title(child.name ?: "")
|
||||
topic(child.topic ?: "")
|
||||
avatarUrl(child.avatarUrl)
|
||||
memberCount(TextUtils.formatCountToShortDecimal(child.memberCount ?: 0))
|
||||
avatarRenderer(avatarRenderer)
|
||||
}
|
||||
}
|
||||
// when (child) {
|
||||
// is SpaceSubChildPeekResult -> {
|
||||
// when (val roomPeekResult = child.roomPeekResult) {
|
||||
// is PeekResult.Success -> {
|
||||
// subSpaceItem {
|
||||
// id(roomPeekResult.roomId)
|
||||
// roomId(roomPeekResult.roomId)
|
||||
// title(roomPeekResult.name)
|
||||
// depth(depth)
|
||||
// avatarUrl(roomPeekResult.avatarUrl)
|
||||
// avatarRenderer(avatarRenderer)
|
||||
// }
|
||||
// buildChildren(child.children, depth + 1)
|
||||
// }
|
||||
// else -> {
|
||||
// // ?? TODO
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// is SpaceChildPeekResult -> {
|
||||
// // We have to check if the peek result was success
|
||||
// when (val roomPeekResult = child.roomPeekResult) {
|
||||
// is PeekResult.Success -> {
|
||||
// roomChildItem {
|
||||
// id(child.id)
|
||||
// depth(depth)
|
||||
// roomId(roomPeekResult.roomId)
|
||||
// title(roomPeekResult.name ?: "")
|
||||
// topic(roomPeekResult.topic ?: "")
|
||||
// avatarUrl(roomPeekResult.avatarUrl)
|
||||
// memberCount(TextUtils.formatCountToShortDecimal(roomPeekResult.numJoinedMembers ?: 0))
|
||||
// avatarRenderer(avatarRenderer)
|
||||
// }
|
||||
// }
|
||||
// else -> {
|
||||
// // What to do here?
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ import im.vector.app.features.spaces.SpacePreviewSharedActionViewModel
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -99,7 +98,7 @@ class SpacePreviewFragment @Inject constructor(
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
when (it.peekResult) {
|
||||
when (it.spaceInfo) {
|
||||
is Uninitialized,
|
||||
is Loading -> {
|
||||
views.spacePreviewPeekingProgress.isVisible = true
|
||||
@ -141,21 +140,23 @@ class SpacePreviewFragment @Inject constructor(
|
||||
}
|
||||
|
||||
private fun updateToolbar(spacePreviewState: SpacePreviewState) {
|
||||
when (val preview = spacePreviewState.peekResult.invoke()) {
|
||||
is SpacePeekResult.Success -> {
|
||||
val roomPeekResult = preview.summary.roomPeekResult
|
||||
val mxItem = MatrixItem.RoomItem(roomPeekResult.roomId, roomPeekResult.name, roomPeekResult.avatarUrl)
|
||||
avatarRenderer.renderSpace(mxItem, views.spacePreviewToolbarAvatar)
|
||||
views.roomPreviewNoPreviewToolbarTitle.text = roomPeekResult.name
|
||||
}
|
||||
is SpacePeekResult.SpacePeekError,
|
||||
null -> {
|
||||
// what to do here?
|
||||
val mxItem = MatrixItem.RoomItem(spacePreviewState.idOrAlias, spacePreviewState.name, spacePreviewState.avatarUrl)
|
||||
avatarRenderer.renderSpace(mxItem, views.spacePreviewToolbarAvatar)
|
||||
views.roomPreviewNoPreviewToolbarTitle.text = spacePreviewState.name
|
||||
}
|
||||
}
|
||||
// when (val preview = spacePreviewState.peekResult.invoke()) {
|
||||
// is SpacePeekResult.Success -> {
|
||||
// val roomPeekResult = preview.summary.roomPeekResult
|
||||
val spaceName = spacePreviewState.spaceInfo.invoke()?.name ?: spacePreviewState.name ?: ""
|
||||
val spaceAvatarUrl = spacePreviewState.spaceInfo.invoke()?.avatarUrl ?: spacePreviewState.avatarUrl
|
||||
val mxItem = MatrixItem.RoomItem(spacePreviewState.idOrAlias, spaceName, spaceAvatarUrl)
|
||||
avatarRenderer.renderSpace(mxItem, views.spacePreviewToolbarAvatar)
|
||||
views.roomPreviewNoPreviewToolbarTitle.text = spaceName
|
||||
// }
|
||||
// is SpacePeekResult.SpacePeekError,
|
||||
// null -> {
|
||||
// // what to do here?
|
||||
// val mxItem = MatrixItem.RoomItem(spacePreviewState.idOrAlias, spacePreviewState.name, spacePreviewState.avatarUrl)
|
||||
// avatarRenderer.renderSpace(mxItem, views.spacePreviewToolbarAvatar)
|
||||
// views.roomPreviewNoPreviewToolbarTitle.text = spacePreviewState.name
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
@ -19,13 +19,25 @@ package im.vector.app.features.spaces.preview
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
|
||||
|
||||
data class SpacePreviewState(
|
||||
val idOrAlias: String,
|
||||
val name: String? = null,
|
||||
val topic: String? = null,
|
||||
val avatarUrl: String? = null,
|
||||
val peekResult: Async<SpacePeekResult> = Uninitialized
|
||||
val spaceInfo: Async<ChildInfo> = Uninitialized,
|
||||
val childInfoList: Async<List<ChildInfo>> = Uninitialized
|
||||
) : MvRxState {
|
||||
constructor(args: SpacePreviewArgs) : this(idOrAlias = args.idOrAlias)
|
||||
}
|
||||
|
||||
data class ChildInfo(
|
||||
val roomId: String,
|
||||
val avatarUrl: String?,
|
||||
val name: String?,
|
||||
val topic: String?,
|
||||
val memberCount: Int?,
|
||||
val isSubSpace: Boolean?,
|
||||
val viaServers: List<String>?,
|
||||
val children: Async<List<ChildInfo>>
|
||||
)
|
||||
|
@ -23,6 +23,7 @@ import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import com.squareup.inject.assisted.Assisted
|
||||
import com.squareup.inject.assisted.AssistedInject
|
||||
@ -30,9 +31,12 @@ import im.vector.app.core.platform.VectorViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.peeking.PeekResult
|
||||
import org.matrix.android.sdk.api.session.space.SpaceService
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpaceSubChildPeekResult
|
||||
import timber.log.Timber
|
||||
|
||||
class SpacePreviewViewModel @AssistedInject constructor(
|
||||
@Assisted private val initialState: SpacePreviewState,
|
||||
@ -73,31 +77,29 @@ class SpacePreviewViewModel @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDismissInvite() {
|
||||
TODO("Not yet implemented")
|
||||
private fun handleDismissInvite() = withState { state ->
|
||||
// Here we need to join the space himself as well as the default rooms in that space
|
||||
// TODO modal loading
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
session.spaceService().rejectInvite(initialState.idOrAlias, null)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "## Space: Failed to reject invite")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAcceptInvite() = withState { state ->
|
||||
// Here we need to join the space himself as well as the default rooms in that space
|
||||
val spaceInfo = state.peekResult.invoke() as? SpacePeekResult.Success
|
||||
|
||||
// TODO if we have no summary, we cannot find auto join rooms...
|
||||
// So maybe we should trigger a retry on summary after the join?
|
||||
val spaceVia = (spaceInfo?.summary?.roomPeekResult as? PeekResult.Success)?.viaServers ?: emptyList()
|
||||
val autoJoinChildren = spaceInfo?.summary?.children
|
||||
?.filter { it.default == true }
|
||||
?.map {
|
||||
SpaceService.ChildAutoJoinInfo(
|
||||
it.id,
|
||||
// via servers
|
||||
(it.roomPeekResult as? PeekResult.Success)?.viaServers ?: emptyList()
|
||||
)
|
||||
} ?: emptyList()
|
||||
val spaceInfo = state.spaceInfo.invoke()
|
||||
val spaceVia = spaceInfo?.viaServers ?: emptyList()
|
||||
|
||||
// trigger modal loading
|
||||
_viewEvents.post(SpacePreviewViewEvents.StartJoining)
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val joinResult = session.spaceService().joinSpace(spaceInfo?.summary?.idOrAlias ?: initialState.idOrAlias, null, spaceVia, autoJoinChildren)
|
||||
val joinResult = session.spaceService().joinSpace(initialState.idOrAlias, null, spaceVia, emptyList())
|
||||
when (joinResult) {
|
||||
SpaceService.JoinSpaceResult.Success,
|
||||
is SpaceService.JoinSpaceResult.PartialSuccess -> {
|
||||
@ -116,20 +118,110 @@ class SpacePreviewViewModel @AssistedInject constructor(
|
||||
initialized = true
|
||||
// peek for the room
|
||||
setState {
|
||||
copy(peekResult = Loading())
|
||||
copy(
|
||||
spaceInfo = Loading(),
|
||||
childInfoList = Loading()
|
||||
)
|
||||
}
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val result = session.spaceService().peekSpace(initialState.idOrAlias)
|
||||
setState {
|
||||
copy(peekResult = Success(result))
|
||||
}
|
||||
resolveSpaceInfo()
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(peekResult = Fail(failure))
|
||||
}
|
||||
Timber.e(failure, "## Space: Failed to resolve space info. Fallback to picking")
|
||||
fallBackResolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun resolveSpaceInfo() {
|
||||
val resolveResult = session.spaceService().querySpaceChildren(initialState.idOrAlias)
|
||||
setState {
|
||||
copy(
|
||||
spaceInfo = Success(
|
||||
resolveResult.first.let {
|
||||
ChildInfo(
|
||||
roomId = it.roomId,
|
||||
avatarUrl = it.avatarUrl,
|
||||
name = it.name,
|
||||
topic = it.topic,
|
||||
memberCount = it.joinedMembersCount,
|
||||
isSubSpace = it.roomType == RoomType.SPACE,
|
||||
children = Uninitialized,
|
||||
viaServers = null
|
||||
)
|
||||
}
|
||||
),
|
||||
childInfoList = Success(
|
||||
resolveResult.second.map {
|
||||
ChildInfo(
|
||||
roomId = it.roomSummary?.roomId ?: "",
|
||||
avatarUrl = it.roomSummary?.avatarUrl,
|
||||
name = it.roomSummary?.name,
|
||||
topic = it.roomSummary?.topic,
|
||||
memberCount = it.roomSummary?.joinedMembersCount,
|
||||
isSubSpace = it.roomSummary?.roomType == RoomType.SPACE,
|
||||
children = Uninitialized,
|
||||
viaServers = null
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fallBackResolve() {
|
||||
try {
|
||||
val resolveResult: SpacePeekResult = session.spaceService().peekSpace(initialState.idOrAlias)
|
||||
val spaceInfo = (resolveResult as? SpacePeekResult.Success)?.summary?.roomPeekResult
|
||||
setState {
|
||||
copy(
|
||||
spaceInfo = Success(
|
||||
ChildInfo(
|
||||
roomId = spaceInfo?.roomId ?: initialState.idOrAlias,
|
||||
avatarUrl = spaceInfo?.avatarUrl,
|
||||
name = spaceInfo?.name,
|
||||
topic = spaceInfo?.topic,
|
||||
memberCount = spaceInfo?.numJoinedMembers,
|
||||
isSubSpace = true,
|
||||
children = Uninitialized,
|
||||
viaServers = spaceInfo?.viaServers
|
||||
|
||||
)
|
||||
),
|
||||
childInfoList = resolveResult.let {
|
||||
when (it) {
|
||||
is SpacePeekResult.Success -> {
|
||||
(resolveResult as SpacePeekResult.Success).summary.children.mapNotNull { spaceChild ->
|
||||
val roomPeekResult = spaceChild.roomPeekResult
|
||||
if (roomPeekResult is PeekResult.Success) {
|
||||
ChildInfo(
|
||||
roomId = spaceChild.id,
|
||||
avatarUrl = roomPeekResult.avatarUrl,
|
||||
name = roomPeekResult.name,
|
||||
topic = roomPeekResult.topic,
|
||||
memberCount = roomPeekResult.numJoinedMembers,
|
||||
isSubSpace = spaceChild is SpaceSubChildPeekResult,
|
||||
children = Uninitialized,
|
||||
viaServers = roomPeekResult.viaServers
|
||||
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
Success(emptyList())
|
||||
}
|
||||
else -> {
|
||||
Fail(Exception("Failed to get info"))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(spaceInfo = Fail(failure), childInfoList = Fail(failure))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user