Room directory visibility management

This commit is contained in:
Benoit Marty 2020-11-24 11:01:07 +01:00 committed by Benoit Marty
parent a570528f6c
commit 90e0006cae
15 changed files with 296 additions and 18 deletions

View file

@ -32,6 +32,7 @@ Features ✨:
- Create DMs with users by scanning their QR code (#2025)
- Add Invite friends quick invite actions (#2348)
- Add friend by scanning QR code, show your code to friends (#2025)
- Add room aliases management, and room directory visibility management in a dedicated screen (#1579, #2428)
Improvements 🙌:
- New room creation tile with quick action (#2346)

View file

@ -17,6 +17,7 @@
package org.matrix.android.sdk.api.session.room
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
@ -39,4 +40,14 @@ interface RoomDirectoryService {
* Includes both the available protocols and all fields required for queries against each protocol.
*/
fun getThirdPartyProtocol(callback: MatrixCallback<Map<String, ThirdPartyProtocol>>): Cancelable
/**
* Get the visibility of a room in the directory
*/
suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility
/**
* Set the visibility of a room in the directory
*/
suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility)
}

View file

@ -35,6 +35,24 @@ internal interface DirectoryAPI {
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/room/{roomAlias}")
fun getRoomIdByAlias(@Path("roomAlias") roomAlias: String): Call<RoomAliasDescription>
/**
* Get the room directory visibility.
*
* @param roomId the room id.
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/list/room/{roomId}")
fun getRoomDirectoryVisibility(@Path("roomId") roomId: String): Call<RoomDirectoryVisibilityJson>
/**
* Set the room directory visibility.
*
* @param roomId the room id.
* @param body the body containing the new directory visibility
*/
@PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "directory/list/room/{roomId}")
fun setRoomDirectoryVisibility(@Path("roomId") roomId: String,
@Body body: RoomDirectoryVisibilityJson): Call<Unit>
/**
* Add alias to the room.
* @param roomAlias the room alias.

View file

@ -0,0 +1,29 @@
/*
* 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.directory
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
@JsonClass(generateAdapter = true)
internal data class RoomDirectoryVisibilityJson(
/**
* The visibility of the room in the directory. One of: ["private", "public"]
*/
@Json(name = "visibility") val visibility: RoomDirectoryVisibility
)

View file

@ -18,19 +18,25 @@ package org.matrix.android.sdk.internal.session.room
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.room.RoomDirectoryService
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
import org.matrix.android.sdk.api.session.room.model.thirdparty.ThirdPartyProtocol
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask
import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
import javax.inject.Inject
internal class DefaultRoomDirectoryService @Inject constructor(private val getPublicRoomTask: GetPublicRoomTask,
private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask,
private val taskExecutor: TaskExecutor) : RoomDirectoryService {
internal class DefaultRoomDirectoryService @Inject constructor(
private val getPublicRoomTask: GetPublicRoomTask,
private val getThirdPartyProtocolsTask: GetThirdPartyProtocolsTask,
private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask,
private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask,
private val taskExecutor: TaskExecutor) : RoomDirectoryService {
override fun getPublicRooms(server: String?,
publicRoomsParams: PublicRoomsParams,
@ -49,4 +55,12 @@ internal class DefaultRoomDirectoryService @Inject constructor(private val getPu
}
.executeBy(taskExecutor)
}
override suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility {
return getRoomDirectoryVisibilityTask.execute(GetRoomDirectoryVisibilityTask.Params(roomId))
}
override suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility) {
setRoomDirectoryVisibilityTask.execute(SetRoomDirectoryVisibilityTask.Params(roomId, roomDirectoryVisibility))
}
}

View file

@ -38,9 +38,13 @@ import org.matrix.android.sdk.internal.session.room.alias.GetRoomLocalAliasesTas
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
import org.matrix.android.sdk.internal.session.room.create.DefaultCreateRoomTask
import org.matrix.android.sdk.internal.session.room.directory.DefaultGetPublicRoomTask
import org.matrix.android.sdk.internal.session.room.directory.DefaultGetRoomDirectoryVisibilityTask
import org.matrix.android.sdk.internal.session.room.directory.DefaultGetThirdPartyProtocolsTask
import org.matrix.android.sdk.internal.session.room.directory.DefaultSetRoomDirectoryVisibilityTask
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
import org.matrix.android.sdk.internal.session.room.directory.GetThirdPartyProtocolsTask
import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask
import org.matrix.android.sdk.internal.session.room.membership.DefaultLoadRoomMembersTask
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
import org.matrix.android.sdk.internal.session.room.membership.admin.DefaultMembershipAdminTask
@ -139,6 +143,12 @@ internal abstract class RoomModule {
@Binds
abstract fun bindGetPublicRoomTask(task: DefaultGetPublicRoomTask): GetPublicRoomTask
@Binds
abstract fun bindGetRoomDirectoryVisibilityTask(task: DefaultGetRoomDirectoryVisibilityTask): GetRoomDirectoryVisibilityTask
@Binds
abstract fun bindSetRoomDirectoryVisibilityTask(task: DefaultSetRoomDirectoryVisibilityTask): SetRoomDirectoryVisibilityTask
@Binds
abstract fun bindGetThirdPartyProtocolsTask(task: DefaultGetThirdPartyProtocolsTask): GetThirdPartyProtocolsTask

View file

@ -0,0 +1,44 @@
/*
* 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.room.directory
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
import org.matrix.android.sdk.internal.session.directory.RoomDirectoryVisibilityJson
import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject
internal interface GetRoomDirectoryVisibilityTask : Task<GetRoomDirectoryVisibilityTask.Params, RoomDirectoryVisibility> {
data class Params(
val roomId: String
)
}
internal class DefaultGetRoomDirectoryVisibilityTask @Inject constructor(
private val directoryAPI: DirectoryAPI,
private val eventBus: EventBus
) : GetRoomDirectoryVisibilityTask {
override suspend fun execute(params: GetRoomDirectoryVisibilityTask.Params): RoomDirectoryVisibility {
return executeRequest<RoomDirectoryVisibilityJson>(eventBus) {
apiCall = directoryAPI.getRoomDirectoryVisibility(params.roomId)
}
.visibility
}
}

View file

@ -0,0 +1,47 @@
/*
* 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.room.directory
import org.greenrobot.eventbus.EventBus
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.directory.DirectoryAPI
import org.matrix.android.sdk.internal.session.directory.RoomDirectoryVisibilityJson
import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject
internal interface SetRoomDirectoryVisibilityTask : Task<SetRoomDirectoryVisibilityTask.Params, Unit> {
data class Params(
val roomId: String,
val roomDirectoryVisibility: RoomDirectoryVisibility
)
}
internal class DefaultSetRoomDirectoryVisibilityTask @Inject constructor(
private val directoryAPI: DirectoryAPI,
private val eventBus: EventBus
) : SetRoomDirectoryVisibilityTask {
override suspend fun execute(params: SetRoomDirectoryVisibilityTask.Params) {
executeRequest<Unit>(eventBus) {
apiCall = directoryAPI.setRoomDirectoryVisibility(
params.roomId,
RoomDirectoryVisibilityJson(visibility = params.roomDirectoryVisibility)
)
}
}
}

View file

@ -61,8 +61,8 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
holder.switchView.isEnabled = enabled
holder.switchView.setOnCheckedChangeListener(null)
holder.switchView.isChecked = switchChecked
holder.switchView.setOnCheckedChangeListener { _, isChecked ->
listener?.invoke(isChecked)
}

View file

@ -17,6 +17,7 @@
package im.vector.app.features.roomprofile.alias
import im.vector.app.core.platform.VectorViewModelAction
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
sealed class RoomAliasAction : VectorViewModelAction {
// Canonical
@ -27,6 +28,9 @@ sealed class RoomAliasAction : VectorViewModelAction {
data class UnpublishAlias(val alias: String) : RoomAliasAction()
data class SetCanonicalAlias(val canonicalAlias: String?) : RoomAliasAction()
// Room directory
data class SetRoomDirectoryVisibility(val roomDirectoryVisibility: RoomDirectoryVisibility) : RoomAliasAction()
// Local
data class RemoveLocalAlias(val alias: String) : RoomAliasAction()
object ToggleAddLocalAliasForm : RoomAliasAction()

View file

@ -18,6 +18,7 @@ package im.vector.app.features.roomprofile.alias
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import im.vector.app.R
@ -32,9 +33,11 @@ import im.vector.app.features.discovery.settingsButtonItem
import im.vector.app.features.discovery.settingsContinueCancelItem
import im.vector.app.features.discovery.settingsInfoItem
import im.vector.app.features.form.formEditTextItem
import im.vector.app.features.form.formSwitchItem
import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter
import im.vector.app.features.roomdirectory.createroom.roomAliasEditItem
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import javax.inject.Inject
class RoomAliasController @Inject constructor(
@ -48,6 +51,7 @@ class RoomAliasController @Inject constructor(
fun toggleManualPublishForm()
fun setNewAlias(value: String)
fun addAlias()
fun setRoomDirectoryVisibility(roomDirectoryVisibility: RoomDirectoryVisibility)
fun toggleLocalAliasForm()
fun setNewLocalAliasLocalPart(value: String)
fun addLocalAlias()
@ -63,12 +67,42 @@ class RoomAliasController @Inject constructor(
override fun buildModels(data: RoomAliasViewState?) {
data ?: return
// Published
// Published alias
buildPublishInfo(data)
// Local
// Room directory visibility
buildRoomDirectoryVisibility(data)
// Local alias
buildLocalInfo(data)
}
private fun buildRoomDirectoryVisibility(data: RoomAliasViewState) {
when (data.roomDirectoryVisibility) {
Uninitialized -> Unit
is Loading -> Unit
is Success -> {
formSwitchItem {
id("roomVisibility")
title(stringProvider.getString(R.string.room_alias_publish_to_directory, data.homeServerName))
showDivider(false)
switchChecked(data.roomDirectoryVisibility() == RoomDirectoryVisibility.PUBLIC)
listener {
if (it) {
callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PUBLIC)
} else {
callback?.setRoomDirectoryVisibility(RoomDirectoryVisibility.PRIVATE)
}
}
}
}
is Fail -> {
errorWithRetryItem {
text(stringProvider.getString(R.string.room_alias_publish_to_directory_error,
errorFormatter.toHumanReadable(data.roomDirectoryVisibility.error)))
}
}
}
}
private fun buildPublishInfo(data: RoomAliasViewState) {
buildProfileSection(
stringProvider.getString(R.string.room_alias_published_alias_title)

View file

@ -40,6 +40,7 @@ import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetShare
import kotlinx.android.synthetic.main.fragment_room_setting_generic.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject
@ -150,6 +151,10 @@ class RoomAliasFragment @Inject constructor(
viewModel.handle(RoomAliasAction.ManualPublishAlias)
}
override fun setRoomDirectoryVisibility(roomDirectoryVisibility: RoomDirectoryVisibility) {
viewModel.handle(RoomAliasAction.SetRoomDirectoryVisibility(roomDirectoryVisibility))
}
override fun toggleLocalAliasForm() {
viewModel.handle(RoomAliasAction.ToggleAddLocalAliasForm)
}

View file

@ -67,6 +67,35 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo
observePowerLevel()
observeRoomCanonicalAlias()
fetchRoomAlias()
fetchRoomDirectoryVisibility()
}
private fun fetchRoomDirectoryVisibility() {
setState {
copy(
roomDirectoryVisibility = Loading()
)
}
viewModelScope.launch {
runCatching {
session.getRoomDirectoryVisibility(room.roomId)
}.fold(
{
setState {
copy(
roomDirectoryVisibility = Success(it)
)
}
},
{
setState {
copy(
roomDirectoryVisibility = Fail(it)
)
}
}
)
}
}
private fun initHomeServerName() {
@ -155,19 +184,43 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo
override fun handle(action: RoomAliasAction) {
when (action) {
RoomAliasAction.ToggleManualPublishForm -> handleToggleManualPublishForm()
is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action)
is RoomAliasAction.ManualPublishAlias -> handleManualPublishAlias()
is RoomAliasAction.UnpublishAlias -> handleUnpublishAlias(action)
is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action)
RoomAliasAction.ToggleAddLocalAliasForm -> handleToggleAddLocalAliasForm()
is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action)
RoomAliasAction.AddLocalAlias -> handleAddLocalAlias()
is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action)
is RoomAliasAction.PublishAlias -> handlePublishAlias(action)
RoomAliasAction.ToggleManualPublishForm -> handleToggleManualPublishForm()
is RoomAliasAction.SetNewAlias -> handleSetNewAlias(action)
is RoomAliasAction.ManualPublishAlias -> handleManualPublishAlias()
is RoomAliasAction.UnpublishAlias -> handleUnpublishAlias(action)
is RoomAliasAction.SetCanonicalAlias -> handleSetCanonicalAlias(action)
is RoomAliasAction.SetRoomDirectoryVisibility -> handleSetRoomDirectoryVisibility(action)
RoomAliasAction.ToggleAddLocalAliasForm -> handleToggleAddLocalAliasForm()
is RoomAliasAction.SetNewLocalAliasLocalPart -> handleSetNewLocalAliasLocalPart(action)
RoomAliasAction.AddLocalAlias -> handleAddLocalAlias()
is RoomAliasAction.RemoveLocalAlias -> handleRemoveLocalAlias(action)
is RoomAliasAction.PublishAlias -> handlePublishAlias(action)
}.exhaustive
}
private fun handleSetRoomDirectoryVisibility(action: RoomAliasAction.SetRoomDirectoryVisibility) {
postLoading(true)
viewModelScope.launch {
runCatching {
session.setRoomDirectoryVisibility(room.roomId, action.roomDirectoryVisibility)
}.fold(
{
setState {
copy(
isLoading = false,
// Local echo, no need to fetch the data from the server again
roomDirectoryVisibility = Success(action.roomDirectoryVisibility)
)
}
},
{
postLoading(false)
_viewEvents.post(RoomAliasViewEvents.Failure(it))
}
)
}
}
private fun handleToggleAddLocalAliasForm() {
setState {
copy(
@ -229,7 +282,8 @@ class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: Roo
updateCanonicalAlias(
canonicalAlias = state.canonicalAlias,
alternativeAliases = state.alternativeAliases - action.alias,
closeForm = false )
closeForm = false
)
}
private fun handleSetCanonicalAlias(action: RoomAliasAction.SetCanonicalAlias) = withState { state ->

View file

@ -20,6 +20,7 @@ import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.app.features.roomprofile.RoomProfileArgs
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomSummary
data class RoomAliasViewState(
@ -27,6 +28,7 @@ data class RoomAliasViewState(
val homeServerName: String = "",
val roomSummary: Async<RoomSummary> = Uninitialized,
val actionPermissions: ActionPermissions = ActionPermissions(),
val roomDirectoryVisibility: Async<RoomDirectoryVisibility> = Uninitialized,
val isLoading: Boolean = false,
val canonicalAlias: String? = null,
val alternativeAliases: List<String> = emptyList(),

View file

@ -1026,7 +1026,7 @@
<!-- room settings : alias -->
<string name="room_settings_alias_title">Room addresses</string>
<string name="room_settings_alias_subtitle">See and managed addresses of this room</string>
<string name="room_settings_alias_subtitle">See and managed addresses of this room, and its visibility in the room directory.</string>
<string name="room_alias_title">Room Addresses</string>
<string name="room_alias_published_alias_title">Published Addresses</string>
@ -1053,6 +1053,11 @@
<string name="room_alias_action_publish">Publish this address</string>
<string name="room_alias_action_unpublish">Unpublish this address</string>
<!-- Parameter will be the url of the homeserver, ex: matrix.org -->
<string name="room_alias_publish_to_directory">Publish this room to the public in %1$s\'s room directory?</string>
<!-- Parameter will be a technical error message -->
<string name="room_alias_publish_to_directory_error">Unable to retrieve the current room directory visibility (%1$s).</string>
<!-- Room settings, access and visibility : WHO CAN READ HISTORY? (read rule) -->
<string name="room_settings_read_history_entry_anyone">Anyone</string>
<string name="room_settings_read_history_entry_members_only_option_time_shared">Members only (since the point in time of selecting this option)</string>