Merge pull request #3912 from vector-im/feature/bca/m11.10-14

Better expose adding spaces as Subspaces
This commit is contained in:
Benoit Marty 2021-09-17 15:21:14 +02:00 committed by GitHub
commit d0e43a2ebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 536 additions and 111 deletions

1
changelog.d/3752.feature Normal file
View file

@ -0,0 +1 @@
Better expose adding spaces as Subspaces

View file

@ -8,6 +8,7 @@
<attr name="actionDescription" format="string" /> <attr name="actionDescription" format="string" />
<attr name="leftIcon" format="reference" /> <attr name="leftIcon" format="reference" />
<attr name="rightIcon" format="reference" /> <attr name="rightIcon" format="reference" />
<attr name="betaAction" format="boolean" />
<attr name="forceStartPadding" format="boolean" /> <attr name="forceStartPadding" format="boolean" />
</declare-styleable> </declare-styleable>

View file

@ -59,7 +59,20 @@ class AppStateHandler @Inject constructor(
val selectedRoomGroupingObservable = selectedSpaceDataSource.observe() val selectedRoomGroupingObservable = selectedSpaceDataSource.observe()
fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? = selectedSpaceDataSource.currentValue?.orNull() fun getCurrentRoomGroupingMethod(): RoomGroupingMethod? {
// XXX we should somehow make it live :/ just a work around
// For example just after creating a space and switching to it the
// name in the app Bar could show Empty Room, and it will not update unless you
// switch space
return selectedSpaceDataSource.currentValue?.orNull()?.let {
if (it is RoomGroupingMethod.BySpace) {
// try to refresh sum?
it.spaceSummary?.roomId?.let { activeSessionHolder.getSafeActiveSession()?.getRoomSummary(it) }?.let {
RoomGroupingMethod.BySpace(it)
} ?: it
} else it
}
}
fun setCurrentSpace(spaceId: String?, session: Session? = null) { fun setCurrentSpace(spaceId: String?, session: Session? = null) {
val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return val uSession = session ?: activeSessionHolder.getSafeActiveSession() ?: return

View file

@ -16,11 +16,14 @@
package im.vector.app.core.extensions package im.vector.app.core.extensions
import android.graphics.drawable.Drawable
import android.text.InputType import android.text.InputType
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.ImageView
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
/** /**
@ -50,3 +53,8 @@ fun View.getMeasurements(): Pair<Int, Int> {
val height = measuredHeight val height = measuredHeight
return width to height return width to height
} }
fun ImageView.setDrawableOrHide(drawableRes: Drawable?) {
setImageDrawable(drawableRes)
isVisible = drawableRes != null
}

View file

@ -26,6 +26,7 @@ import androidx.core.view.isGone
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.setDrawableOrHide
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.databinding.ViewBottomSheetActionButtonBinding import im.vector.app.databinding.ViewBottomSheetActionButtonBinding
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
@ -80,7 +81,7 @@ class BottomSheetActionButton @JvmOverloads constructor(
var rightIcon: Drawable? = null var rightIcon: Drawable? = null
set(value) { set(value) {
field = value field = value
views.bottomSheetActionIcon.setImageDrawable(value) views.bottomSheetActionIcon.setDrawableOrHide(value)
} }
var tint: Int? = null var tint: Int? = null
@ -95,6 +96,12 @@ class BottomSheetActionButton @JvmOverloads constructor(
value?.let { views.bottomSheetActionTitle.setTextColor(it) } value?.let { views.bottomSheetActionTitle.setTextColor(it) }
} }
var isBetaAction: Boolean? = null
set(value) {
field = value
views.bottomSheetActionBeta.isVisible = field ?: false
}
init { init {
inflate(context, R.layout.view_bottom_sheet_action_button, this) inflate(context, R.layout.view_bottom_sheet_action_button, this)
views = ViewBottomSheetActionButtonBinding.bind(this) views = ViewBottomSheetActionButtonBinding.bind(this)
@ -109,6 +116,8 @@ class BottomSheetActionButton @JvmOverloads constructor(
tint = getColor(R.styleable.BottomSheetActionButton_tint, ThemeUtils.getColor(context, android.R.attr.textColor)) tint = getColor(R.styleable.BottomSheetActionButton_tint, ThemeUtils.getColor(context, android.R.attr.textColor))
titleTextColor = getColor(R.styleable.BottomSheetActionButton_titleTextColor, ThemeUtils.getColor(context, R.attr.colorPrimary)) titleTextColor = getColor(R.styleable.BottomSheetActionButton_titleTextColor, ThemeUtils.getColor(context, R.attr.colorPrimary))
isBetaAction = getBoolean(R.styleable.BottomSheetActionButton_betaAction, false)
} }
} }
} }

View file

@ -29,6 +29,7 @@ import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import com.google.android.material.badge.BadgeDrawable import com.google.android.material.badge.BadgeDrawable
import im.vector.app.AppStateHandler
import im.vector.app.R import im.vector.app.R
import im.vector.app.RoomGroupingMethod import im.vector.app.RoomGroupingMethod
import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.commitTransaction
@ -69,7 +70,8 @@ class HomeDetailFragment @Inject constructor(
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val alertManager: PopupAlertManager, private val alertManager: PopupAlertManager,
private val callManager: WebRtcCallManager, private val callManager: WebRtcCallManager,
private val vectorPreferences: VectorPreferences private val vectorPreferences: VectorPreferences,
private val appStateHandler: AppStateHandler
) : VectorBaseFragment<FragmentHomeDetailBinding>(), ) : VectorBaseFragment<FragmentHomeDetailBinding>(),
KeysBackupBanner.Delegate, KeysBackupBanner.Delegate,
CurrentCallsView.Callback, CurrentCallsView.Callback,
@ -204,6 +206,18 @@ class HomeDetailFragment @Inject constructor(
// update notification tab if needed // update notification tab if needed
updateTabVisibilitySafely(R.id.bottom_action_notification, vectorPreferences.labAddNotificationTab()) updateTabVisibilitySafely(R.id.bottom_action_notification, vectorPreferences.labAddNotificationTab())
callManager.checkForProtocolsSupportIfNeeded() callManager.checkForProtocolsSupportIfNeeded()
// Current space/group is not live so at least refresh toolbar on resume
appStateHandler.getCurrentRoomGroupingMethod()?.let { roomGroupingMethod ->
when (roomGroupingMethod) {
is RoomGroupingMethod.ByLegacyGroup -> {
onGroupChange(roomGroupingMethod.groupSummary)
}
is RoomGroupingMethod.BySpace -> {
onSpaceChange(roomGroupingMethod.spaceSummary)
}
}
}
} }
private fun promptForNewUnknownDevices(uid: String, state: UnknownDevicesState, newest: DeviceInfo) { private fun promptForNewUnknownDevices(uid: String, state: UnknownDevicesState, newest: DeviceInfo) {

View file

@ -70,7 +70,6 @@ class MatrixToRoomSpaceFragment @Inject constructor(
val matrixItem = peek.roomItem val matrixItem = peek.roomItem
avatarRenderer.render(matrixItem, views.matrixToCardAvatar) avatarRenderer.render(matrixItem, views.matrixToCardAvatar)
if (peek.roomType == RoomType.SPACE) { if (peek.roomType == RoomType.SPACE) {
views.matrixToBetaTag.isVisible = true
views.matrixToAccessImage.isVisible = true views.matrixToAccessImage.isVisible = true
if (peek.isPublic) { if (peek.isPublic) {
views.matrixToAccessText.setTextOrHide(context?.getString(R.string.public_space)) views.matrixToAccessText.setTextOrHide(context?.getString(R.string.public_space))
@ -79,8 +78,6 @@ class MatrixToRoomSpaceFragment @Inject constructor(
views.matrixToAccessText.setTextOrHide(context?.getString(R.string.private_space)) views.matrixToAccessText.setTextOrHide(context?.getString(R.string.private_space))
views.matrixToAccessImage.setImageResource(R.drawable.ic_room_private) views.matrixToAccessImage.setImageResource(R.drawable.ic_room_private)
} }
} else {
views.matrixToBetaTag.isVisible = false
} }
views.matrixToCardNameText.setTextOrHide(peek.name) views.matrixToCardNameText.setTextOrHide(peek.name)
views.matrixToCardAliasText.setTextOrHide(peek.alias) views.matrixToCardAliasText.setTextOrHide(peek.alias)

View file

@ -50,7 +50,6 @@ class SpaceCardRenderer @Inject constructor(
inCard.matrixToCardButtonLoading.isVisible = false inCard.matrixToCardButtonLoading.isVisible = false
avatarRenderer.render(spaceSummary.toMatrixItem(), inCard.matrixToCardAvatar) avatarRenderer.render(spaceSummary.toMatrixItem(), inCard.matrixToCardAvatar)
inCard.matrixToCardNameText.text = spaceSummary.name inCard.matrixToCardNameText.text = spaceSummary.name
inCard.matrixToBetaTag.isVisible = true
inCard.matrixToCardAliasText.setTextOrHide(spaceSummary.canonicalAlias) inCard.matrixToCardAliasText.setTextOrHide(spaceSummary.canonicalAlias)
inCard.matrixToCardDescText.setTextOrHide(spaceSummary.topic.linkify(matrixLinkCallback)) inCard.matrixToCardDescText.setTextOrHide(spaceSummary.topic.linkify(matrixLinkCallback))
if (spaceSummary.isPublic) { if (spaceSummary.isPublic) {
@ -97,7 +96,6 @@ class SpaceCardRenderer @Inject constructor(
inCard.matrixToCardButtonLoading.isVisible = false inCard.matrixToCardButtonLoading.isVisible = false
avatarRenderer.render(spaceChildInfo.toMatrixItem(), inCard.matrixToCardAvatar) avatarRenderer.render(spaceChildInfo.toMatrixItem(), inCard.matrixToCardAvatar)
inCard.matrixToCardNameText.setTextOrHide(spaceChildInfo.name) inCard.matrixToCardNameText.setTextOrHide(spaceChildInfo.name)
inCard.matrixToBetaTag.isVisible = true
inCard.matrixToCardAliasText.setTextOrHide(spaceChildInfo.canonicalAlias) inCard.matrixToCardAliasText.setTextOrHide(spaceChildInfo.canonicalAlias)
inCard.matrixToCardDescText.setTextOrHide(spaceChildInfo.topic?.linkify(matrixLinkCallback)) inCard.matrixToCardDescText.setTextOrHide(spaceChildInfo.topic?.linkify(matrixLinkCallback))
if (spaceChildInfo.worldReadable) { if (spaceChildInfo.worldReadable) {

View file

@ -53,7 +53,10 @@ class CreateRoomActivity : VectorBaseActivity<ActivitySimpleBinding>(), ToolbarC
addFragment( addFragment(
R.id.simpleFragmentContainer, R.id.simpleFragmentContainer,
CreateRoomFragment::class.java, CreateRoomFragment::class.java,
CreateRoomArgs(intent?.getStringExtra(INITIAL_NAME) ?: "") CreateRoomArgs(
intent?.getStringExtra(INITIAL_NAME) ?: "",
isSpace = intent?.getBooleanExtra(IS_SPACE, false) ?: false
)
) )
} }
} }
@ -74,10 +77,12 @@ class CreateRoomActivity : VectorBaseActivity<ActivitySimpleBinding>(), ToolbarC
companion object { companion object {
private const val INITIAL_NAME = "INITIAL_NAME" private const val INITIAL_NAME = "INITIAL_NAME"
private const val IS_SPACE = "IS_SPACE"
fun getIntent(context: Context, initialName: String = ""): Intent { fun getIntent(context: Context, initialName: String = "", isSpace: Boolean = false): Intent {
return Intent(context, CreateRoomActivity::class.java).apply { return Intent(context, CreateRoomActivity::class.java).apply {
putExtra(INITIAL_NAME, initialName) putExtra(INITIAL_NAME, initialName)
putExtra(IS_SPACE, isSpace)
} }
} }
} }

View file

@ -38,6 +38,7 @@ import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.databinding.FragmentCreateRoomBinding import im.vector.app.databinding.FragmentCreateRoomBinding
import im.vector.app.features.navigation.Navigator
import im.vector.app.features.roomdirectory.RoomDirectorySharedAction import im.vector.app.features.roomdirectory.RoomDirectorySharedAction
import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet
@ -51,11 +52,13 @@ import javax.inject.Inject
@Parcelize @Parcelize
data class CreateRoomArgs( data class CreateRoomArgs(
val initialName: String, val initialName: String,
val parentSpaceId: String? = null val parentSpaceId: String? = null,
val isSpace: Boolean = false
) : Parcelable ) : Parcelable
class CreateRoomFragment @Inject constructor( class CreateRoomFragment @Inject constructor(
private val createRoomController: CreateRoomController, private val createRoomController: CreateRoomController,
private val createSpaceController: CreateSubSpaceController,
val createRoomViewModelFactory: CreateRoomViewModel.Factory, val createRoomViewModelFactory: CreateRoomViewModel.Factory,
colorProvider: ColorProvider colorProvider: ColorProvider
) : VectorBaseFragment<FragmentCreateRoomBinding>(), ) : VectorBaseFragment<FragmentCreateRoomBinding>(),
@ -93,6 +96,11 @@ class CreateRoomFragment @Inject constructor(
} }
} }
override fun onResume() {
super.onResume()
views.createRoomTitle.text = getString(if (args.isSpace) R.string.create_new_space else R.string.create_new_room)
}
private fun setupRoomJoinRuleSharedActionViewModel() { private fun setupRoomJoinRuleSharedActionViewModel() {
roomJoinRuleSharedActionViewModel = activityViewModelProvider.get(RoomJoinRuleSharedActionViewModel::class.java) roomJoinRuleSharedActionViewModel = activityViewModelProvider.get(RoomJoinRuleSharedActionViewModel::class.java)
roomJoinRuleSharedActionViewModel roomJoinRuleSharedActionViewModel
@ -112,19 +120,27 @@ class CreateRoomFragment @Inject constructor(
private fun setupWaitingView() { private fun setupWaitingView() {
views.waitingView.waitingStatusText.isVisible = true views.waitingView.waitingStatusText.isVisible = true
views.waitingView.waitingStatusText.setText(R.string.create_room_in_progress) views.waitingView.waitingStatusText.setText(
if (args.isSpace) R.string.create_space_in_progress else R.string.create_room_in_progress
)
} }
override fun onDestroyView() { override fun onDestroyView() {
views.createRoomForm.cleanup() views.createRoomForm.cleanup()
createRoomController.listener = null createRoomController.listener = null
createSpaceController.listener = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
if (args.isSpace) {
views.createRoomForm.configureWith(createSpaceController)
createSpaceController.listener = this
} else {
views.createRoomForm.configureWith(createRoomController) views.createRoomForm.configureWith(createRoomController)
createRoomController.listener = this createRoomController.listener = this
} }
}
override fun onAvatarDelete() { override fun onAvatarDelete() {
viewModel.handle(CreateRoomAction.SetAvatar(null)) viewModel.handle(CreateRoomAction.SetAvatar(null))
@ -153,7 +169,11 @@ class CreateRoomFragment @Inject constructor(
} else { } else {
listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC) listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC)
} }
RoomJoinRuleBottomSheet.newInstance(state.roomJoinRules, allowed.map { it.toOption(false) }) RoomJoinRuleBottomSheet.newInstance(state.roomJoinRules,
allowed.map { it.toOption(false) },
state.isSubSpace,
state.parentSpaceSummary?.displayName
)
.show(childFragmentManager, "RoomJoinRuleBottomSheet") .show(childFragmentManager, "RoomJoinRuleBottomSheet")
} }
// override fun setIsPublic(isPublic: Boolean) { // override fun setIsPublic(isPublic: Boolean) {
@ -203,12 +223,24 @@ class CreateRoomFragment @Inject constructor(
views.waitingView.root.isVisible = async is Loading views.waitingView.root.isVisible = async is Loading
if (async is Success) { if (async is Success) {
// Navigate to freshly created room // Navigate to freshly created room
if (state.isSubSpace) {
navigator.switchToSpace(
requireContext(),
async(),
Navigator.PostSwitchSpaceAction.None
)
} else {
navigator.openRoom(requireActivity(), async()) navigator.openRoom(requireActivity(), async())
}
sharedActionViewModel.post(RoomDirectorySharedAction.Close) sharedActionViewModel.post(RoomDirectorySharedAction.Close)
} else { } else {
// Populate list with Epoxy // Populate list with Epoxy
if (args.isSpace) {
createSpaceController.setData(state)
} else {
createRoomController.setData(state) createRoomController.setData(state)
} }
} }
}
} }

View file

@ -42,9 +42,11 @@ import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset
@ -241,6 +243,18 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init
name = state.roomName.takeIf { it.isNotBlank() } name = state.roomName.takeIf { it.isNotBlank() }
topic = state.roomTopic.takeIf { it.isNotBlank() } topic = state.roomTopic.takeIf { it.isNotBlank() }
avatarUri = state.avatarUri avatarUri = state.avatarUri
if (state.isSubSpace) {
// Space-rooms are distinguished from regular messaging rooms by the m.room.type of m.space
roomType = RoomType.SPACE
// Space-rooms should be created with a power level for events_default of 100,
// to prevent the rooms accidentally/maliciously clogging up with messages from random members of the space.
powerLevelContentOverride = PowerLevelsContent(
eventsDefault = 100
)
}
when (state.roomJoinRules) { when (state.roomJoinRules) {
RoomJoinRules.PUBLIC -> { RoomJoinRules.PUBLIC -> {
// Directory visibility // Directory visibility

View file

@ -37,12 +37,14 @@ data class CreateRoomViewState(
val parentSpaceId: String?, val parentSpaceId: String?,
val parentSpaceSummary: RoomSummary? = null, val parentSpaceSummary: RoomSummary? = null,
val supportsRestricted: Boolean = false, val supportsRestricted: Boolean = false,
val aliasLocalPart: String? = null val aliasLocalPart: String? = null,
val isSubSpace: Boolean = false
) : MvRxState { ) : MvRxState {
constructor(args: CreateRoomArgs) : this( constructor(args: CreateRoomArgs) : this(
roomName = args.initialName, roomName = args.initialName,
parentSpaceId = args.parentSpaceId parentSpaceId = args.parentSpaceId,
isSubSpace = args.isSpace
) )
/** /**

View file

@ -0,0 +1,153 @@
/*
* 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.roomdirectory.createroom
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import im.vector.app.R
import im.vector.app.core.epoxy.profiles.buildProfileAction
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.genericPillItem
import im.vector.app.features.discovery.settingsSectionTitleItem
import im.vector.app.features.form.formEditTextItem
import im.vector.app.features.form.formEditableSquareAvatarItem
import im.vector.app.features.form.formMultiLineEditTextItem
import im.vector.app.features.form.formSubmitButtonItem
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import javax.inject.Inject
class CreateSubSpaceController @Inject constructor(
private val stringProvider: StringProvider,
private val roomAliasErrorFormatter: RoomAliasErrorFormatter
) : TypedEpoxyController<CreateRoomViewState>() {
var listener: CreateRoomController.Listener? = null
override fun buildModels(viewState: CreateRoomViewState) {
// display the form
buildForm(viewState, viewState.asyncCreateRoomRequest !is Loading)
}
private fun buildForm(data: CreateRoomViewState, enableFormElement: Boolean) {
val host = this
genericPillItem {
id("beta")
imageRes(R.drawable.ic_beta_pill)
tintIcon(false)
text(host.stringProvider.getString(R.string.space_add_space_to_any_space_you_manage))
}
formEditableSquareAvatarItem {
id("avatar")
enabled(enableFormElement)
imageUri(data.avatarUri)
clickListener { host.listener?.onAvatarChange() }
deleteListener { host.listener?.onAvatarDelete() }
}
formEditTextItem {
id("name")
enabled(enableFormElement)
enabled(true)
value(data.roomName)
hint(host.stringProvider.getString(R.string.create_room_name_hint))
onTextChange { text ->
host.listener?.onNameChange(text)
}
}
if (data.roomJoinRules == RoomJoinRules.PUBLIC) {
formEditTextItem {
id("alias")
enabled(enableFormElement)
value(data.aliasLocalPart)
hint(host.stringProvider.getString(R.string.create_space_alias_hint))
suffixText(":" + data.homeServerName)
prefixText("#")
errorMessage(
host.roomAliasErrorFormatter.format(
(((data.asyncCreateRoomRequest as? Fail)?.error) as? CreateRoomFailure.AliasError)?.aliasError)
)
onTextChange { value ->
host.listener?.setAliasLocalPart(value)
}
}
}
formMultiLineEditTextItem {
id("topic")
enabled(enableFormElement)
value(data.roomTopic)
hint(host.stringProvider.getString(R.string.create_space_topic_hint))
textSizeSp(16)
onTextChange { text ->
host.listener?.onTopicChange(text)
}
}
settingsSectionTitleItem {
id("visibility")
titleResId(R.string.room_settings_space_access_title)
}
when (data.roomJoinRules) {
RoomJoinRules.INVITE -> {
buildProfileAction(
id = "joinRule",
title = stringProvider.getString(R.string.room_settings_room_access_private_title),
subtitle = stringProvider.getString(R.string.room_settings_room_access_private_description),
divider = false,
editable = true,
action = { host.listener?.selectVisibility() }
)
}
RoomJoinRules.PUBLIC -> {
buildProfileAction(
id = "joinRule",
title = stringProvider.getString(R.string.room_settings_room_access_public_title),
subtitle = stringProvider.getString(R.string.room_settings_room_access_public_description),
divider = false,
editable = true,
action = { host.listener?.selectVisibility() }
)
}
RoomJoinRules.RESTRICTED -> {
buildProfileAction(
id = "joinRule",
title = stringProvider.getString(R.string.room_settings_room_access_restricted_title),
subtitle = stringProvider.getString(R.string.room_create_member_of_space_name_can_join, data.parentSpaceSummary?.displayName),
divider = false,
editable = true,
action = { host.listener?.selectVisibility() }
)
}
else -> {
// not yet supported
}
}
formSubmitButtonItem {
id("submit")
enabled(enableFormElement && data.roomName.isNullOrBlank().not())
buttonTitleId(R.string.create_room_action_create)
buttonClickListener { host.listener?.submit() }
}
}
}

View file

@ -39,7 +39,9 @@ fun RoomJoinRules.toOption(needUpgrade: Boolean) = JoinRulesOptionSupport(this,
@Parcelize @Parcelize
data class RoomJoinRuleBottomSheetArgs( data class RoomJoinRuleBottomSheetArgs(
val currentRoomJoinRule: RoomJoinRules, val currentRoomJoinRule: RoomJoinRules,
val allowedJoinedRules: List<JoinRulesOptionSupport> val allowedJoinedRules: List<JoinRulesOptionSupport>,
val isSpace: Boolean = false,
val parentSpaceName: String?
) : Parcelable ) : Parcelable
class RoomJoinRuleBottomSheet : BottomSheetGeneric<RoomJoinRuleState, RoomJoinRuleRadioAction>() { class RoomJoinRuleBottomSheet : BottomSheetGeneric<RoomJoinRuleState, RoomJoinRuleRadioAction>() {
@ -73,11 +75,13 @@ class RoomJoinRuleBottomSheet : BottomSheetGeneric<RoomJoinRuleState, RoomJoinRu
fun newInstance(currentRoomJoinRule: RoomJoinRules, fun newInstance(currentRoomJoinRule: RoomJoinRules,
allowedJoinedRules: List<JoinRulesOptionSupport> = listOf( allowedJoinedRules: List<JoinRulesOptionSupport> = listOf(
RoomJoinRules.INVITE, RoomJoinRules.PUBLIC RoomJoinRules.INVITE, RoomJoinRules.PUBLIC
).map { it.toOption(true) } ).map { it.toOption(true) },
isSpace: Boolean = false,
parentSpaceName: String? = null
): RoomJoinRuleBottomSheet { ): RoomJoinRuleBottomSheet {
return RoomJoinRuleBottomSheet().apply { return RoomJoinRuleBottomSheet().apply {
setArguments( setArguments(
RoomJoinRuleBottomSheetArgs(currentRoomJoinRule, allowedJoinedRules) RoomJoinRuleBottomSheetArgs(currentRoomJoinRule, allowedJoinedRules, isSpace, parentSpaceName)
) )
} }
} }

View file

@ -20,8 +20,6 @@ import im.vector.app.R
import im.vector.app.core.resources.DrawableProvider import im.vector.app.core.resources.DrawableProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController
import me.gujun.android.span.image
import me.gujun.android.span.span
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
import javax.inject.Inject import javax.inject.Inject
@ -30,7 +28,11 @@ class RoomJoinRuleController @Inject constructor(
private val drawableProvider: DrawableProvider private val drawableProvider: DrawableProvider
) : BottomSheetGenericController<RoomJoinRuleState, RoomJoinRuleRadioAction>() { ) : BottomSheetGenericController<RoomJoinRuleState, RoomJoinRuleRadioAction>() {
override fun getTitle() = stringProvider.getString(R.string.room_settings_room_access_rules_pref_dialog_title) override fun getTitle() =
stringProvider.getString(
// generic title for both room and space
R.string.room_settings_access_rules_pref_dialog_title
)
override fun getActions(state: RoomJoinRuleState): List<RoomJoinRuleRadioAction> { override fun getActions(state: RoomJoinRuleState): List<RoomJoinRuleRadioAction> {
return listOf( return listOf(
@ -42,21 +44,21 @@ class RoomJoinRuleController @Inject constructor(
), ),
RoomJoinRuleRadioAction( RoomJoinRuleRadioAction(
roomJoinRule = RoomJoinRules.PUBLIC, roomJoinRule = RoomJoinRules.PUBLIC,
description = stringProvider.getString(R.string.room_settings_room_access_public_description), description = stringProvider.getString(
if (state.isSpace) R.string.room_settings_space_access_public_description
else R.string.room_settings_room_access_public_description
),
title = stringProvider.getString(R.string.room_settings_room_access_public_title), title = stringProvider.getString(R.string.room_settings_room_access_public_title),
isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC
), ),
RoomJoinRuleRadioAction( RoomJoinRuleRadioAction(
roomJoinRule = RoomJoinRules.RESTRICTED, roomJoinRule = RoomJoinRules.RESTRICTED,
description = stringProvider.getString(R.string.room_settings_room_access_restricted_description), description = if (state.parentSpaceName != null) {
title = span { stringProvider.getString(R.string.room_create_member_of_space_name_can_join, state.parentSpaceName)
+stringProvider.getString(R.string.room_settings_room_access_restricted_title) } else {
+" " stringProvider.getString(R.string.room_settings_room_access_restricted_description)
image(
drawableProvider.getDrawable(R.drawable.ic_beta_pill)!!,
"bottom"
)
}, },
title = stringProvider.getString(R.string.room_settings_room_access_restricted_title),
isSelected = state.currentRoomJoinRule == RoomJoinRules.RESTRICTED isSelected = state.currentRoomJoinRule == RoomJoinRules.RESTRICTED
) )
).filter { state.allowedJoinedRules.map { it.rule }.contains(it.roomJoinRule) } ).filter { state.allowedJoinedRules.map { it.rule }.contains(it.roomJoinRule) }

View file

@ -24,11 +24,15 @@ data class RoomJoinRuleState(
val currentRoomJoinRule: RoomJoinRules = RoomJoinRules.INVITE, val currentRoomJoinRule: RoomJoinRules = RoomJoinRules.INVITE,
val allowedJoinedRules: List<JoinRulesOptionSupport> = val allowedJoinedRules: List<JoinRulesOptionSupport> =
listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC).map { it.toOption(true) }, listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC).map { it.toOption(true) },
val currentGuestAccess: GuestAccess? = null val currentGuestAccess: GuestAccess? = null,
val isSpace: Boolean = false,
val parentSpaceName: String?
) : BottomSheetGenericState() { ) : BottomSheetGenericState() {
constructor(args: RoomJoinRuleBottomSheetArgs) : this( constructor(args: RoomJoinRuleBottomSheetArgs) : this(
currentRoomJoinRule = args.currentRoomJoinRule, currentRoomJoinRule = args.currentRoomJoinRule,
allowedJoinedRules = args.allowedJoinedRules allowedJoinedRules = args.allowedJoinedRules,
isSpace = args.isSpace,
parentSpaceName = args.parentSpaceName
) )
} }

View file

@ -33,7 +33,6 @@ import im.vector.app.databinding.BottomSheetSpaceSettingsBinding
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.navigation.Navigator import im.vector.app.features.navigation.Navigator
import im.vector.app.features.rageshake.BugReporter import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.rageshake.ReportType
import im.vector.app.features.roomprofile.RoomProfileActivity import im.vector.app.features.roomprofile.RoomProfileActivity
import im.vector.app.features.spaces.manage.ManageType import im.vector.app.features.spaces.manage.ManageType
import im.vector.app.features.spaces.manage.SpaceManageActivity import im.vector.app.features.spaces.manage.SpaceManageActivity
@ -79,10 +78,6 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
views.spaceBetaTag.debouncedClicks {
bugReporter.openBugReportScreen(requireActivity(), ReportType.SPACE_BETA_FEEDBACK)
}
views.invitePeople.views.bottomSheetActionClickableZone.debouncedClicks { views.invitePeople.views.bottomSheetActionClickableZone.debouncedClicks {
dismiss() dismiss()
interactionListener?.onShareSpaceSelected(spaceArgs.spaceId) interactionListener?.onShareSpaceSelected(spaceArgs.spaceId)
@ -106,6 +101,11 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
startActivity(SpaceManageActivity.newIntent(requireActivity(), spaceArgs.spaceId, ManageType.AddRooms)) startActivity(SpaceManageActivity.newIntent(requireActivity(), spaceArgs.spaceId, ManageType.AddRooms))
} }
views.addSpaces.views.bottomSheetActionClickableZone.debouncedClicks {
dismiss()
startActivity(SpaceManageActivity.newIntent(requireActivity(), spaceArgs.spaceId, ManageType.AddRoomsOnlySpaces))
}
views.leaveSpace.views.bottomSheetActionClickableZone.debouncedClicks { views.leaveSpace.views.bottomSheetActionClickableZone.debouncedClicks {
LeaveSpaceBottomSheet.newInstance(spaceArgs.spaceId).show(childFragmentManager, "LOGOUT") LeaveSpaceBottomSheet.newInstance(spaceArgs.spaceId).show(childFragmentManager, "LOGOUT")
} }
@ -128,6 +128,7 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
views.invitePeople.isVisible = state.canInvite || state.spaceSummary?.isPublic.orFalse() views.invitePeople.isVisible = state.canInvite || state.spaceSummary?.isPublic.orFalse()
views.addRooms.isVisible = state.canAddChild views.addRooms.isVisible = state.canAddChild
views.addSpaces.isVisible = state.canAddChild
} }
companion object { companion object {

View file

@ -34,7 +34,7 @@ class WizardButtonView @JvmOverloads constructor(context: Context, attrs: Attrib
private val views: ViewSpaceTypeButtonBinding private val views: ViewSpaceTypeButtonBinding
var title: String? = null var title: CharSequence? = null
set(value) { set(value) {
if (value != title) { if (value != title) {
field = value field = value

View file

@ -44,6 +44,7 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.matrixto.SpaceCardRenderer import im.vector.app.features.matrixto.SpaceCardRenderer
import im.vector.app.features.permalink.PermalinkHandler import im.vector.app.features.permalink.PermalinkHandler
import im.vector.app.features.spaces.manage.ManageType import im.vector.app.features.spaces.manage.ManageType
import im.vector.app.features.spaces.manage.SpaceAddRoomSpaceChooserBottomSheet
import im.vector.app.features.spaces.manage.SpaceManageActivity import im.vector.app.features.spaces.manage.SpaceManageActivity
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
@ -75,6 +76,27 @@ class SpaceDirectoryFragment @Inject constructor(
private val viewModel by activityViewModel(SpaceDirectoryViewModel::class) private val viewModel by activityViewModel(SpaceDirectoryViewModel::class)
private val epoxyVisibilityTracker = EpoxyVisibilityTracker() private val epoxyVisibilityTracker = EpoxyVisibilityTracker()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
childFragmentManager.setFragmentResultListener(SpaceAddRoomSpaceChooserBottomSheet.REQUEST_KEY, this) { _, bundle ->
bundle.getString(SpaceAddRoomSpaceChooserBottomSheet.BUNDLE_KEY_ACTION)?.let { action ->
val spaceId = withState(viewModel) { it.spaceId }
when (action) {
SpaceAddRoomSpaceChooserBottomSheet.ACTION_ADD_ROOMS -> {
addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRooms))
}
SpaceAddRoomSpaceChooserBottomSheet.ACTION_ADD_SPACES -> {
addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRoomsOnlySpaces))
}
else -> {
// nop
}
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -170,7 +192,7 @@ class SpaceDirectoryFragment @Inject constructor(
} }
override fun addExistingRooms(spaceId: String) { override fun addExistingRooms(spaceId: String) {
addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRooms)) SpaceAddRoomSpaceChooserBottomSheet.newInstance().show(childFragmentManager, "SpaceAddRoomSpaceChooserBottomSheet")
} }
override fun loadAdditionalItemsIfNeeded() { override fun loadAdditionalItemsIfNeeded() {

View file

@ -27,6 +27,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.appcompat.queryTextChanges import com.jakewharton.rxbinding3.appcompat.queryTextChanges
import im.vector.app.R import im.vector.app.R
@ -112,9 +113,23 @@ class SpaceAddRoomFragment @Inject constructor(
dmEpoxyController.disabled = !it dmEpoxyController.disabled = !it
}.disposeOnDestroyView() }.disposeOnDestroyView()
viewModel.selectSubscribe(this, SpaceAddRoomsState::onlyShowSpaces) {
spaceEpoxyController.disabled = !it
roomEpoxyController.disabled = it
views.createNewRoom.text = if (it) getString(R.string.create_space) else getString(R.string.create_new_room)
val title = if (it) getString(R.string.space_add_existing_spaces) else getString(R.string.space_add_existing_rooms_only)
views.appBarTitle.text = title
}.disposeOnDestroyView()
views.createNewRoom.debouncedClicks { views.createNewRoom.debouncedClicks {
withState(viewModel) { state ->
if (state.onlyShowSpaces) {
sharedViewModel.handle(SpaceManagedSharedAction.CreateSpace)
} else {
sharedViewModel.handle(SpaceManagedSharedAction.CreateRoom) sharedViewModel.handle(SpaceManagedSharedAction.CreateRoom)
} }
}
}
viewModel.observeViewEvents { viewModel.observeViewEvents {
when (it) { when (it) {

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.spaces.manage
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.setFragmentResult
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetAddRoomsOrSpacesToSpaceBinding
class SpaceAddRoomSpaceChooserBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetAddRoomsOrSpacesToSpaceBinding>() {
override val showExpanded = true
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
BottomSheetAddRoomsOrSpacesToSpaceBinding.inflate(inflater, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
views.addSpaces.views.bottomSheetActionClickableZone.debouncedClicks {
setFragmentResult(REQUEST_KEY, Bundle().apply {
putString(BUNDLE_KEY_ACTION, ACTION_ADD_SPACES)
})
dismiss()
}
views.addRooms.views.bottomSheetActionClickableZone.debouncedClicks {
setFragmentResult(REQUEST_KEY, Bundle().apply {
putString(BUNDLE_KEY_ACTION, ACTION_ADD_ROOMS)
})
dismiss()
}
}
companion object {
const val REQUEST_KEY = "SpaceAddRoomSpaceChooserBottomSheet"
const val BUNDLE_KEY_ACTION = "SpaceAddRoomSpaceChooserBottomSheet.Action"
const val ACTION_ADD_ROOMS = "Action.AddRoom"
const val ACTION_ADD_SPACES = "Action.AddSpaces"
fun newInstance()
: SpaceAddRoomSpaceChooserBottomSheet {
return SpaceAddRoomSpaceChooserBottomSheet()
}
}
}

View file

@ -27,10 +27,12 @@ data class SpaceAddRoomsState(
val spaceName: String = "", val spaceName: String = "",
val ignoreRooms: List<String> = emptyList(), val ignoreRooms: List<String> = emptyList(),
val isSaving: Async<List<String>> = Uninitialized, val isSaving: Async<List<String>> = Uninitialized,
val shouldShowDMs : Boolean = false val shouldShowDMs: Boolean = false,
val onlyShowSpaces: Boolean = false
// val selectionList: Map<String, Boolean> = emptyMap() // val selectionList: Map<String, Boolean> = emptyMap()
) : MvRxState { ) : MvRxState {
constructor(args: SpaceManageArgs) : this( constructor(args: SpaceManageArgs) : this(
spaceId = args.spaceId spaceId = args.spaceId,
onlyShowSpaces = args.manageType == ManageType.AddRoomsOnlySpaces
) )
} }

View file

@ -127,7 +127,7 @@ class SpaceAddRoomsViewModel @AssistedInject constructor(
copy( copy(
spaceName = spaceSummary?.displayName ?: "", spaceName = spaceSummary?.displayName ?: "",
ignoreRooms = (spaceSummary?.flattenParentIds ?: emptyList()) + listOf(initialState.spaceId), ignoreRooms = (spaceSummary?.flattenParentIds ?: emptyList()) + listOf(initialState.spaceId),
shouldShowDMs = spaceSummary?.isPublic == false shouldShowDMs = !onlyShowSpaces && spaceSummary?.isPublic == false
) )
} }
} }

View file

@ -99,7 +99,8 @@ class SpaceManageActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>(),
if (isFirstCreation()) { if (isFirstCreation()) {
withState(sharedViewModel) { withState(sharedViewModel) {
when (it.manageType) { when (it.manageType) {
ManageType.AddRooms -> { ManageType.AddRooms,
ManageType.AddRoomsOnlySpaces -> {
val simpleName = SpaceAddRoomFragment::class.java.simpleName val simpleName = SpaceAddRoomFragment::class.java.simpleName
if (supportFragmentManager.findFragmentByTag(simpleName) == null) { if (supportFragmentManager.findFragmentByTag(simpleName) == null) {
supportFragmentManager.commitTransaction { supportFragmentManager.commitTransaction {
@ -148,6 +149,13 @@ class SpaceManageActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>(),
CreateRoomArgs("", parentSpaceId = args?.spaceId) CreateRoomArgs("", parentSpaceId = args?.spaceId)
) )
} }
SpaceManagedSharedViewEvents.NavigateToCreateSpace -> {
addFragmentToBackstack(
R.id.simpleFragmentContainer,
CreateRoomFragment::class.java,
CreateRoomArgs("", parentSpaceId = args?.spaceId, isSpace = true)
)
}
SpaceManagedSharedViewEvents.NavigateToManageRooms -> { SpaceManagedSharedViewEvents.NavigateToManageRooms -> {
args?.spaceId?.let { spaceId -> args?.spaceId?.let { spaceId ->
addFragmentToBackstack( addFragmentToBackstack(

View file

@ -23,6 +23,7 @@ import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
@ -55,9 +56,10 @@ class SpaceManageSharedViewModel @AssistedInject constructor(
SpaceManagedSharedAction.HideLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.HideLoading) SpaceManagedSharedAction.HideLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.HideLoading)
SpaceManagedSharedAction.ShowLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.ShowLoading) SpaceManagedSharedAction.ShowLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.ShowLoading)
SpaceManagedSharedAction.CreateRoom -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToCreateRoom) SpaceManagedSharedAction.CreateRoom -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToCreateRoom)
SpaceManagedSharedAction.CreateSpace -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToCreateSpace)
SpaceManagedSharedAction.ManageRooms -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToManageRooms) SpaceManagedSharedAction.ManageRooms -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToManageRooms)
SpaceManagedSharedAction.OpenSpaceAliasesSettings -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToAliasSettings) SpaceManagedSharedAction.OpenSpaceAliasesSettings -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToAliasSettings)
SpaceManagedSharedAction.OpenSpacePermissionSettings -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToPermissionSettings) SpaceManagedSharedAction.OpenSpacePermissionSettings -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToPermissionSettings)
} }.exhaustive
} }
} }

View file

@ -20,6 +20,7 @@ import com.airbnb.mvrx.MvRxState
enum class ManageType { enum class ManageType {
AddRooms, AddRooms,
AddRoomsOnlySpaces,
Settings, Settings,
ManageRooms ManageRooms
} }

View file

@ -23,6 +23,7 @@ sealed class SpaceManagedSharedAction : VectorViewModelAction {
object ShowLoading : SpaceManagedSharedAction() object ShowLoading : SpaceManagedSharedAction()
object HideLoading : SpaceManagedSharedAction() object HideLoading : SpaceManagedSharedAction()
object CreateRoom : SpaceManagedSharedAction() object CreateRoom : SpaceManagedSharedAction()
object CreateSpace : SpaceManagedSharedAction()
object ManageRooms : SpaceManagedSharedAction() object ManageRooms : SpaceManagedSharedAction()
object OpenSpaceAliasesSettings : SpaceManagedSharedAction() object OpenSpaceAliasesSettings : SpaceManagedSharedAction()
object OpenSpacePermissionSettings : SpaceManagedSharedAction() object OpenSpacePermissionSettings : SpaceManagedSharedAction()

View file

@ -23,6 +23,7 @@ sealed class SpaceManagedSharedViewEvents : VectorViewEvents {
object ShowLoading : SpaceManagedSharedViewEvents() object ShowLoading : SpaceManagedSharedViewEvents()
object HideLoading : SpaceManagedSharedViewEvents() object HideLoading : SpaceManagedSharedViewEvents()
object NavigateToCreateRoom : SpaceManagedSharedViewEvents() object NavigateToCreateRoom : SpaceManagedSharedViewEvents()
object NavigateToCreateSpace : SpaceManagedSharedViewEvents()
object NavigateToManageRooms : SpaceManagedSharedViewEvents() object NavigateToManageRooms : SpaceManagedSharedViewEvents()
object NavigateToAliasSettings : SpaceManagedSharedViewEvents() object NavigateToAliasSettings : SpaceManagedSharedViewEvents()
object NavigateToPermissionSettings : SpaceManagedSharedViewEvents() object NavigateToPermissionSettings : SpaceManagedSharedViewEvents()

View file

@ -25,7 +25,6 @@ import im.vector.app.core.resources.StringProvider
import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formEditTextItem
import im.vector.app.features.form.formEditableSquareAvatarItem import im.vector.app.features.form.formEditableSquareAvatarItem
import im.vector.app.features.form.formMultiLineEditTextItem import im.vector.app.features.form.formMultiLineEditTextItem
import im.vector.app.features.form.formSwitchItem
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.roomprofile.settings.RoomSettingsViewState import im.vector.app.features.roomprofile.settings.RoomSettingsViewState
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
@ -106,27 +105,35 @@ class SpaceSettingsController @Inject constructor(
} }
val isPublic = (data.newRoomJoinRules.newJoinRules ?: data.currentRoomJoinRules) == RoomJoinRules.PUBLIC val isPublic = (data.newRoomJoinRules.newJoinRules ?: data.currentRoomJoinRules) == RoomJoinRules.PUBLIC
if (vectorPreferences.labsUseExperimentalRestricted()) {
buildProfileAction( buildProfileAction(
id = "joinRule", id = "joinRule",
title = stringProvider.getString(R.string.room_settings_room_access_title), title = stringProvider.getString(R.string.room_settings_space_access_title),
subtitle = data.getJoinRuleWording(stringProvider), subtitle = data.getJoinRuleWording(stringProvider),
divider = false, divider = true,
editable = data.actionPermissions.canChangeJoinRule, editable = data.actionPermissions.canChangeJoinRule,
action = { if (data.actionPermissions.canChangeJoinRule) callback?.onJoinRuleClicked() } action = { if (data.actionPermissions.canChangeJoinRule) callback?.onJoinRuleClicked() }
) )
} else { // if (vectorPreferences.labsUseExperimentalRestricted()) {
formSwitchItem { // buildProfileAction(
id("isPublic") // id = "joinRule",
enabled(data.actionPermissions.canChangeJoinRule) // title = stringProvider.getString(R.string.room_settings_room_access_title),
title(host.stringProvider.getString(R.string.make_this_space_public)) // subtitle = data.getJoinRuleWording(stringProvider),
switchChecked(isPublic) // divider = false,
// editable = data.actionPermissions.canChangeJoinRule,
listener { value -> // action = { if (data.actionPermissions.canChangeJoinRule) callback?.onJoinRuleClicked() }
host.callback?.setIsPublic(value) // )
} // } else {
} // formSwitchItem {
} // id("isPublic")
// enabled(data.actionPermissions.canChangeJoinRule)
// title(host.stringProvider.getString(R.string.make_this_space_public))
// switchChecked(isPublic)
//
// listener { value ->
// host.callback?.setIsPublic(value)
// }
// }
// }
dividerItem { dividerItem {
id("divider") id("divider")
} }

View file

@ -47,7 +47,7 @@ import im.vector.app.features.roomprofile.settings.RoomSettingsAction
import im.vector.app.features.roomprofile.settings.RoomSettingsViewEvents import im.vector.app.features.roomprofile.settings.RoomSettingsViewEvents
import im.vector.app.features.roomprofile.settings.RoomSettingsViewModel import im.vector.app.features.roomprofile.settings.RoomSettingsViewModel
import im.vector.app.features.roomprofile.settings.RoomSettingsViewState import im.vector.app.features.roomprofile.settings.RoomSettingsViewState
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleActivity
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel
import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.GuestAccess
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
@ -133,12 +133,6 @@ class SpaceSettingsFragment @Inject constructor(
state.roomSummary()?.let { state.roomSummary()?.let {
views.roomSettingsToolbarTitleView.text = it.displayName views.roomSettingsToolbarTitleView.text = it.displayName
views.roomSettingsToolbarTitleView.setCompoundDrawablesWithIntrinsicBounds(
null,
null,
drawableProvider.getDrawable(R.drawable.ic_beta_pill),
null
)
avatarRenderer.render(it.toMatrixItem(), views.roomSettingsToolbarAvatarImageView) avatarRenderer.render(it.toMatrixItem(), views.roomSettingsToolbarAvatarImageView)
views.roomSettingsDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel) views.roomSettingsDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel)
} }
@ -199,10 +193,8 @@ class SpaceSettingsFragment @Inject constructor(
// N/A for space settings screen // N/A for space settings screen
} }
override fun onJoinRuleClicked() = withState(viewModel) { state -> override fun onJoinRuleClicked() {
val currentJoinRule = state.newRoomJoinRules.newJoinRules ?: state.currentRoomJoinRules startActivity(RoomJoinRuleActivity.newIntent(requireContext(), roomProfileArgs.roomId))
RoomJoinRuleBottomSheet.newInstance(currentJoinRule)
.show(childFragmentManager, "RoomJoinRuleBottomSheet")
} }
override fun onToggleGuestAccess() = withState(viewModel) { state -> override fun onToggleGuestAccess() = withState(viewModel) { state ->

View file

@ -0,0 +1,45 @@
<?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="?android:colorBackground"
android:orientation="vertical">
<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="16dp"
android:gravity="center"
android:text="@string/space_add_existing_rooms"
android:textColor="?vctr_content_primary"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent" />
<im.vector.app.core.ui.views.BottomSheetActionButton
android:id="@+id/addRooms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:actionTitle="@string/space_add_child_title"
app:leftIcon="@drawable/ic_fab_add"
app:tint="?vctr_content_primary"
app:titleTextColor="?vctr_content_primary"
tools:actionDescription="" />
<im.vector.app.core.ui.views.BottomSheetActionButton
android:id="@+id/addSpaces"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:actionTitle="@string/add_space"
app:betaAction="true"
app:leftIcon="@drawable/ic_fab_add"
app:tint="?vctr_content_primary"
app:titleTextColor="?vctr_content_primary"
tools:actionDescription="" />
</LinearLayout>

View file

@ -38,21 +38,12 @@
android:textStyle="bold" android:textStyle="bold"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/spaceDescription" app:layout_constraintBottom_toTopOf="@+id/spaceDescription"
app:layout_constraintEnd_toStartOf="@id/spaceBetaTag" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/spaceAvatarImageView" app:layout_constraintStart_toEndOf="@id/spaceAvatarImageView"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" app:layout_constraintVertical_chainStyle="packed"
tools:text="@sample/spaces.json/data/name" /> tools:text="@sample/spaces.json/data/name" />
<ImageView
android:id="@+id/spaceBetaTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:contentDescription="@string/a11y_beta"
android:src="@drawable/ic_beta_pill"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/spaceDescription" android:id="@+id/spaceDescription"
@ -129,6 +120,16 @@
app:titleTextColor="?vctr_content_primary" app:titleTextColor="?vctr_content_primary"
tools:actionDescription="" /> tools:actionDescription="" />
<im.vector.app.core.ui.views.BottomSheetActionButton
android:id="@+id/addSpaces"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:actionTitle="@string/add_space"
app:leftIcon="@drawable/ic_fab_add"
app:tint="?vctr_content_primary"
app:titleTextColor="?vctr_content_primary"
app:betaAction="true"
tools:actionDescription="" />
<im.vector.app.core.ui.views.BottomSheetActionButton <im.vector.app.core.ui.views.BottomSheetActionButton
android:id="@+id/leaveSpace" android:id="@+id/leaveSpace"

View file

@ -28,18 +28,6 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:src="@sample/room_round_avatars" /> tools:src="@sample/room_round_avatars" />
<ImageView
android:id="@+id/matrixToBetaTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:contentDescription="@string/a11y_beta"
android:src="@drawable/ic_beta_pill"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<TextView <TextView
android:id="@+id/matrixToCardNameText" android:id="@+id/matrixToCardNameText"
style="@style/Widget.Vector.TextView.Subtitle" style="@style/Widget.Vector.TextView.Subtitle"

View file

@ -12,7 +12,7 @@
<ImageView <ImageView
android:id="@+id/itemGenericPillImage" android:id="@+id/itemGenericPillImage"
android:layout_width="20dp" android:layout_width="wrap_content"
android:layout_height="20dp" android:layout_height="20dp"
android:importantForAccessibility="no" android:importantForAccessibility="no"
android:src="@drawable/ic_info" android:src="@drawable/ic_info"

View file

@ -15,8 +15,7 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:text="@string/spaces_header" android:text="@string/spaces_header"
android:textColor="?vctr_content_primary" android:textColor="?vctr_content_primary"
android:textStyle="bold" android:textStyle="bold" />
app:drawableEndCompat="@drawable/ic_beta_pill" />
<TextView <TextView
style="@style/Widget.Vector.TextView.Body" style="@style/Widget.Vector.TextView.Body"

View file

@ -45,7 +45,7 @@
android:textColor="?colorPrimary" android:textColor="?colorPrimary"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/bottomSheetActionSubTitle" app:layout_constraintBottom_toTopOf="@+id/bottomSheetActionSubTitle"
app:layout_constraintEnd_toStartOf="@+id/bottomSheetActionIcon" app:layout_constraintEnd_toStartOf="@+id/bottomSheetActionBeta"
app:layout_constraintStart_toEndOf="@+id/bottomSheetActionLeftIcon" app:layout_constraintStart_toEndOf="@+id/bottomSheetActionLeftIcon"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" app:layout_constraintVertical_chainStyle="packed"
@ -62,12 +62,23 @@
android:visibility="gone" android:visibility="gone"
app:layout_constrainedWidth="true" app:layout_constrainedWidth="true"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/bottomSheetActionIcon" app:layout_constraintEnd_toStartOf="@+id/bottomSheetActionBeta"
app:layout_constraintStart_toStartOf="@+id/bottomSheetActionTitle" app:layout_constraintStart_toStartOf="@+id/bottomSheetActionTitle"
app:layout_constraintTop_toBottomOf="@+id/bottomSheetActionTitle" app:layout_constraintTop_toBottomOf="@+id/bottomSheetActionTitle"
tools:text="For maximum security, do this in person" tools:text="For maximum security, do this in person"
tools:visibility="visible" /> tools:visibility="visible" />
<ImageView
android:id="@+id/bottomSheetActionBeta"
android:layout_width="48dp"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/bottomSheetActionIcon"
app:layout_constraintStart_toEndOf="@+id/bottomSheetActionTitle"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/ic_beta_pill" />
<ImageView <ImageView
android:id="@+id/bottomSheetActionIcon" android:id="@+id/bottomSheetActionIcon"
android:layout_width="48dp" android:layout_width="48dp"

View file

@ -1476,7 +1476,9 @@
<string name="room_settings_room_read_history_rules_pref_dialog_title">Who can read history?</string> <string name="room_settings_room_read_history_rules_pref_dialog_title">Who can read history?</string>
<string name="room_settings_room_read_history_dialog_subtitle">Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.</string> <string name="room_settings_room_read_history_dialog_subtitle">Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.</string>
<string name="room_settings_room_access_rules_pref_dialog_title">Who can access this room?</string> <string name="room_settings_room_access_rules_pref_dialog_title">Who can access this room?</string>
<string name="room_settings_access_rules_pref_dialog_title">Who can access?</string>
<string name="room_settings_room_access_title">Room access</string> <string name="room_settings_room_access_title">Room access</string>
<string name="room_settings_space_access_title">Space access</string>
<string name="room_settings_guest_access_title">Allow guests to join</string> <string name="room_settings_guest_access_title">Allow guests to join</string>
<!-- room settings : alias --> <!-- room settings : alias -->
@ -1533,6 +1535,7 @@
<string name="room_settings_room_access_private_description">Only people invited can find and join</string> <string name="room_settings_room_access_private_description">Only people invited can find and join</string>
<string name="room_settings_room_access_public_title">Public</string> <string name="room_settings_room_access_public_title">Public</string>
<string name="room_settings_room_access_public_description">Anyone can find the room and join</string> <string name="room_settings_room_access_public_description">Anyone can find the room and join</string>
<string name="room_settings_space_access_public_description">Anyone can find the space and join</string>
<string name="room_settings_room_access_restricted_title">Space members only</string> <string name="room_settings_room_access_restricted_title">Space members only</string>
<string name="room_settings_room_access_restricted_description">Anyone in a space with this room can find and join it. Only admins of this room can add it to a space.</string> <string name="room_settings_room_access_restricted_description">Anyone in a space with this room can find and join it. Only admins of this room can add it to a space.</string>
<string name="room_create_member_of_space_name_can_join">Members of Space %s can find, preview and join.</string> <string name="room_create_member_of_space_name_can_join">Members of Space %s can find, preview and join.</string>
@ -2187,6 +2190,7 @@
<string name="malformed_message">Malformed event, cannot display</string> <string name="malformed_message">Malformed event, cannot display</string>
<string name="create_new_room">Create New Room</string> <string name="create_new_room">Create New Room</string>
<string name="create_new_space">Create New Space</string>
<string name="error_no_network">No network. Please check your Internet connection.</string> <string name="error_no_network">No network. Please check your Internet connection.</string>
<string name="action_change">"Change"</string> <string name="action_change">"Change"</string>
<string name="change_room_directory_network">"Change network"</string> <string name="change_room_directory_network">"Change network"</string>
@ -2672,6 +2676,7 @@
<string name="create_room_alias_empty">Please provide a room address</string> <string name="create_room_alias_empty">Please provide a room address</string>
<string name="create_room_alias_invalid">Some characters are not allowed</string> <string name="create_room_alias_invalid">Some characters are not allowed</string>
<string name="create_room_in_progress">Creating room…</string> <string name="create_room_in_progress">Creating room…</string>
<string name="create_space_in_progress">Creating space…</string>
<string name="login_error_threepid_denied">Your email domain is not authorized to register on this server</string> <string name="login_error_threepid_denied">Your email domain is not authorized to register on this server</string>
@ -3493,6 +3498,9 @@
<string name="pick_tings_to_leave">Pick things to leave</string> <string name="pick_tings_to_leave">Pick things to leave</string>
<string name="space_add_existing_rooms">Add existing rooms and space</string> <string name="space_add_existing_rooms">Add existing rooms and space</string>
<string name="space_add_existing_rooms_only">Add existing rooms</string>
<string name="space_add_existing_spaces">Add existing spaces</string>
<string name="space_add_space_to_any_space_you_manage">Add a space to any space you manage.</string>
<string name="space_add_rooms">Add rooms</string> <string name="space_add_rooms">Add rooms</string>
<string name="spaces_beta_welcome_to_spaces">Welcome to Spaces!</string> <string name="spaces_beta_welcome_to_spaces">Welcome to Spaces!</string>
<string name="spaces_beta_welcome_to_spaces_desc">Spaces are a new way to group rooms and people.</string> <string name="spaces_beta_welcome_to_spaces_desc">Spaces are a new way to group rooms and people.</string>