Unverified Commit d81d971c authored by Valere's avatar Valere Committed by GitHub
Browse files

Merge pull request #3302 from vector-im/feature/bca/spaces_admin_manage

Feature/bca/spaces admin manage
parents e6d4f9a1 723f7cc3
......@@ -29,5 +29,6 @@ data class SpaceChildInfo(
val activeMemberCount: Int?,
val autoJoin: Boolean,
val viaServers: List<String>,
val parentRoomId: String?
val parentRoomId: String?,
val suggested: Boolean?
)
......@@ -46,5 +46,8 @@ interface Space {
@Throws
suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean)
@Throws
suspend fun setChildrenSuggested(roomId: String, suggested: Boolean)
// fun getChildren() : List<IRoomSummary>
}
......@@ -269,5 +269,9 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
obj.setString(RoomSummaryEntityFields.JOIN_RULES_STR, roomJoinRules?.name)
}
realm.schema.get("SpaceChildSummaryEntity")
?.addField(SpaceChildSummaryEntityFields.SUGGESTED, Boolean::class.java)
?.setNullable(SpaceChildSummaryEntityFields.SUGGESTED, true)
}
}
......@@ -89,7 +89,8 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
order = it.order,
autoJoin = it.autoJoin ?: false,
viaServers = it.viaServers.toList(),
parentRoomId = roomSummaryEntity.roomId
parentRoomId = roomSummaryEntity.roomId,
suggested = it.suggested
)
},
flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList()
......
......@@ -29,6 +29,8 @@ internal open class SpaceChildSummaryEntity(
var autoJoin: Boolean? = null,
var suggested: Boolean? = null,
var childRoomId: String? = null,
// Link to the actual space summary if it is known locally
var childSummaryEntity: RoomSummaryEntity? = null,
......
......@@ -63,20 +63,21 @@ internal class DefaultSpace(
}
override suspend fun removeChildren(roomId: String) {
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
.firstOrNull()
?.content.toModel<SpaceChildContent>()
?: // should we throw here?
return
// val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
// .firstOrNull()
// ?.content.toModel<SpaceChildContent>()
// ?: // should we throw here?
// return
// edit state event and set via to null
room.sendStateEvent(
eventType = EventType.STATE_SPACE_CHILD,
stateKey = roomId,
body = SpaceChildContent(
order = existing.order,
order = null,
via = null,
autoJoin = existing.autoJoin
autoJoin = null,
suggested = null
).toContent()
)
}
......@@ -94,7 +95,8 @@ internal class DefaultSpace(
body = SpaceChildContent(
order = order,
via = existing.via,
autoJoin = existing.autoJoin
autoJoin = existing.autoJoin,
suggested = existing.suggested
).toContent()
)
}
......@@ -105,6 +107,34 @@ internal class DefaultSpace(
?.content.toModel<SpaceChildContent>()
?: throw IllegalArgumentException("$roomId is not a child of this space")
if (existing.autoJoin == autoJoin) {
// nothing to do?
return
}
// edit state event and set via to null
room.sendStateEvent(
eventType = EventType.STATE_SPACE_CHILD,
stateKey = roomId,
body = SpaceChildContent(
order = existing.order,
via = existing.via,
autoJoin = autoJoin,
suggested = existing.suggested
).toContent()
)
}
override suspend fun setChildrenSuggested(roomId: String, suggested: Boolean) {
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
.firstOrNull()
?.content.toModel<SpaceChildContent>()
?: throw IllegalArgumentException("$roomId is not a child of this space")
if (existing.suggested == suggested) {
// nothing to do?
return
}
// edit state event and set via to null
room.sendStateEvent(
eventType = EventType.STATE_SPACE_CHILD,
......@@ -112,7 +142,8 @@ internal class DefaultSpace(
body = SpaceChildContent(
order = existing.order,
via = existing.via,
autoJoin = autoJoin
autoJoin = existing.autoJoin,
suggested = suggested
).toContent()
)
}
......
......@@ -145,7 +145,8 @@ internal class DefaultSpaceService @Inject constructor(
autoJoin = childStateEvContent.autoJoin ?: false,
viaServers = childStateEvContent.via.orEmpty(),
activeMemberCount = childSummary.numJoinedMembers,
parentRoomId = childStateEv.roomId
parentRoomId = childStateEv.roomId,
suggested = childStateEvContent.suggested
)
}
}.orEmpty()
......
......@@ -125,6 +125,8 @@ import im.vector.app.features.spaces.create.CreateSpaceDefaultRoomsFragment
import im.vector.app.features.spaces.create.CreateSpaceDetailsFragment
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
import im.vector.app.features.spaces.manage.SpaceAddRoomFragment
import im.vector.app.features.spaces.manage.SpaceManageRoomsFragment
import im.vector.app.features.spaces.manage.SpaceSettingsFragment
import im.vector.app.features.spaces.people.SpacePeopleFragment
import im.vector.app.features.spaces.preview.SpacePreviewFragment
import im.vector.app.features.terms.ReviewTermsFragment
......@@ -684,4 +686,14 @@ interface FragmentModule {
@IntoMap
@FragmentKey(SpacePeopleFragment::class)
fun bindSpacePeopleFragment(fragment: SpacePeopleFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SpaceSettingsFragment::class)
fun bindSpaceSettingsFragment(fragment: SpaceSettingsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(SpaceManageRoomsFragment::class)
fun bindSpaceManageRoomsFragment(fragment: SpaceManageRoomsFragment): Fragment
}
/*
* 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.core.epoxy
import android.widget.CompoundButton
import com.google.android.material.switchmaterial.SwitchMaterial
import com.google.android.material.textfield.TextInputEditText
fun VectorEpoxyHolder.setValueOnce(textInputEditText: TextInputEditText, value: String?) {
if (view.isAttachedToWindow) {
// the view is attached to the window
// So it is a rebind of new data and you could ignore it assuming this is text that was already inputted into the view.
// Downside is if you ever wanted to programmatically change the content of the edit text while it is on screen you would not be able to
} else {
textInputEditText.setText(value)
}
}
fun VectorEpoxyHolder.setValueOnce(switchView: SwitchMaterial, switchChecked: Boolean, listener: CompoundButton.OnCheckedChangeListener) {
if (view.isAttachedToWindow) {
// the view is attached to the window
// So it is a rebind of new data and you could ignore it assuming this is value that was already inputted into the view.
} else {
switchView.isChecked = switchChecked
switchView.setOnCheckedChangeListener(listener)
}
}
......@@ -57,15 +57,3 @@ fun EditText.setupAsSearch(@DrawableRes searchIconRes: Int = R.drawable.ic_searc
return@OnTouchListener false
})
}
/**
* Update the edit text value, only if necessary and move the cursor to the end of the text
*/
fun EditText.setTextSafe(value: String?) {
if (value != null && text.toString() != value) {
setText(value)
// To fix jumping cursor to the start https://github.com/airbnb/epoxy/issues/426
// Note: there is still a known bug if deleting char in the middle of the text, by long pressing on the backspace button.
setSelection(value.length)
}
}
......@@ -27,7 +27,7 @@ import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextSafe
import im.vector.app.core.epoxy.setValueOnce
import im.vector.app.core.platform.SimpleTextWatcher
@EpoxyModelClass(layout = R.layout.item_form_text_input)
......@@ -60,7 +60,7 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
@EpoxyAttribute
var endIconMode: Int? = null
@EpoxyAttribute
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var onTextChange: ((String) -> Unit)? = null
private val onTextChangeListener = object : SimpleTextWatcher() {
......@@ -76,8 +76,8 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
holder.textInputLayout.error = errorMessage
holder.textInputLayout.endIconMode = endIconMode ?: TextInputLayout.END_ICON_NONE
// Update only if text is different and value is not null
holder.textInputEditText.setTextSafe(value)
holder.setValueOnce(holder.textInputEditText, value)
holder.textInputEditText.isEnabled = enabled
inputType?.let { holder.textInputEditText.inputType = it }
holder.textInputEditText.isSingleLine = singleLine ?: false
......
......@@ -26,7 +26,7 @@ import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextSafe
import im.vector.app.core.epoxy.setValueOnce
import im.vector.app.core.platform.SimpleTextWatcher
@EpoxyModelClass(layout = R.layout.item_form_text_input_with_button)
......@@ -61,8 +61,8 @@ abstract class FormEditTextWithButtonItem : VectorEpoxyModel<FormEditTextWithBut
holder.textInputLayout.isEnabled = enabled
holder.textInputLayout.hint = hint
// Update only if text is different
holder.textInputEditText.setTextSafe(value)
holder.setValueOnce(holder.textInputEditText, value)
holder.textInputEditText.isEnabled = enabled
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
......
......@@ -83,7 +83,6 @@ abstract class FormEditableSquareAvatarItem : EpoxyModelWithHolder<FormEditableS
override fun unbind(holder: Holder) {
avatarRenderer?.clear(holder.image)
GlideApp.with(holder.image).clear(holder.image)
super.unbind(holder)
}
......
......@@ -27,7 +27,7 @@ import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextSafe
import im.vector.app.core.epoxy.setValueOnce
import im.vector.app.core.platform.SimpleTextWatcher
@EpoxyModelClass(layout = R.layout.item_form_multiline_text_input)
......@@ -57,7 +57,7 @@ abstract class FormMultiLineEditTextItem : VectorEpoxyModel<FormMultiLineEditTex
@EpoxyAttribute
var typeFace: Typeface = Typeface.DEFAULT
@EpoxyAttribute
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var onTextChange: ((String) -> Unit)? = null
private val onTextChangeListener = object : SimpleTextWatcher() {
......@@ -76,8 +76,8 @@ abstract class FormMultiLineEditTextItem : VectorEpoxyModel<FormMultiLineEditTex
holder.textInputEditText.textSize = textSizeSp?.toFloat() ?: 14f
holder.textInputEditText.minLines = minLines
// Update only if text is different and value is not null
holder.textInputEditText.setTextSafe(value)
holder.setValueOnce(holder.textInputEditText, value)
holder.textInputEditText.isEnabled = enabled
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
......
......@@ -25,6 +25,7 @@ import com.google.android.material.switchmaterial.SwitchMaterial
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.epoxy.setValueOnce
import im.vector.app.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_form_switch)
......@@ -40,7 +41,7 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
var switchChecked: Boolean = false
@EpoxyAttribute
var title: String? = null
var title: CharSequence? = null
@EpoxyAttribute
var summary: String? = null
......@@ -61,11 +62,10 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
holder.switchView.isEnabled = enabled
holder.switchView.setOnCheckedChangeListener(null)
holder.switchView.isChecked = switchChecked
holder.switchView.setOnCheckedChangeListener { _, isChecked ->
holder.setValueOnce(holder.switchView, switchChecked) { _, isChecked ->
listener?.invoke(isChecked)
}
holder.divider.isVisible = showDivider
}
......
......@@ -74,6 +74,7 @@ import im.vector.app.features.share.SharedData
import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet
import im.vector.app.features.spaces.SpaceExploreActivity
import im.vector.app.features.spaces.SpacePreviewActivity
import im.vector.app.features.spaces.manage.ManageType
import im.vector.app.features.spaces.manage.SpaceManageActivity
import im.vector.app.features.spaces.people.SpacePeopleActivity
import im.vector.app.features.terms.ReviewTermsActivity
......@@ -123,7 +124,7 @@ class DefaultNavigator @Inject constructor(
}
}
Navigator.PostSwitchSpaceAction.OpenAddExistingRooms -> {
startActivity(context, SpaceManageActivity.newIntent(context, spaceId), false)
startActivity(context, SpaceManageActivity.newIntent(context, spaceId, ManageType.AddRooms), false)
}
is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> {
val args = RoomDetailArgs(
......
......@@ -27,7 +27,7 @@ import com.google.android.material.textfield.TextInputLayout
import im.vector.app.R
import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.VectorEpoxyModel
import im.vector.app.core.extensions.setTextSafe
import im.vector.app.core.epoxy.setValueOnce
import im.vector.app.core.platform.SimpleTextWatcher
@EpoxyModelClass(layout = R.layout.item_room_alias_text_input)
......@@ -62,8 +62,7 @@ abstract class RoomAliasEditItem : VectorEpoxyModel<RoomAliasEditItem.Holder>()
holder.textInputLayout.isEnabled = enabled
holder.textInputLayout.error = errorMessage
// Update only if text is different and value is not null
holder.textInputEditText.setTextSafe(value)
holder.setValueOnce(holder.textInputEditText, value)
holder.textInputEditText.isEnabled = enabled
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
holder.homeServerText.text = homeServer
......
......@@ -121,7 +121,7 @@ class RoomSettingsController @Inject constructor(
buildProfileAction(
id = "joinRule",
title = stringProvider.getString(R.string.room_settings_room_access_title),
subtitle = data.getJoinRuleWording(),
subtitle = data.getJoinRuleWording(stringProvider),
dividerColor = dividerColor,
divider = false,
editable = data.actionPermissions.canChangeJoinRule,
......@@ -142,24 +142,4 @@ class RoomSettingsController @Inject constructor(
}
}
}
private fun RoomSettingsViewState.getJoinRuleWording(): String {
return when (val joinRule = newRoomJoinRules.newJoinRules ?: currentRoomJoinRules) {
RoomJoinRules.INVITE -> {
stringProvider.getString(R.string.room_settings_room_access_private_title)
}
RoomJoinRules.PUBLIC -> {
stringProvider.getString(R.string.room_settings_room_access_public_title)
}
RoomJoinRules.KNOCK -> {
stringProvider.getString(R.string.room_settings_room_access_entry_knock)
}
RoomJoinRules.RESTRICTED -> {
stringProvider.getString(R.string.room_settings_room_access_restricted_title)
}
else -> {
stringProvider.getString(R.string.room_settings_room_access_entry_unknown, joinRule.value)
}
}
}
}
......@@ -60,7 +60,8 @@ class RoomSettingsFragment @Inject constructor(
VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
RoomSettingsController.Callback,
OnBackPressed,
GalleryOrCameraDialogHelper.Listener {
GalleryOrCameraDialogHelper.Listener,
RoomSettingsViewModel.Factory {
private val viewModel: RoomSettingsViewModel by fragmentViewModel()
private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel
......@@ -76,6 +77,10 @@ class RoomSettingsFragment @Inject constructor(
override fun getMenuRes() = R.menu.vector_room_settings
override fun create(initialState: RoomSettingsViewState): RoomSettingsViewModel {
return viewModelFactory.create(initialState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
......
......@@ -17,6 +17,7 @@
package im.vector.app.features.roomprofile.settings
import androidx.core.net.toFile
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
......@@ -55,8 +56,11 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomSettingsViewState): RoomSettingsViewModel? {
val fragment: RoomSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.viewModelFactory.create(state)
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")
}
}
......@@ -123,7 +127,9 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
canChangeJoinRule = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
EventType.STATE_ROOM_JOIN_RULES)
&& powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
EventType.STATE_ROOM_GUEST_ACCESS)
EventType.STATE_ROOM_GUEST_ACCESS),
canAddChildren = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
EventType.STATE_SPACE_CHILD)
)
setState { copy(actionPermissions = permissions) }
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment