mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
room version cap support + room upgrade
This commit is contained in:
parent
0c88d11429
commit
171793d190
@ -16,6 +16,8 @@
|
||||
|
||||
package org.matrix.android.sdk.api.session.homeserver
|
||||
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities.Companion.MAX_UPLOAD_FILE_SIZE_UNKNOWN
|
||||
|
||||
data class HomeServerCapabilities(
|
||||
/**
|
||||
* True if it is possible to change the password of the account.
|
||||
@ -32,7 +34,9 @@ data class HomeServerCapabilities(
|
||||
/**
|
||||
* Default identity server url, provided in Wellknown
|
||||
*/
|
||||
val defaultIdentityServerUrl: String? = null
|
||||
val defaultIdentityServerUrl: String? = null,
|
||||
|
||||
val roomVersions: RoomVersionCapabilities? = null
|
||||
) {
|
||||
companion object {
|
||||
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.homeserver
|
||||
|
||||
data class RoomVersionCapabilities(
|
||||
val defaultRoomVersion: String,
|
||||
val supportedVersion: List<RoomVersionInfo>
|
||||
)
|
||||
|
||||
data class RoomVersionInfo(
|
||||
val version: String,
|
||||
val status: RoomVersionStatus
|
||||
)
|
||||
|
||||
enum class RoomVersionStatus {
|
||||
STABLE,
|
||||
UNSTABLE
|
||||
}
|
@ -34,6 +34,7 @@ import org.matrix.android.sdk.api.session.room.tags.TagsService
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineService
|
||||
import org.matrix.android.sdk.api.session.room.typing.TypingService
|
||||
import org.matrix.android.sdk.api.session.room.uploads.UploadsService
|
||||
import org.matrix.android.sdk.api.session.room.version.RoomVersionService
|
||||
import org.matrix.android.sdk.api.session.search.SearchResult
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
@ -57,7 +58,8 @@ interface Room :
|
||||
RelationService,
|
||||
RoomCryptoService,
|
||||
RoomPushRuleService,
|
||||
RoomAccountDataService {
|
||||
RoomAccountDataService,
|
||||
RoomVersionService {
|
||||
|
||||
/**
|
||||
* The roomId of this room
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.api.session.room.version
|
||||
|
||||
interface RoomVersionService {
|
||||
|
||||
fun getRoomVersion(): String
|
||||
|
||||
/**
|
||||
* Upgrade to the given room version
|
||||
* @return the replacement room id
|
||||
*/
|
||||
suspend fun upgradeToVersion(version: String): String
|
||||
|
||||
suspend fun getRecommendedVersion() : String
|
||||
|
||||
fun userMayUpgradeRoom(userId: String): Boolean
|
||||
}
|
@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session.space
|
||||
|
||||
import org.matrix.android.sdk.api.session.room.Room
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
|
||||
|
||||
interface Space {
|
||||
|
||||
@ -38,6 +39,8 @@ interface Space {
|
||||
autoJoin: Boolean = false,
|
||||
suggested: Boolean? = false)
|
||||
|
||||
fun getChildInfo(roomId: String): SpaceChildContent?
|
||||
|
||||
suspend fun removeChildren(roomId: String)
|
||||
|
||||
@Throws
|
||||
|
@ -46,7 +46,7 @@ import javax.inject.Inject
|
||||
class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
||||
|
||||
companion object {
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 14L
|
||||
const val SESSION_STORE_SCHEMA_VERSION = 15L
|
||||
}
|
||||
|
||||
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
|
||||
@ -66,6 +66,7 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
||||
if (oldVersion <= 11) migrateTo12(realm)
|
||||
if (oldVersion <= 12) migrateTo13(realm)
|
||||
if (oldVersion <= 13) migrateTo14(realm)
|
||||
if (oldVersion <= 14) migrateTo15(realm)
|
||||
}
|
||||
|
||||
private fun migrateTo1(realm: DynamicRealm) {
|
||||
@ -306,4 +307,10 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
||||
|
||||
roomAccountDataSchema.isEmbedded = true
|
||||
}
|
||||
|
||||
private fun migrateTo15(realm: DynamicRealm) {
|
||||
Timber.d("Step 14 -> 15")
|
||||
realm.schema.get("HomeServerCapabilitiesEntity")
|
||||
?.addField(HomeServerCapabilitiesEntityFields.ROOM_VERSION_JSON, String::class.java)
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,15 @@
|
||||
|
||||
package org.matrix.android.sdk.internal.database.mapper
|
||||
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||
import org.matrix.android.sdk.api.session.homeserver.RoomVersionCapabilities
|
||||
import org.matrix.android.sdk.api.session.homeserver.RoomVersionInfo
|
||||
import org.matrix.android.sdk.api.session.homeserver.RoomVersionStatus
|
||||
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import org.matrix.android.sdk.internal.session.homeserver.RoomVersions
|
||||
import org.matrix.android.sdk.internal.session.room.version.DefaultRoomVersionService
|
||||
|
||||
/**
|
||||
* HomeServerCapabilitiesEntity -> HomeSeverCapabilities
|
||||
@ -29,7 +36,21 @@ internal object HomeServerCapabilitiesMapper {
|
||||
canChangePassword = entity.canChangePassword,
|
||||
maxUploadFileSize = entity.maxUploadFileSize,
|
||||
lastVersionIdentityServerSupported = entity.lastVersionIdentityServerSupported,
|
||||
defaultIdentityServerUrl = entity.defaultIdentityServerUrl
|
||||
defaultIdentityServerUrl = entity.defaultIdentityServerUrl,
|
||||
roomVersions = entity.roomVersionJson?.let {
|
||||
tryOrNull {
|
||||
MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).fromJson(it)?.let {
|
||||
RoomVersionCapabilities(
|
||||
defaultRoomVersion = it.default ?: DefaultRoomVersionService.DEFAULT_ROOM_VERSION,
|
||||
supportedVersion = it.available.entries.map { entry ->
|
||||
RoomVersionInfo(entry.key, RoomVersionStatus.STABLE
|
||||
.takeIf { entry.value == "stable" }
|
||||
?: RoomVersionStatus.UNSTABLE)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,8 @@ internal open class HomeServerCapabilitiesEntity(
|
||||
var maxUploadFileSize: Long = HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN,
|
||||
var lastVersionIdentityServerSupported: Boolean = false,
|
||||
var defaultIdentityServerUrl: String? = null,
|
||||
var lastUpdatedTimestamp: Long = 0L
|
||||
var lastUpdatedTimestamp: Long = 0L,
|
||||
var roomVersionJson: String? = null
|
||||
) : RealmObject() {
|
||||
|
||||
companion object
|
||||
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.homeserver
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.matrix.android.sdk.api.extensions.orTrue
|
||||
import org.matrix.android.sdk.api.util.JsonDict
|
||||
|
||||
/**
|
||||
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-capabilities
|
||||
@ -38,9 +39,14 @@ internal data class Capabilities(
|
||||
* Capability to indicate if the user can change their password.
|
||||
*/
|
||||
@Json(name = "m.change_password")
|
||||
val changePassword: ChangePassword? = null
|
||||
val changePassword: ChangePassword? = null,
|
||||
|
||||
// No need for m.room_versions for the moment
|
||||
/**
|
||||
* This capability describes the default and available room versions a server supports, and at what level of stability.
|
||||
* Clients should make use of this capability to determine if users need to be encouraged to upgrade their rooms.
|
||||
*/
|
||||
@Json(name = "m.room_versions")
|
||||
val roomVersions: RoomVersions? = null
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@ -52,6 +58,18 @@ internal data class ChangePassword(
|
||||
val enabled: Boolean?
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class RoomVersions(
|
||||
/**
|
||||
* Required. True if the user can change their password, false otherwise.
|
||||
*/
|
||||
@Json(name = "default")
|
||||
val default: String?,
|
||||
|
||||
@Json(name = "available")
|
||||
val available: JsonDict
|
||||
)
|
||||
|
||||
// The spec says: If not present, the client should assume that password changes are possible via the API
|
||||
internal fun GetCapabilitiesResult.canChangePassword(): Boolean {
|
||||
return capabilities?.changePassword?.enabled.orTrue()
|
||||
|
@ -24,6 +24,7 @@ import org.matrix.android.sdk.internal.auth.version.Versions
|
||||
import org.matrix.android.sdk.internal.auth.version.isLoginAndRegistrationSupportedBySdk
|
||||
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
|
||||
import org.matrix.android.sdk.internal.database.query.getOrCreate
|
||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
@ -104,6 +105,10 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
|
||||
|
||||
if (getCapabilitiesResult != null) {
|
||||
homeServerCapabilitiesEntity.canChangePassword = getCapabilitiesResult.canChangePassword()
|
||||
|
||||
homeServerCapabilitiesEntity.roomVersionJson = getCapabilitiesResult.capabilities?.roomVersions?.let {
|
||||
MoshiProvider.providesMoshi().adapter(RoomVersions::class.java).toJson(it)
|
||||
}
|
||||
}
|
||||
|
||||
if (getMediaConfigResult != null) {
|
||||
|
@ -37,6 +37,7 @@ import org.matrix.android.sdk.api.session.room.tags.TagsService
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineService
|
||||
import org.matrix.android.sdk.api.session.room.typing.TypingService
|
||||
import org.matrix.android.sdk.api.session.room.uploads.UploadsService
|
||||
import org.matrix.android.sdk.api.session.room.version.RoomVersionService
|
||||
import org.matrix.android.sdk.api.session.search.SearchResult
|
||||
import org.matrix.android.sdk.api.session.space.Space
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
@ -69,7 +70,8 @@ internal class DefaultRoom(override val roomId: String,
|
||||
private val roomAccountDataService: RoomAccountDataService,
|
||||
private val sendStateTask: SendStateTask,
|
||||
private val viaParameterFinder: ViaParameterFinder,
|
||||
private val searchTask: SearchTask) :
|
||||
private val searchTask: SearchTask,
|
||||
private val roomVersionService: RoomVersionService) :
|
||||
Room,
|
||||
TimelineService by timelineService,
|
||||
SendService by sendService,
|
||||
@ -85,7 +87,8 @@ internal class DefaultRoom(override val roomId: String,
|
||||
RelationService by relationService,
|
||||
MembershipService by roomMembersService,
|
||||
RoomPushRuleService by roomPushRuleService,
|
||||
RoomAccountDataService by roomAccountDataService {
|
||||
RoomAccountDataService by roomAccountDataService,
|
||||
RoomVersionService by roomVersionService {
|
||||
|
||||
override fun getRoomSummaryLive(): LiveData<Optional<RoomSummary>> {
|
||||
return roomSummaryDataSource.getRoomSummaryLive(roomId)
|
||||
|
@ -369,4 +369,15 @@ internal interface RoomAPI {
|
||||
@Path("roomId") roomId: String,
|
||||
@Path("type") type: String,
|
||||
@Body content: JsonDict)
|
||||
|
||||
/**
|
||||
* Upgrades the given room to a particular room version.
|
||||
* Errors:
|
||||
* 400, The request was invalid. One way this can happen is if the room version requested is not supported by the homeserver
|
||||
* (M_UNSUPPORTED_ROOM_VERSION)
|
||||
* 403: The user is not permitted to upgrade the room.(M_FORBIDDEN)
|
||||
*/
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/upgrade")
|
||||
suspend fun upgradeRoom(@Path("roomId") roomId: String,
|
||||
@Body body: RoomUpgradeBody): RoomUpgradeResponse
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import org.matrix.android.sdk.internal.session.room.tags.DefaultTagsService
|
||||
import org.matrix.android.sdk.internal.session.room.timeline.DefaultTimelineService
|
||||
import org.matrix.android.sdk.internal.session.room.typing.DefaultTypingService
|
||||
import org.matrix.android.sdk.internal.session.room.uploads.DefaultUploadsService
|
||||
import org.matrix.android.sdk.internal.session.room.version.DefaultRoomVersionService
|
||||
import org.matrix.android.sdk.internal.session.search.SearchTask
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -61,6 +62,7 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService:
|
||||
private val relationServiceFactory: DefaultRelationService.Factory,
|
||||
private val membershipServiceFactory: DefaultMembershipService.Factory,
|
||||
private val roomPushRuleServiceFactory: DefaultRoomPushRuleService.Factory,
|
||||
private val roomVersionServiceFactory: DefaultRoomVersionService.Factory,
|
||||
private val roomAccountDataServiceFactory: DefaultRoomAccountDataService.Factory,
|
||||
private val sendStateTask: SendStateTask,
|
||||
private val viaParameterFinder: ViaParameterFinder,
|
||||
@ -89,7 +91,8 @@ internal class DefaultRoomFactory @Inject constructor(private val cryptoService:
|
||||
roomAccountDataService = roomAccountDataServiceFactory.create(roomId),
|
||||
sendStateTask = sendStateTask,
|
||||
searchTask = searchTask,
|
||||
viaParameterFinder = viaParameterFinder
|
||||
viaParameterFinder = viaParameterFinder,
|
||||
roomVersionService = roomVersionServiceFactory.create(roomId)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +92,8 @@ 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.room.version.DefaultRoomVersionUpgradeTask
|
||||
import org.matrix.android.sdk.internal.session.room.version.RoomVersionUpgradeTask
|
||||
import org.matrix.android.sdk.internal.session.space.DefaultSpaceService
|
||||
import retrofit2.Retrofit
|
||||
|
||||
@ -243,4 +245,7 @@ internal abstract class RoomModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindGetEventTask(task: DefaultGetEventTask): GetEventTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindRoomVersionUpgradeTask(task: DefaultRoomVersionUpgradeTask): RoomVersionUpgradeTask
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.room
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class RoomUpgradeBody(
|
||||
@Json(name = "new_version")
|
||||
val newVersion: String
|
||||
)
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.room
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class RoomUpgradeResponse(
|
||||
@Json(name = "replacement_room")
|
||||
val replacementRoomId: String
|
||||
)
|
@ -354,7 +354,7 @@ internal class RoomSummaryUpdater @Inject constructor(
|
||||
// we keep real m.child/m.parent relations and add the one for common memberships
|
||||
dmRoom.flattenParentIds += "|${flattenRelated.joinToString("|")}|"
|
||||
}
|
||||
// Timber.v("## SPACES: flatten of ${dmRoom.otherMemberIds.joinToString(",")} is ${dmRoom.flattenParentIds}")
|
||||
Timber.v("## SPACES: flatten of ${dmRoom.otherMemberIds.joinToString(",")} is ${dmRoom.flattenParentIds}")
|
||||
}
|
||||
|
||||
// Maybe a good place to count the number of notifications for spaces?
|
||||
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.room.version
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import io.realm.Realm
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.create.RoomCreateContent
|
||||
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||
import org.matrix.android.sdk.api.session.room.version.RoomVersionService
|
||||
import org.matrix.android.sdk.internal.database.mapper.HomeServerCapabilitiesMapper
|
||||
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
|
||||
import org.matrix.android.sdk.internal.database.query.get
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
||||
|
||||
internal class DefaultRoomVersionService @AssistedInject constructor(
|
||||
@Assisted private val roomId: String,
|
||||
@SessionDatabase private val monarchy: Monarchy,
|
||||
private val stateEventDataSource: StateEventDataSource,
|
||||
private val roomVersionUpgradeTask: RoomVersionUpgradeTask
|
||||
) : RoomVersionService {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(roomId: String): DefaultRoomVersionService
|
||||
}
|
||||
|
||||
override fun getRoomVersion(): String {
|
||||
return stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_CREATE, QueryStringValue.IsEmpty)
|
||||
?.content
|
||||
?.toModel<RoomCreateContent>()
|
||||
?.roomVersion
|
||||
// as per spec -> Defaults to "1" if the key does not exist.
|
||||
?: DEFAULT_ROOM_VERSION
|
||||
}
|
||||
|
||||
override suspend fun upgradeToVersion(version: String): String {
|
||||
return roomVersionUpgradeTask.execute(
|
||||
RoomVersionUpgradeTask.Params(
|
||||
roomId, version
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getRecommendedVersion(): String {
|
||||
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
|
||||
HomeServerCapabilitiesEntity.get(realm)?.let {
|
||||
HomeServerCapabilitiesMapper.map(it)
|
||||
}?.roomVersions?.defaultRoomVersion ?: DEFAULT_ROOM_VERSION
|
||||
}
|
||||
}
|
||||
|
||||
override fun userMayUpgradeRoom(userId: String): Boolean {
|
||||
val powerLevelsHelper = stateEventDataSource.getStateEvent(roomId, EventType.STATE_ROOM_POWER_LEVELS, QueryStringValue.NoCondition)
|
||||
?.content?.toModel<PowerLevelsContent>()
|
||||
?.let { PowerLevelsHelper(it) }
|
||||
|
||||
return powerLevelsHelper?.isUserAllowedToSend(userId, true, EventType.STATE_ROOM_TOMBSTONE) ?: false
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_ROOM_VERSION = "1"
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.room.version
|
||||
|
||||
import io.realm.RealmConfiguration
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
|
||||
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
|
||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||
import org.matrix.android.sdk.internal.network.executeRequest
|
||||
import org.matrix.android.sdk.internal.session.room.RoomAPI
|
||||
import org.matrix.android.sdk.internal.session.room.RoomUpgradeBody
|
||||
import org.matrix.android.sdk.internal.task.Task
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface RoomVersionUpgradeTask : Task<RoomVersionUpgradeTask.Params, String> {
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val newVersion: String
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultRoomVersionUpgradeTask @Inject constructor(
|
||||
private val roomAPI: RoomAPI,
|
||||
private val globalErrorReceiver: GlobalErrorReceiver,
|
||||
@SessionDatabase
|
||||
private val realmConfiguration: RealmConfiguration
|
||||
) : RoomVersionUpgradeTask {
|
||||
|
||||
override suspend fun execute(params: RoomVersionUpgradeTask.Params): String {
|
||||
val replacementRoomId = executeRequest(globalErrorReceiver) {
|
||||
roomAPI.upgradeRoom(
|
||||
roomId = params.roomId,
|
||||
body = RoomUpgradeBody(params.newVersion)
|
||||
)
|
||||
}.replacementRoomId
|
||||
|
||||
// Wait for room to come back from the sync (but it can maybe be in the DB if the sync response is received before)
|
||||
tryOrNull {
|
||||
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
|
||||
realm.where(RoomSummaryEntity::class.java)
|
||||
.equalTo(RoomSummaryEntityFields.ROOM_ID, replacementRoomId)
|
||||
.equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)
|
||||
}
|
||||
}
|
||||
return replacementRoomId
|
||||
}
|
||||
}
|
@ -86,6 +86,12 @@ internal class DefaultSpace(
|
||||
)
|
||||
}
|
||||
|
||||
override fun getChildInfo(roomId: String): SpaceChildContent? {
|
||||
return room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
.firstOrNull()
|
||||
?.content.toModel<SpaceChildContent>()
|
||||
}
|
||||
|
||||
override suspend fun setChildrenOrder(roomId: String, order: String?) {
|
||||
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||
.firstOrNull()
|
||||
|
@ -159,7 +159,6 @@ class VectorApplication :
|
||||
// Do not display the name change popup
|
||||
doNotShowDisclaimerDialog(this)
|
||||
}
|
||||
|
||||
if (authenticationService.hasAuthenticatedSessions() && !activeSessionHolder.hasActiveSession()) {
|
||||
val lastAuthenticatedSession = authenticationService.getLastAuthenticatedSession()!!
|
||||
activeSessionHolder.setActiveSession(lastAuthenticatedSession)
|
||||
|
@ -40,12 +40,14 @@ import im.vector.app.features.debug.DebugMenuActivity
|
||||
import im.vector.app.features.devtools.RoomDevToolActivity
|
||||
import im.vector.app.features.home.HomeActivity
|
||||
import im.vector.app.features.home.HomeModule
|
||||
import im.vector.app.features.home.room.detail.JoinReplacementRoomBottomSheet
|
||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||
import im.vector.app.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
|
||||
import im.vector.app.features.home.room.detail.search.SearchActivity
|
||||
import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet
|
||||
import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
|
||||
import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet
|
||||
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
|
||||
import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
|
||||
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
|
||||
import im.vector.app.features.home.room.list.RoomListModule
|
||||
@ -191,6 +193,8 @@ interface ScreenComponent {
|
||||
fun inject(bottomSheet: SpaceSettingsMenuBottomSheet)
|
||||
fun inject(bottomSheet: InviteRoomSpaceChooserBottomSheet)
|
||||
fun inject(bottomSheet: SpaceInviteBottomSheet)
|
||||
fun inject(bottomSheet: JoinReplacementRoomBottomSheet)
|
||||
fun inject(bottomSheet: MigrateRoomBottomSheet)
|
||||
|
||||
/* ==========================================================================================
|
||||
* Others
|
||||
|
@ -46,7 +46,7 @@ import java.util.concurrent.TimeUnit
|
||||
/**
|
||||
* Add MvRx capabilities to bottomsheetdialog (like BaseMvRxFragment)
|
||||
*/
|
||||
abstract class VectorBaseBottomSheetDialogFragment<VB: ViewBinding> : BottomSheetDialogFragment(), MvRxView {
|
||||
abstract class VectorBaseBottomSheetDialogFragment<VB : ViewBinding> : BottomSheetDialogFragment(), MvRxView {
|
||||
|
||||
private val mvrxViewIdProperty = MvRxViewId()
|
||||
final override val mvrxViewId: String by mvrxViewIdProperty
|
||||
@ -168,6 +168,10 @@ abstract class VectorBaseBottomSheetDialogFragment<VB: ViewBinding> : BottomShee
|
||||
|
||||
@CallSuper
|
||||
override fun invalidate() {
|
||||
forceExpandState()
|
||||
}
|
||||
|
||||
protected fun forceExpandState() {
|
||||
if (showExpanded) {
|
||||
// Force the bottom sheet to be expanded
|
||||
bottomSheetBehavior?.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2019 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.core.ui.list
|
||||
|
||||
import android.widget.ProgressBar
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
|
||||
/**
|
||||
* A generic list item.
|
||||
* Displays an item with a title, and optional description.
|
||||
* Can display an accessory on the right, that can be an image or an indeterminate progress.
|
||||
* If provided with an action, will display a button at the bottom of the list item.
|
||||
*/
|
||||
@EpoxyModelClass(layout = R.layout.item_generic_progress)
|
||||
abstract class GenericProgressBarItem : VectorEpoxyModel<GenericProgressBarItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute
|
||||
var progress: Int = 0
|
||||
|
||||
@EpoxyAttribute
|
||||
var total: Int = 100
|
||||
|
||||
@EpoxyAttribute
|
||||
var indeterminate: Boolean = false
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.progressbar.progress = progress
|
||||
holder.progressbar.max = total
|
||||
holder.progressbar.isIndeterminate = indeterminate
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val progressbar by bind<ProgressBar>(R.id.genericProgressBar)
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ import android.graphics.Color
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.italic
|
||||
import im.vector.app.R
|
||||
@ -44,7 +44,7 @@ class NotificationAreaView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : RelativeLayout(context, attrs, defStyleAttr) {
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
var delegate: Delegate? = null
|
||||
private var state: State = State.Initial
|
||||
@ -127,7 +127,7 @@ class NotificationAreaView @JvmOverloads constructor(
|
||||
|
||||
private fun renderTombstone(state: State.Tombstone) {
|
||||
visibility = View.VISIBLE
|
||||
views.roomNotificationIcon.setImageResource(R.drawable.error)
|
||||
views.roomNotificationIcon.setImageResource(R.drawable.ic_warning_badge)
|
||||
val message = span {
|
||||
+resources.getString(R.string.room_tombstone_versioned_description)
|
||||
+"\n"
|
||||
|
@ -50,7 +50,8 @@ enum class Command(val command: String, val parameters: String, @StringRes val d
|
||||
CREATE_SPACE("/createspace", "<name> <invitee>*", R.string.command_description_create_space, true),
|
||||
ADD_TO_SPACE("/addToSpace", "spaceId", R.string.command_description_create_space, true),
|
||||
JOIN_SPACE("/joinSpace", "spaceId", R.string.command_description_join_space, true),
|
||||
LEAVE_ROOM("/leave", "<roomId?>", R.string.command_description_leave_room, true);
|
||||
LEAVE_ROOM("/leave", "<roomId?>", R.string.command_description_leave_room, true),
|
||||
UPGRADE_ROOM("/upgraderoom", "newVersion", R.string.command_description_upgrade_room, true);
|
||||
|
||||
val length
|
||||
get() = command.length + 1
|
||||
|
@ -312,24 +312,28 @@ object CommandParser {
|
||||
)
|
||||
}
|
||||
}
|
||||
Command.ADD_TO_SPACE.command -> {
|
||||
Command.ADD_TO_SPACE.command -> {
|
||||
val rawCommand = textMessage.substring(Command.ADD_TO_SPACE.command.length).trim()
|
||||
ParsedCommand.AddToSpace(
|
||||
rawCommand
|
||||
)
|
||||
}
|
||||
Command.JOIN_SPACE.command -> {
|
||||
Command.JOIN_SPACE.command -> {
|
||||
val spaceIdOrAlias = textMessage.substring(Command.JOIN_SPACE.command.length).trim()
|
||||
ParsedCommand.JoinSpace(
|
||||
spaceIdOrAlias
|
||||
)
|
||||
}
|
||||
Command.LEAVE_ROOM.command -> {
|
||||
Command.LEAVE_ROOM.command -> {
|
||||
val spaceIdOrAlias = textMessage.substring(Command.LEAVE_ROOM.command.length).trim()
|
||||
ParsedCommand.LeaveRoom(
|
||||
spaceIdOrAlias
|
||||
)
|
||||
}
|
||||
Command.UPGRADE_ROOM.command -> {
|
||||
val newVersion = textMessage.substring(Command.UPGRADE_ROOM.command.length).trim()
|
||||
ParsedCommand.UpgradeRoom(newVersion)
|
||||
}
|
||||
else -> {
|
||||
// Unknown command
|
||||
ParsedCommand.ErrorUnknownSlashCommand(slashCommand)
|
||||
|
@ -61,4 +61,5 @@ sealed class ParsedCommand {
|
||||
class AddToSpace(val spaceId: String) : ParsedCommand()
|
||||
class JoinSpace(val spaceIdOrAlias: String) : ParsedCommand()
|
||||
class LeaveRoom(val roomId: String) : ParsedCommand()
|
||||
class UpgradeRoom(val newVersion: String) : ParsedCommand()
|
||||
}
|
||||
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.home.room.detail
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.parentFragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ScreenComponent
|
||||
import im.vector.app.core.epoxy.ClickListener
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.platform.ButtonStateView
|
||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||
import im.vector.app.databinding.BottomSheetTombstoneJoinBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class JoinReplacementRoomBottomSheet :
|
||||
VectorBaseBottomSheetDialogFragment<BottomSheetTombstoneJoinBinding>() {
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
|
||||
BottomSheetTombstoneJoinBinding.inflate(inflater, container, false)
|
||||
|
||||
@Inject
|
||||
lateinit var errorFormatter: ErrorFormatter
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
private val viewModel: RoomDetailViewModel by parentFragmentViewModel()
|
||||
|
||||
override val showExpanded: Boolean
|
||||
get() = true
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
views.roomUpgradeButton.retryClicked = object : ClickListener {
|
||||
override fun invoke(view: View) {
|
||||
withState(viewModel) { it.tombstoneEvent }?.let {
|
||||
viewModel.handle(RoomDetailAction.HandleTombstoneEvent(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.selectSubscribe(this, RoomDetailViewState::tombstoneEventHandling) { joinState ->
|
||||
when (joinState) {
|
||||
// it should never be Uninitialized
|
||||
Uninitialized -> views.roomUpgradeButton.render(ButtonStateView.State.Loaded)
|
||||
is Loading -> {
|
||||
views.roomUpgradeButton.render(ButtonStateView.State.Loading)
|
||||
views.descriptionText.setText(R.string.it_may_take_some_time)
|
||||
}
|
||||
is Success -> {
|
||||
views.roomUpgradeButton.render(ButtonStateView.State.Loaded)
|
||||
dismiss()
|
||||
}
|
||||
is Fail -> {
|
||||
// display the error message
|
||||
views.descriptionText.text = errorFormatter.toHumanReadable(joinState.error)
|
||||
views.roomUpgradeButton.render(ButtonStateView.State.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -108,4 +108,5 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
||||
|
||||
// Failed messages
|
||||
object RemoveAllFailedMessages : RoomDetailAction()
|
||||
data class RoomUpgradeSuccess(val replacementRoom: String): RoomDetailAction()
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.setFragmentResultListener
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -147,6 +148,7 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageTextItem
|
||||
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
|
||||
import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet
|
||||
import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
|
||||
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
|
||||
import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
|
||||
import im.vector.app.features.html.EventHtmlRenderer
|
||||
import im.vector.app.features.html.PillImageSpan
|
||||
@ -306,6 +308,15 @@ class RoomDetailFragment @Inject constructor(
|
||||
|
||||
private lateinit var emojiPopup: EmojiPopup
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setFragmentResultListener(MigrateRoomBottomSheet.REQUEST_KEY) { _, bundle ->
|
||||
bundle.getString(MigrateRoomBottomSheet.BUNDLE_KEY_REPLACEMENT_ROOM)?.let { replacementRoomId ->
|
||||
roomDetailViewModel.handle(RoomDetailAction.RoomUpgradeSuccess(replacementRoomId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
|
||||
@ -405,6 +416,8 @@ class RoomDetailFragment @Inject constructor(
|
||||
is RoomDetailViewEvents.StartChatEffect -> handleChatEffect(it.type)
|
||||
RoomDetailViewEvents.StopChatEffects -> handleStopChatEffects()
|
||||
is RoomDetailViewEvents.DisplayAndAcceptCall -> acceptIncomingCall(it)
|
||||
RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement()
|
||||
is RoomDetailViewEvents.ShowRoomUpgradeDialog -> handleShowRoomUpgradeDialog(it)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
@ -423,6 +436,19 @@ class RoomDetailFragment @Inject constructor(
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun handleRoomReplacement() {
|
||||
// this will join a new room, it can take time and might fail
|
||||
// so we need to report progress and retry
|
||||
val tag = JoinReplacementRoomBottomSheet::javaClass.name
|
||||
JoinReplacementRoomBottomSheet().show(childFragmentManager, tag)
|
||||
}
|
||||
|
||||
private fun handleShowRoomUpgradeDialog(roomDetailViewEvents: RoomDetailViewEvents.ShowRoomUpgradeDialog) {
|
||||
val tag = MigrateRoomBottomSheet::javaClass.name
|
||||
MigrateRoomBottomSheet.newInstance(roomDetailArgs.roomId, roomDetailViewEvents.newVersion)
|
||||
.show(parentFragmentManager, tag)
|
||||
}
|
||||
|
||||
private fun handleChatEffect(chatEffect: ChatEffect) {
|
||||
when (chatEffect) {
|
||||
ChatEffect.CONFETTI -> {
|
||||
@ -1306,16 +1332,14 @@ class RoomDetailFragment @Inject constructor(
|
||||
private fun renderTombstoneEventHandling(async: Async<String>) {
|
||||
when (async) {
|
||||
is Loading -> {
|
||||
// TODO Better handling progress
|
||||
vectorBaseActivity.showWaitingView(getString(R.string.joining_room))
|
||||
// shown in bottom sheet
|
||||
}
|
||||
is Success -> {
|
||||
navigator.openRoom(vectorBaseActivity, async())
|
||||
vectorBaseActivity.finish()
|
||||
}
|
||||
is Fail -> {
|
||||
vectorBaseActivity.hideWaitingView()
|
||||
vectorBaseActivity.toast(errorFormatter.toHumanReadable(async.error))
|
||||
// shown in bottom sheet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,4 +94,6 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
|
||||
|
||||
data class StartChatEffect(val type: ChatEffect) : RoomDetailViewEvents()
|
||||
object StopChatEffects : RoomDetailViewEvents()
|
||||
object RoomReplacementStarted : RoomDetailViewEvents()
|
||||
data class ShowRoomUpgradeDialog(val newVersion: String, val isPublic: Boolean): RoomDetailViewEvents()
|
||||
}
|
||||
|
@ -321,6 +321,11 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
is RoomDetailAction.DoNotShowPreviewUrlFor -> handleDoNotShowPreviewUrlFor(action)
|
||||
RoomDetailAction.RemoveAllFailedMessages -> handleRemoveAllFailedMessages()
|
||||
RoomDetailAction.ResendAll -> handleResendAll()
|
||||
is RoomDetailAction.RoomUpgradeSuccess -> {
|
||||
setState {
|
||||
copy(tombstoneEventHandling = Success(action.replacementRoom))
|
||||
}
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
@ -585,6 +590,11 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
val viaServers = MatrixPatterns.extractServerNameFromId(action.event.senderId)
|
||||
?.let { listOf(it) }
|
||||
.orEmpty()
|
||||
// need to provide feedback as joining could take some time
|
||||
_viewEvents.post(RoomDetailViewEvents.RoomReplacementStarted)
|
||||
setState {
|
||||
copy(tombstoneEventHandling = Loading())
|
||||
}
|
||||
viewModelScope.launch {
|
||||
val result = runCatchingToAsync {
|
||||
session.joinRoom(roomId, viaServers = viaServers)
|
||||
@ -817,6 +827,23 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.UpgradeRoom -> {
|
||||
_viewEvents.post(
|
||||
RoomDetailViewEvents.ShowRoomUpgradeDialog(
|
||||
slashCommandResult.newVersion,
|
||||
room.roomSummary()?.isPublic ?: false
|
||||
)
|
||||
)
|
||||
// session.coroutineScope.launch {
|
||||
// try {
|
||||
// room.upgradeToVersion(slashCommandResult.newVersion)
|
||||
// } catch (failure: Throwable) {
|
||||
// _viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure))
|
||||
// }
|
||||
// }
|
||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
is SendMode.EDIT -> {
|
||||
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.home.room.detail.upgrade
|
||||
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class MigrateRoomAction : VectorViewModelAction {
|
||||
data class SetAutoInvite(val autoInvite: Boolean) : MigrateRoomAction()
|
||||
data class SetUpdateKnownParentSpace(val update: Boolean) : MigrateRoomAction()
|
||||
object UpgradeRoom : MigrateRoomAction()
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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.home.room.detail.upgrade
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import com.airbnb.epoxy.OnModelBuildFinishedListener
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.core.di.ScreenComponent
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
||||
import im.vector.app.databinding.BottomSheetGenericListBinding
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import javax.inject.Inject
|
||||
|
||||
class MigrateRoomBottomSheet :
|
||||
VectorBaseBottomSheetDialogFragment<BottomSheetGenericListBinding>(),
|
||||
MigrateRoomViewModel.Factory, MigrateRoomController.InteractionListener {
|
||||
|
||||
@Parcelize
|
||||
data class Args(
|
||||
val roomId: String,
|
||||
val newVersion: String
|
||||
) : Parcelable
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: MigrateRoomViewModel.Factory
|
||||
|
||||
override val showExpanded = true
|
||||
|
||||
@Inject
|
||||
lateinit var epoxyController: MigrateRoomController
|
||||
|
||||
val viewModel: MigrateRoomViewModel by fragmentViewModel()
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
|
||||
BottomSheetGenericListBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
epoxyController.setData(state)
|
||||
super.invalidate()
|
||||
|
||||
when (val result = state.upgradingStatus) {
|
||||
is Success -> {
|
||||
val result = result.invoke()
|
||||
if (result is UpgradeRoomViewModelTask.Result.Success) {
|
||||
setFragmentResult(REQUEST_KEY, Bundle().apply {
|
||||
putString(BUNDLE_KEY_REPLACEMENT_ROOM, result.replacementRoomId)
|
||||
})
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val postBuild = OnModelBuildFinishedListener {
|
||||
view?.post { forceExpandState() }
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
epoxyController.callback = this
|
||||
views.bottomSheetRecyclerView.configureWith(epoxyController)
|
||||
epoxyController.addModelBuildListener(postBuild)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
views.bottomSheetRecyclerView.cleanup()
|
||||
epoxyController.removeModelBuildListener(postBuild)
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun create(initialState: MigrateRoomViewState): MigrateRoomViewModel {
|
||||
return viewModelFactory.create(initialState)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val REQUEST_KEY = "MigrateRoomBottomSheetRequest"
|
||||
const val BUNDLE_KEY_REPLACEMENT_ROOM = "BUNDLE_KEY_REPLACEMENT_ROOM"
|
||||
|
||||
fun newInstance(roomId: String, newVersion: String)
|
||||
: MigrateRoomBottomSheet {
|
||||
return MigrateRoomBottomSheet().apply {
|
||||
setArguments(Args(roomId, newVersion))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAutoInvite(autoInvite: Boolean) {
|
||||
viewModel.handle(MigrateRoomAction.SetAutoInvite(autoInvite))
|
||||
}
|
||||
|
||||
override fun onAutoUpdateParent(update: Boolean) {
|
||||
viewModel.handle(MigrateRoomAction.SetUpdateKnownParentSpace(update))
|
||||
}
|
||||
|
||||
override fun onConfirmUpgrade() {
|
||||
viewModel.handle(MigrateRoomAction.UpgradeRoom)
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.home.room.detail.upgrade
|
||||
|
||||
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.errorWithRetryItem
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.ui.bottomsheet.bottomSheetTitleItem
|
||||
import im.vector.app.core.ui.list.ItemStyle
|
||||
import im.vector.app.core.ui.list.genericFooterItem
|
||||
import im.vector.app.core.ui.list.genericProgressBarItem
|
||||
import im.vector.app.features.form.formSubmitButtonItem
|
||||
import im.vector.app.features.form.formSwitchItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class MigrateRoomController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val errorFormatter: ErrorFormatter
|
||||
) : TypedEpoxyController<MigrateRoomViewState>() {
|
||||
|
||||
interface InteractionListener {
|
||||
fun onAutoInvite(autoInvite: Boolean)
|
||||
fun onAutoUpdateParent(update: Boolean)
|
||||
fun onConfirmUpgrade()
|
||||
}
|
||||
|
||||
var callback: InteractionListener? = null
|
||||
|
||||
override fun buildModels(data: MigrateRoomViewState?) {
|
||||
data ?: return
|
||||
|
||||
val host = this@MigrateRoomController
|
||||
|
||||
bottomSheetTitleItem {
|
||||
id("title")
|
||||
title(
|
||||
host.stringProvider.getString(if (data.isPublic) R.string.upgrade_public_room else R.string.upgrade_private_room)
|
||||
)
|
||||
}
|
||||
|
||||
genericFooterItem {
|
||||
id("warning_text")
|
||||
centered(false)
|
||||
style(ItemStyle.NORMAL_TEXT)
|
||||
text(host.stringProvider.getString(R.string.upgrade_room_warning))
|
||||
}
|
||||
|
||||
genericFooterItem {
|
||||
id("from_to_room")
|
||||
centered(false)
|
||||
style(ItemStyle.NORMAL_TEXT)
|
||||
text(host.stringProvider.getString(R.string.upgrade_public_room_from_to, data.currentVersion, data.newVersion))
|
||||
}
|
||||
|
||||
if (!data.isPublic && data.otherMemberCount > 0) {
|
||||
formSwitchItem {
|
||||
id("auto_invite")
|
||||
switchChecked(data.shouldIssueInvites)
|
||||
title(host.stringProvider.getString(R.string.upgrade_room_auto_invite))
|
||||
listener { switch -> host.callback?.onAutoInvite(switch) }
|
||||
}
|
||||
}
|
||||
|
||||
if (data.knownParents.isNotEmpty()) {
|
||||
formSwitchItem {
|
||||
id("update_parent")
|
||||
switchChecked(data.shouldUpdateKnownParents)
|
||||
title(host.stringProvider.getString(R.string.upgrade_room_update_parent))
|
||||
listener { switch -> host.callback?.onAutoUpdateParent(switch) }
|
||||
}
|
||||
}
|
||||
when (data.upgradingStatus) {
|
||||
is Loading -> {
|
||||
genericProgressBarItem {
|
||||
id("upgrade_progress")
|
||||
indeterminate(data.upgradingProgressIndeterminate)
|
||||
progress(data.upgradingProgress)
|
||||
total(data.upgradingProgressTotal)
|
||||
}
|
||||
}
|
||||
is Success -> {
|
||||
when (val result = data.upgradingStatus.invoke()) {
|
||||
is UpgradeRoomViewModelTask.Result.Failure -> {
|
||||
val errorText = when (result) {
|
||||
is UpgradeRoomViewModelTask.Result.UnknownRoom -> {
|
||||
// should not happen
|
||||
host.stringProvider.getString(R.string.unknown_error)
|
||||
}
|
||||
is UpgradeRoomViewModelTask.Result.NotAllowed -> {
|
||||
host.stringProvider.getString(R.string.upgrade_room_no_power_to_manage)
|
||||
}
|
||||
is UpgradeRoomViewModelTask.Result.ErrorFailure -> {
|
||||
host.errorFormatter.toHumanReadable(result.throwable)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
errorWithRetryItem {
|
||||
id("error")
|
||||
text(errorText)
|
||||
listener { host.callback?.onConfirmUpgrade() }
|
||||
}
|
||||
}
|
||||
is UpgradeRoomViewModelTask.Result.Success -> {
|
||||
// nop, dismisses
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
formSubmitButtonItem {
|
||||
id("migrate")
|
||||
buttonTitleId(R.string.upgrade)
|
||||
buttonClickListener { host.callback?.onConfirmUpgrade() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 im.vector.app.features.home.room.detail.upgrade
|
||||
|
||||
import com.airbnb.mvrx.ActivityViewModelContext
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.session.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
||||
class MigrateRoomViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: MigrateRoomViewState,
|
||||
private val session: Session,
|
||||
private val upgradeRoomViewModelTask: UpgradeRoomViewModelTask)
|
||||
: VectorViewModel<MigrateRoomViewState, MigrateRoomAction, EmptyViewEvents>(initialState) {
|
||||
|
||||
init {
|
||||
val room = session.getRoom(initialState.roomId)
|
||||
val summary = session.getRoomSummary(initialState.roomId)
|
||||
setState {
|
||||
copy(
|
||||
currentVersion = room?.getRoomVersion(),
|
||||
isPublic = summary?.isPublic ?: false,
|
||||
otherMemberCount = summary?.otherMemberIds?.count() ?: 0,
|
||||
knownParents = summary?.flattenParentIds ?: emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(initialState: MigrateRoomViewState): MigrateRoomViewModel
|
||||
}
|
||||
|
||||
companion object : MvRxViewModelFactory<MigrateRoomViewModel, MigrateRoomViewState> {
|
||||
|
||||
override fun create(viewModelContext: ViewModelContext, state: MigrateRoomViewState): MigrateRoomViewModel? {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: MigrateRoomAction) {
|
||||
when (action) {
|
||||
is MigrateRoomAction.SetAutoInvite -> {
|
||||
setState {
|
||||
copy(shouldIssueInvites = action.autoInvite)
|
||||
}
|
||||
}
|
||||
is MigrateRoomAction.SetUpdateKnownParentSpace -> {
|
||||
setState {
|
||||
copy(shouldUpdateKnownParents = action.update)
|
||||
}
|
||||
}
|
||||
MigrateRoomAction.UpgradeRoom -> {
|
||||
handleUpgradeRoom(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val upgradingProgress: ((indeterminate: Boolean, progress: Int, total: Int) -> Unit) = { indeterminate, progress, total ->
|
||||
setState {
|
||||
copy(
|
||||
upgradingProgress = progress,
|
||||
upgradingProgressTotal = total,
|
||||
upgradingProgressIndeterminate = indeterminate
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUpgradeRoom(action: MigrateRoomAction) = withState { state ->
|
||||
val summary = session.getRoomSummary(state.roomId)
|
||||
setState {
|
||||
copy(upgradingStatus = Loading())
|
||||
}
|
||||
session.coroutineScope.launch {
|
||||
val result = upgradeRoomViewModelTask.execute(UpgradeRoomViewModelTask.Params(
|
||||
roomId = state.roomId,
|
||||
newVersion = state.newVersion,
|
||||
userIdsToAutoInvite = summary?.otherMemberIds?.takeIf { state.shouldIssueInvites } ?: emptyList(),
|
||||
parentSpaceToUpdate = summary?.flattenParentIds?.takeIf { state.shouldUpdateKnownParents } ?: emptyList(),
|
||||
progressReporter = upgradingProgress
|
||||
))
|
||||
|
||||
setState {
|
||||
copy(upgradingStatus = Success(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.home.room.detail.upgrade
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
|
||||
data class MigrateRoomViewState(
|
||||
val roomId: String,
|
||||
val newVersion: String,
|
||||
val currentVersion: String? = null,
|
||||
val isPublic: Boolean = false,
|
||||
val shouldIssueInvites: Boolean = false,
|
||||
val shouldUpdateKnownParents: Boolean = false,
|
||||
val otherMemberCount: Int = 0,
|
||||
val knownParents: List<String> = emptyList(),
|
||||
val upgradingStatus: Async<UpgradeRoomViewModelTask.Result> = Uninitialized,
|
||||
val upgradingProgress: Int = 0,
|
||||
val upgradingProgressTotal: Int = 0,
|
||||
val upgradingProgressIndeterminate: Boolean = true
|
||||
) : MvRxState {
|
||||
constructor(args: MigrateRoomBottomSheet.Args) : this(
|
||||
roomId = args.roomId,
|
||||
newVersion = args.newVersion
|
||||
)
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.home.room.detail.upgrade
|
||||
|
||||
import im.vector.app.core.platform.ViewModelTask
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class UpgradeRoomViewModelTask @Inject constructor(
|
||||
val session: Session,
|
||||
val stringProvider: StringProvider
|
||||
) : ViewModelTask<UpgradeRoomViewModelTask.Params, UpgradeRoomViewModelTask.Result> {
|
||||
|
||||
sealed class Result {
|
||||
data class Success(val replacementRoomId: String) : Result()
|
||||
abstract class Failure(val throwable: Throwable?) : Result()
|
||||
object UnknownRoom : Failure(null)
|
||||
object NotAllowed : Failure(null)
|
||||
class ErrorFailure(throwable: Throwable) : Failure(throwable)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val roomId: String,
|
||||
val newVersion: String,
|
||||
val userIdsToAutoInvite: List<String> = emptyList(),
|
||||
val parentSpaceToUpdate: List<String> = emptyList(),
|
||||
val progressReporter: ((indeterminate: Boolean, progress: Int, total: Int) -> Unit)? = null
|
||||
)
|
||||
|
||||
override suspend fun execute(params: Params): Result {
|
||||
params.progressReporter?.invoke(true, 0, 0)
|
||||
|
||||
val room = session.getRoom(params.roomId)
|
||||
?: return Result.UnknownRoom
|
||||
if (!room.userMayUpgradeRoom(session.myUserId)) {
|
||||
return Result.NotAllowed
|
||||
}
|
||||
|
||||
val updatedRoomId = try {
|
||||
room.upgradeToVersion(params.newVersion)
|
||||
} catch (failure: Throwable) {
|
||||
return Result.ErrorFailure(failure)
|
||||
}
|
||||
|
||||
val totalStep = params.userIdsToAutoInvite.size + params.parentSpaceToUpdate.size
|
||||
var currentStep = 0
|
||||
params.userIdsToAutoInvite.forEach {
|
||||
params.progressReporter?.invoke(false, currentStep, totalStep)
|
||||
tryOrNull {
|
||||
session.getRoom(updatedRoomId)?.invite(it)
|
||||
}
|
||||
currentStep++
|
||||
}
|
||||
|
||||
params.parentSpaceToUpdate.forEach { parentId ->
|
||||
params.progressReporter?.invoke(false, currentStep, totalStep)
|
||||
// we try and silently fail
|
||||
try {
|
||||
session.getRoom(parentId)?.asSpace()?.let { parentSpace ->
|
||||
val currentInfo = parentSpace.getChildInfo(params.roomId)
|
||||
if (currentInfo != null) {
|
||||
parentSpace.addChildren(
|
||||
roomId = updatedRoomId,
|
||||
viaServers = currentInfo.via,
|
||||
order = currentInfo.order,
|
||||
autoJoin = currentInfo.autoJoin ?: false,
|
||||
suggested = currentInfo.suggested
|
||||
)
|
||||
|
||||
parentSpace.removeChildren(params.roomId)
|
||||
}
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
Timber.d("## Migrate: Failed to update space parent. cause: ${failure.localizedMessage}")
|
||||
} finally {
|
||||
currentStep++
|
||||
}
|
||||
}
|
||||
|
||||
return Result.Success(updatedRoomId)
|
||||
}
|
||||
}
|
@ -26,16 +26,20 @@ import im.vector.app.core.epoxy.errorWithRetryItem
|
||||
import im.vector.app.core.epoxy.loadingItem
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.ui.list.genericWithValueItem
|
||||
import im.vector.app.features.discovery.settingsCenteredImageItem
|
||||
import im.vector.app.features.discovery.settingsInfoItem
|
||||
import im.vector.app.features.discovery.settingsSectionTitleItem
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import org.matrix.android.sdk.api.federation.FederationVersion
|
||||
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
|
||||
import org.matrix.android.sdk.api.session.homeserver.RoomVersionStatus
|
||||
import javax.inject.Inject
|
||||
|
||||
class HomeserverSettingsController @Inject constructor(
|
||||
private val stringProvider: StringProvider,
|
||||
private val errorFormatter: ErrorFormatter
|
||||
private val errorFormatter: ErrorFormatter,
|
||||
private val vectorPreferences: VectorPreferences
|
||||
) : TypedEpoxyController<HomeServerSettingsViewState>() {
|
||||
|
||||
var callback: Callback? = null
|
||||
@ -118,5 +122,36 @@ class HomeserverSettingsController @Inject constructor(
|
||||
helperText(host.stringProvider.getString(R.string.settings_server_upload_size_content, "${limit / 1048576L} MB"))
|
||||
}
|
||||
}
|
||||
|
||||
if (vectorPreferences.developerMode()) {
|
||||
val roomCapabilities = data.homeServerCapabilities.roomVersions
|
||||
if (roomCapabilities != null) {
|
||||
settingsSectionTitleItem {
|
||||
id("room_versions")
|
||||
titleResId(R.string.settings_server_room_versions)
|
||||
}
|
||||
|
||||
genericWithValueItem {
|
||||
id("room_version_default")
|
||||
title(host.stringProvider.getString(R.string.settings_server_default_room_version))
|
||||
value(roomCapabilities.defaultRoomVersion)
|
||||
}
|
||||
|
||||
roomCapabilities.supportedVersion.forEach {
|
||||
genericWithValueItem {
|
||||
id("room_version_${it.version}")
|
||||
title(it.version)
|
||||
value(
|
||||
host.stringProvider.getString(
|
||||
when (it.status) {
|
||||
RoomVersionStatus.STABLE -> R.string.settings_server_room_version_stable
|
||||
RoomVersionStatus.UNSTABLE -> R.string.settings_server_room_version_unstable
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
48
vector/src/main/res/layout/bottom_sheet_tombstone_join.xml
Normal file
48
vector/src/main/res/layout/bottom_sheet_tombstone_join.xml
Normal file
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorSurface"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headerText"
|
||||
style="@style/Widget.Vector.TextView.Subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/joining_replacement_room"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/descriptionText"
|
||||
style="@style/Widget.Vector.TextView.Subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:gravity="center"
|
||||
android:textColor="?vctr_content_secondary"
|
||||
app:layout_constraintBottom_toTopOf="@id/joinInfoHelpText"
|
||||
app:layout_constraintTop_toBottomOf="@id/headerText"
|
||||
app:layout_constraintVertical_bias="1"
|
||||
tools:text="@string/it_may_take_some_time" />
|
||||
|
||||
<im.vector.app.core.platform.ButtonStateView
|
||||
android:id="@+id/roomUpgradeButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="@dimen/layout_vertical_margin_big"
|
||||
app:bsv_button_text="@string/join"
|
||||
app:bsv_loaded_image_src="@drawable/ic_tick"
|
||||
app:bsv_use_flat_button="true" />
|
||||
|
||||
</LinearLayout>
|
6
vector/src/main/res/layout/item_generic_progress.xml
Normal file
6
vector/src/main/res/layout/item_generic_progress.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/genericProgressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp" />
|
@ -10,7 +10,7 @@
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="?vctr_keys_backup_banner_accent_color"
|
||||
@ -18,7 +18,7 @@
|
||||
android:gravity="center|start"
|
||||
android:minHeight="80dp"
|
||||
android:padding="16dp"
|
||||
app:drawableStartCompat="@drawable/error"
|
||||
app:drawableStartCompat="@drawable/ic_warning_badge"
|
||||
tools:text="This room is continuation…" />
|
||||
|
||||
</FrameLayout>
|
@ -1,38 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?vctr_keys_backup_banner_accent_color"
|
||||
android:minHeight="48dp"
|
||||
tools:parentTag="android.widget.RelativeLayout">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?vctr_list_separator" />
|
||||
android:orientation="horizontal"
|
||||
android:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/roomNotificationIcon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_gravity="top"
|
||||
android:importantForAccessibility="no"
|
||||
android:padding="5dp"
|
||||
tools:src="@drawable/error" />
|
||||
tools:src="@drawable/ic_warning_badge" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomNotificationMessage"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="64dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:accessibilityLiveRegion="polite"
|
||||
android:gravity="center"
|
||||
android:gravity="start"
|
||||
android:textColor="?vctr_content_primary"
|
||||
tools:text="@string/room_do_not_have_permission_to_post" />
|
||||
|
||||
</merge>
|
||||
</LinearLayout>
|
@ -926,7 +926,7 @@
|
||||
<string name="room_resend_unsent_messages">Resend unsent messages</string>
|
||||
<string name="room_delete_unsent_messages">Delete unsent messages</string>
|
||||
<string name="room_message_file_not_found">File not found</string>
|
||||
<string name="room_do_not_have_permission_to_post">You do not have permission to post to this room</string>
|
||||
<string name="room_do_not_have_permission_to_post">You do not have permission to post to this room.</string>
|
||||
<plurals name="room_new_messages_notification">
|
||||
<item quantity="one">%d new message</item>
|
||||
<item quantity="other">%d new messages</item>
|
||||
@ -1820,7 +1820,7 @@
|
||||
|
||||
<string name="error_empty_field_enter_user_name">Please enter a username.</string>
|
||||
<string name="error_empty_field_your_password">Please enter your password.</string>
|
||||
<string name="room_tombstone_versioned_description">This room has been replaced and is no longer active</string>
|
||||
<string name="room_tombstone_versioned_description">This room has been replaced and is no longer active.</string>
|
||||
<string name="room_tombstone_continuation_link">The conversation continues here</string>
|
||||
<string name="room_tombstone_continuation_description">This room is a continuation of another conversation</string>
|
||||
<string name="room_tombstone_predecessor_link">Click here to see older messages</string>
|
||||
@ -2735,6 +2735,11 @@
|
||||
<string name="settings_server_upload_size_title">Server file upload limit</string>
|
||||
<string name="settings_server_upload_size_content">Your homeserver accepts attachments (files, media, etc.) with a size up to %s.</string>
|
||||
<string name="settings_server_upload_size_unknown">The limit is unknown.</string>
|
||||
<!-- Only visible in developer mode-->
|
||||
<string name="settings_server_room_versions">Room Versions 🕶</string>
|
||||
<string name="settings_server_default_room_version">Default Version</string>
|
||||
<string name="settings_server_room_version_stable">stable</string>
|
||||
<string name="settings_server_room_version_unstable">unstable</string>
|
||||
|
||||
<string name="settings_failed_to_get_crypto_device_info">No cryptographic information available</string>
|
||||
|
||||
@ -3295,6 +3300,7 @@
|
||||
<string name="command_description_create_space">Create a Space</string>
|
||||
<string name="command_description_join_space">Join the Space with the given id</string>
|
||||
<string name="command_description_leave_room">Leave room with given id (or current room if null)</string>
|
||||
<string name="command_description_upgrade_room">Upgrades a room to a new version</string>
|
||||
|
||||
<string name="event_status_a11y_sending">Sending</string>
|
||||
<string name="event_status_a11y_sent">Sent</string>
|
||||
@ -3406,4 +3412,19 @@
|
||||
|
||||
<string name="teammate_spaces_arent_quite_ready">"Teammate spaces aren’t quite ready but you can still give them a try"</string>
|
||||
<string name="teammate_spaces_might_not_join">"At the moment people might not be able to join any private rooms you make.\n\nWe’ll be improving this as part of the beta, but just wanted to let you know."</string>
|
||||
</resources>
|
||||
|
||||
<string name="joining_replacement_room">Join replacement room</string>
|
||||
<string name="it_may_take_some_time">Please be patient, it may take some time.</string>
|
||||
|
||||
|
||||
<string name="upgrade">Upgrade</string>
|
||||
<string name="upgrade_public_room">Upgrade public room</string>
|
||||
<string name="upgrade_private_room">Upgrade private room</string>
|
||||
<string name="upgrade_room_warning">Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.\nThis usually only affects how the room is processed on the server.</string>
|
||||
<string name="upgrade_public_room_from_to">You\'ll upgrade this room from %s to %s.</string>
|
||||
<string name="upgrade_room_auto_invite">Automatically invite users</string>
|
||||
<string name="upgrade_room_update_parent_sapce">Automatically update space parent</string>
|
||||
<string name="upgrade_room_update_parent">Automatically update parent space</string>
|
||||
<string name="upgrade_room_no_power_to_manage">You need permission to upgrade a room</string>
|
||||
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user