Merge pull request #3302 from vector-im/feature/bca/spaces_admin_manage
Feature/bca/spaces admin manage
This commit is contained in:
commit
d81d971ce0
|
@ -29,5 +29,6 @@ data class SpaceChildInfo(
|
||||||
val activeMemberCount: Int?,
|
val activeMemberCount: Int?,
|
||||||
val autoJoin: Boolean,
|
val autoJoin: Boolean,
|
||||||
val viaServers: List<String>,
|
val viaServers: List<String>,
|
||||||
val parentRoomId: String?
|
val parentRoomId: String?,
|
||||||
|
val suggested: Boolean?
|
||||||
)
|
)
|
||||||
|
|
|
@ -46,5 +46,8 @@ interface Space {
|
||||||
@Throws
|
@Throws
|
||||||
suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean)
|
suspend fun setChildrenAutoJoin(roomId: String, autoJoin: Boolean)
|
||||||
|
|
||||||
|
@Throws
|
||||||
|
suspend fun setChildrenSuggested(roomId: String, suggested: Boolean)
|
||||||
|
|
||||||
// fun getChildren() : List<IRoomSummary>
|
// fun getChildren() : List<IRoomSummary>
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,5 +269,9 @@ class RealmSessionStoreMigration @Inject constructor() : RealmMigration {
|
||||||
|
|
||||||
obj.setString(RoomSummaryEntityFields.JOIN_RULES_STR, roomJoinRules?.name)
|
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,
|
order = it.order,
|
||||||
autoJoin = it.autoJoin ?: false,
|
autoJoin = it.autoJoin ?: false,
|
||||||
viaServers = it.viaServers.toList(),
|
viaServers = it.viaServers.toList(),
|
||||||
parentRoomId = roomSummaryEntity.roomId
|
parentRoomId = roomSummaryEntity.roomId,
|
||||||
|
suggested = it.suggested
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList()
|
flattenParentIds = roomSummaryEntity.flattenParentIds?.split("|") ?: emptyList()
|
||||||
|
|
|
@ -29,6 +29,8 @@ internal open class SpaceChildSummaryEntity(
|
||||||
|
|
||||||
var autoJoin: Boolean? = null,
|
var autoJoin: Boolean? = null,
|
||||||
|
|
||||||
|
var suggested: Boolean? = null,
|
||||||
|
|
||||||
var childRoomId: String? = null,
|
var childRoomId: String? = null,
|
||||||
// Link to the actual space summary if it is known locally
|
// Link to the actual space summary if it is known locally
|
||||||
var childSummaryEntity: RoomSummaryEntity? = null,
|
var childSummaryEntity: RoomSummaryEntity? = null,
|
||||||
|
|
|
@ -63,20 +63,21 @@ internal class DefaultSpace(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun removeChildren(roomId: String) {
|
override suspend fun removeChildren(roomId: String) {
|
||||||
val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
// val existing = room.getStateEvents(setOf(EventType.STATE_SPACE_CHILD), QueryStringValue.Equals(roomId))
|
||||||
.firstOrNull()
|
// .firstOrNull()
|
||||||
?.content.toModel<SpaceChildContent>()
|
// ?.content.toModel<SpaceChildContent>()
|
||||||
?: // should we throw here?
|
// ?: // should we throw here?
|
||||||
return
|
// return
|
||||||
|
|
||||||
// edit state event and set via to null
|
// edit state event and set via to null
|
||||||
room.sendStateEvent(
|
room.sendStateEvent(
|
||||||
eventType = EventType.STATE_SPACE_CHILD,
|
eventType = EventType.STATE_SPACE_CHILD,
|
||||||
stateKey = roomId,
|
stateKey = roomId,
|
||||||
body = SpaceChildContent(
|
body = SpaceChildContent(
|
||||||
order = existing.order,
|
order = null,
|
||||||
via = null,
|
via = null,
|
||||||
autoJoin = existing.autoJoin
|
autoJoin = null,
|
||||||
|
suggested = null
|
||||||
).toContent()
|
).toContent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -94,7 +95,8 @@ internal class DefaultSpace(
|
||||||
body = SpaceChildContent(
|
body = SpaceChildContent(
|
||||||
order = order,
|
order = order,
|
||||||
via = existing.via,
|
via = existing.via,
|
||||||
autoJoin = existing.autoJoin
|
autoJoin = existing.autoJoin,
|
||||||
|
suggested = existing.suggested
|
||||||
).toContent()
|
).toContent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -105,6 +107,11 @@ internal class DefaultSpace(
|
||||||
?.content.toModel<SpaceChildContent>()
|
?.content.toModel<SpaceChildContent>()
|
||||||
?: throw IllegalArgumentException("$roomId is not a child of this space")
|
?: 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
|
// edit state event and set via to null
|
||||||
room.sendStateEvent(
|
room.sendStateEvent(
|
||||||
eventType = EventType.STATE_SPACE_CHILD,
|
eventType = EventType.STATE_SPACE_CHILD,
|
||||||
|
@ -112,7 +119,31 @@ internal class DefaultSpace(
|
||||||
body = SpaceChildContent(
|
body = SpaceChildContent(
|
||||||
order = existing.order,
|
order = existing.order,
|
||||||
via = existing.via,
|
via = existing.via,
|
||||||
autoJoin = autoJoin
|
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,
|
||||||
|
stateKey = roomId,
|
||||||
|
body = SpaceChildContent(
|
||||||
|
order = existing.order,
|
||||||
|
via = existing.via,
|
||||||
|
autoJoin = existing.autoJoin,
|
||||||
|
suggested = suggested
|
||||||
).toContent()
|
).toContent()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,8 @@ internal class DefaultSpaceService @Inject constructor(
|
||||||
autoJoin = childStateEvContent.autoJoin ?: false,
|
autoJoin = childStateEvContent.autoJoin ?: false,
|
||||||
viaServers = childStateEvContent.via.orEmpty(),
|
viaServers = childStateEvContent.via.orEmpty(),
|
||||||
activeMemberCount = childSummary.numJoinedMembers,
|
activeMemberCount = childSummary.numJoinedMembers,
|
||||||
parentRoomId = childStateEv.roomId
|
parentRoomId = childStateEv.roomId,
|
||||||
|
suggested = childStateEvContent.suggested
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.orEmpty()
|
}.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.create.CreateSpaceDetailsFragment
|
||||||
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
|
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
|
||||||
import im.vector.app.features.spaces.manage.SpaceAddRoomFragment
|
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.people.SpacePeopleFragment
|
||||||
import im.vector.app.features.spaces.preview.SpacePreviewFragment
|
import im.vector.app.features.spaces.preview.SpacePreviewFragment
|
||||||
import im.vector.app.features.terms.ReviewTermsFragment
|
import im.vector.app.features.terms.ReviewTermsFragment
|
||||||
|
@ -684,4 +686,14 @@ interface FragmentModule {
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(SpacePeopleFragment::class)
|
@FragmentKey(SpacePeopleFragment::class)
|
||||||
fun bindSpacePeopleFragment(fragment: SpacePeopleFragment): Fragment
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.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
|
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.R
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
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
|
import im.vector.app.core.platform.SimpleTextWatcher
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_form_text_input)
|
@EpoxyModelClass(layout = R.layout.item_form_text_input)
|
||||||
|
@ -60,7 +60,7 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var endIconMode: Int? = null
|
var endIconMode: Int? = null
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
var onTextChange: ((String) -> Unit)? = null
|
var onTextChange: ((String) -> Unit)? = null
|
||||||
|
|
||||||
private val onTextChangeListener = object : SimpleTextWatcher() {
|
private val onTextChangeListener = object : SimpleTextWatcher() {
|
||||||
|
@ -76,8 +76,8 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
|
||||||
holder.textInputLayout.error = errorMessage
|
holder.textInputLayout.error = errorMessage
|
||||||
holder.textInputLayout.endIconMode = endIconMode ?: TextInputLayout.END_ICON_NONE
|
holder.textInputLayout.endIconMode = endIconMode ?: TextInputLayout.END_ICON_NONE
|
||||||
|
|
||||||
// Update only if text is different and value is not null
|
holder.setValueOnce(holder.textInputEditText, value)
|
||||||
holder.textInputEditText.setTextSafe(value)
|
|
||||||
holder.textInputEditText.isEnabled = enabled
|
holder.textInputEditText.isEnabled = enabled
|
||||||
inputType?.let { holder.textInputEditText.inputType = it }
|
inputType?.let { holder.textInputEditText.inputType = it }
|
||||||
holder.textInputEditText.isSingleLine = singleLine ?: false
|
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.R
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
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
|
import im.vector.app.core.platform.SimpleTextWatcher
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_form_text_input_with_button)
|
@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.isEnabled = enabled
|
||||||
holder.textInputLayout.hint = hint
|
holder.textInputLayout.hint = hint
|
||||||
|
|
||||||
// Update only if text is different
|
holder.setValueOnce(holder.textInputEditText, value)
|
||||||
holder.textInputEditText.setTextSafe(value)
|
|
||||||
holder.textInputEditText.isEnabled = enabled
|
holder.textInputEditText.isEnabled = enabled
|
||||||
|
|
||||||
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
|
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
|
||||||
|
|
|
@ -83,7 +83,6 @@ abstract class FormEditableSquareAvatarItem : EpoxyModelWithHolder<FormEditableS
|
||||||
|
|
||||||
override fun unbind(holder: Holder) {
|
override fun unbind(holder: Holder) {
|
||||||
avatarRenderer?.clear(holder.image)
|
avatarRenderer?.clear(holder.image)
|
||||||
GlideApp.with(holder.image).clear(holder.image)
|
|
||||||
super.unbind(holder)
|
super.unbind(holder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ import com.google.android.material.textfield.TextInputLayout
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
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
|
import im.vector.app.core.platform.SimpleTextWatcher
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_form_multiline_text_input)
|
@EpoxyModelClass(layout = R.layout.item_form_multiline_text_input)
|
||||||
|
@ -57,7 +57,7 @@ abstract class FormMultiLineEditTextItem : VectorEpoxyModel<FormMultiLineEditTex
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var typeFace: Typeface = Typeface.DEFAULT
|
var typeFace: Typeface = Typeface.DEFAULT
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
var onTextChange: ((String) -> Unit)? = null
|
var onTextChange: ((String) -> Unit)? = null
|
||||||
|
|
||||||
private val onTextChangeListener = object : SimpleTextWatcher() {
|
private val onTextChangeListener = object : SimpleTextWatcher() {
|
||||||
|
@ -76,8 +76,8 @@ abstract class FormMultiLineEditTextItem : VectorEpoxyModel<FormMultiLineEditTex
|
||||||
holder.textInputEditText.textSize = textSizeSp?.toFloat() ?: 14f
|
holder.textInputEditText.textSize = textSizeSp?.toFloat() ?: 14f
|
||||||
holder.textInputEditText.minLines = minLines
|
holder.textInputEditText.minLines = minLines
|
||||||
|
|
||||||
// Update only if text is different and value is not null
|
holder.setValueOnce(holder.textInputEditText, value)
|
||||||
holder.textInputEditText.setTextSafe(value)
|
|
||||||
holder.textInputEditText.isEnabled = enabled
|
holder.textInputEditText.isEnabled = enabled
|
||||||
|
|
||||||
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
|
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import com.google.android.material.switchmaterial.SwitchMaterial
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.app.core.epoxy.setValueOnce
|
||||||
import im.vector.app.core.extensions.setTextOrHide
|
import im.vector.app.core.extensions.setTextOrHide
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_form_switch)
|
@EpoxyModelClass(layout = R.layout.item_form_switch)
|
||||||
|
@ -40,7 +41,7 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
|
||||||
var switchChecked: Boolean = false
|
var switchChecked: Boolean = false
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var title: String? = null
|
var title: CharSequence? = null
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var summary: String? = null
|
var summary: String? = null
|
||||||
|
@ -61,11 +62,10 @@ abstract class FormSwitchItem : VectorEpoxyModel<FormSwitchItem.Holder>() {
|
||||||
|
|
||||||
holder.switchView.isEnabled = enabled
|
holder.switchView.isEnabled = enabled
|
||||||
|
|
||||||
holder.switchView.setOnCheckedChangeListener(null)
|
holder.setValueOnce(holder.switchView, switchChecked) { _, isChecked ->
|
||||||
holder.switchView.isChecked = switchChecked
|
|
||||||
holder.switchView.setOnCheckedChangeListener { _, isChecked ->
|
|
||||||
listener?.invoke(isChecked)
|
listener?.invoke(isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.divider.isVisible = showDivider
|
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.InviteRoomSpaceChooserBottomSheet
|
||||||
import im.vector.app.features.spaces.SpaceExploreActivity
|
import im.vector.app.features.spaces.SpaceExploreActivity
|
||||||
import im.vector.app.features.spaces.SpacePreviewActivity
|
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.manage.SpaceManageActivity
|
||||||
import im.vector.app.features.spaces.people.SpacePeopleActivity
|
import im.vector.app.features.spaces.people.SpacePeopleActivity
|
||||||
import im.vector.app.features.terms.ReviewTermsActivity
|
import im.vector.app.features.terms.ReviewTermsActivity
|
||||||
|
@ -123,7 +124,7 @@ class DefaultNavigator @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Navigator.PostSwitchSpaceAction.OpenAddExistingRooms -> {
|
Navigator.PostSwitchSpaceAction.OpenAddExistingRooms -> {
|
||||||
startActivity(context, SpaceManageActivity.newIntent(context, spaceId), false)
|
startActivity(context, SpaceManageActivity.newIntent(context, spaceId, ManageType.AddRooms), false)
|
||||||
}
|
}
|
||||||
is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> {
|
is Navigator.PostSwitchSpaceAction.OpenDefaultRoom -> {
|
||||||
val args = RoomDetailArgs(
|
val args = RoomDetailArgs(
|
||||||
|
|
|
@ -27,7 +27,7 @@ import com.google.android.material.textfield.TextInputLayout
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
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
|
import im.vector.app.core.platform.SimpleTextWatcher
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_room_alias_text_input)
|
@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.isEnabled = enabled
|
||||||
holder.textInputLayout.error = errorMessage
|
holder.textInputLayout.error = errorMessage
|
||||||
|
|
||||||
// Update only if text is different and value is not null
|
holder.setValueOnce(holder.textInputEditText, value)
|
||||||
holder.textInputEditText.setTextSafe(value)
|
|
||||||
holder.textInputEditText.isEnabled = enabled
|
holder.textInputEditText.isEnabled = enabled
|
||||||
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
|
holder.textInputEditText.addTextChangedListener(onTextChangeListener)
|
||||||
holder.homeServerText.text = homeServer
|
holder.homeServerText.text = homeServer
|
||||||
|
|
|
@ -121,7 +121,7 @@ class RoomSettingsController @Inject constructor(
|
||||||
buildProfileAction(
|
buildProfileAction(
|
||||||
id = "joinRule",
|
id = "joinRule",
|
||||||
title = stringProvider.getString(R.string.room_settings_room_access_title),
|
title = stringProvider.getString(R.string.room_settings_room_access_title),
|
||||||
subtitle = data.getJoinRuleWording(),
|
subtitle = data.getJoinRuleWording(stringProvider),
|
||||||
dividerColor = dividerColor,
|
dividerColor = dividerColor,
|
||||||
divider = false,
|
divider = false,
|
||||||
editable = data.actionPermissions.canChangeJoinRule,
|
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>(),
|
VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
|
||||||
RoomSettingsController.Callback,
|
RoomSettingsController.Callback,
|
||||||
OnBackPressed,
|
OnBackPressed,
|
||||||
GalleryOrCameraDialogHelper.Listener {
|
GalleryOrCameraDialogHelper.Listener,
|
||||||
|
RoomSettingsViewModel.Factory {
|
||||||
|
|
||||||
private val viewModel: RoomSettingsViewModel by fragmentViewModel()
|
private val viewModel: RoomSettingsViewModel by fragmentViewModel()
|
||||||
private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel
|
private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel
|
||||||
|
@ -76,6 +77,10 @@ class RoomSettingsFragment @Inject constructor(
|
||||||
|
|
||||||
override fun getMenuRes() = R.menu.vector_room_settings
|
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?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
|
roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package im.vector.app.features.roomprofile.settings
|
package im.vector.app.features.roomprofile.settings
|
||||||
|
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
|
import com.airbnb.mvrx.ActivityViewModelContext
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
|
@ -55,8 +56,11 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
override fun create(viewModelContext: ViewModelContext, state: RoomSettingsViewState): RoomSettingsViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: RoomSettingsViewState): RoomSettingsViewModel? {
|
||||||
val fragment: RoomSettingsFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
val factory = when (viewModelContext) {
|
||||||
return fragment.viewModelFactory.create(state)
|
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,
|
canChangeJoinRule = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
|
||||||
EventType.STATE_ROOM_JOIN_RULES)
|
EventType.STATE_ROOM_JOIN_RULES)
|
||||||
&& powerLevelsHelper.isUserAllowedToSend(session.myUserId, true,
|
&& 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) }
|
setState { copy(actionPermissions = permissions) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import android.net.Uri
|
||||||
import com.airbnb.mvrx.Async
|
import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.features.roomprofile.RoomProfileArgs
|
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||||
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
|
||||||
|
@ -51,7 +53,8 @@ data class RoomSettingsViewState(
|
||||||
val canChangeName: Boolean = false,
|
val canChangeName: Boolean = false,
|
||||||
val canChangeTopic: Boolean = false,
|
val canChangeTopic: Boolean = false,
|
||||||
val canChangeHistoryVisibility: Boolean = false,
|
val canChangeHistoryVisibility: Boolean = false,
|
||||||
val canChangeJoinRule: Boolean = false
|
val canChangeJoinRule: Boolean = false,
|
||||||
|
val canAddChildren: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
sealed class AvatarAction {
|
sealed class AvatarAction {
|
||||||
|
@ -67,4 +70,24 @@ data class RoomSettingsViewState(
|
||||||
) {
|
) {
|
||||||
fun hasChanged() = newJoinRules != null || newGuestAccess != null
|
fun hasChanged() = newJoinRules != null || newGuestAccess != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getJoinRuleWording(stringProvider: StringProvider): 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import im.vector.app.features.navigation.Navigator
|
||||||
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
|
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
|
||||||
import im.vector.app.features.roomprofile.RoomProfileActivity
|
import im.vector.app.features.roomprofile.RoomProfileActivity
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
import im.vector.app.features.spaces.manage.ManageType
|
||||||
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 kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
@ -94,6 +95,13 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
|
||||||
val powerLevelsHelper = PowerLevelsHelper(powerLevelContent)
|
val powerLevelsHelper = PowerLevelsHelper(powerLevelContent)
|
||||||
val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId)
|
val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId)
|
||||||
val canAddChild = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD)
|
val canAddChild = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD)
|
||||||
|
|
||||||
|
val canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR)
|
||||||
|
val canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME)
|
||||||
|
val canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC)
|
||||||
|
|
||||||
|
views.spaceSettings.isVisible = canChangeAvatar || canChangeName || canChangeTopic
|
||||||
|
|
||||||
views.invitePeople.isVisible = canInvite
|
views.invitePeople.isVisible = canInvite
|
||||||
views.addRooms.isVisible = canAddChild
|
views.addRooms.isVisible = canAddChild
|
||||||
}.disposeOnDestroyView()
|
}.disposeOnDestroyView()
|
||||||
|
@ -107,9 +115,9 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
|
||||||
navigator.openRoomProfile(requireContext(), spaceArgs.spaceId, RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_MEMBERS)
|
navigator.openRoomProfile(requireContext(), spaceArgs.spaceId, RoomProfileActivity.EXTRA_DIRECT_ACCESS_ROOM_MEMBERS)
|
||||||
}
|
}
|
||||||
|
|
||||||
views.spaceSettings.isVisible = vectorPreferences.developerMode()
|
|
||||||
views.spaceSettings.views.bottomSheetActionClickableZone.debouncedClicks {
|
views.spaceSettings.views.bottomSheetActionClickableZone.debouncedClicks {
|
||||||
navigator.openRoomProfile(requireContext(), spaceArgs.spaceId)
|
// navigator.openRoomProfile(requireContext(), spaceArgs.spaceId)
|
||||||
|
startActivity(SpaceManageActivity.newIntent(requireActivity(), spaceArgs.spaceId, ManageType.Settings))
|
||||||
}
|
}
|
||||||
|
|
||||||
views.exploreRooms.views.bottomSheetActionClickableZone.debouncedClicks {
|
views.exploreRooms.views.bottomSheetActionClickableZone.debouncedClicks {
|
||||||
|
@ -118,7 +126,7 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
|
||||||
|
|
||||||
views.addRooms.views.bottomSheetActionClickableZone.debouncedClicks {
|
views.addRooms.views.bottomSheetActionClickableZone.debouncedClicks {
|
||||||
dismiss()
|
dismiss()
|
||||||
startActivity(SpaceManageActivity.newIntent(requireActivity(), spaceArgs.spaceId))
|
startActivity(SpaceManageActivity.newIntent(requireActivity(), spaceArgs.spaceId, ManageType.AddRooms))
|
||||||
}
|
}
|
||||||
|
|
||||||
views.leaveSpace.views.bottomSheetActionClickableZone.debouncedClicks {
|
views.leaveSpace.views.bottomSheetActionClickableZone.debouncedClicks {
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_room_to_manage_in_space)
|
||||||
|
abstract class RoomManageSelectionItem : VectorEpoxyModel<RoomManageSelectionItem.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||||
|
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||||
|
@EpoxyAttribute var space: Boolean = false
|
||||||
|
@EpoxyAttribute var selected: Boolean = false
|
||||||
|
@EpoxyAttribute var suggested: Boolean = false
|
||||||
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: View.OnClickListener? = null
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
if (space) {
|
||||||
|
avatarRenderer.renderSpace(matrixItem, holder.avatarImageView)
|
||||||
|
} else {
|
||||||
|
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
||||||
|
}
|
||||||
|
holder.titleText.text = matrixItem.getBestName()
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
holder.checkboxImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_checkbox_on))
|
||||||
|
holder.checkboxImage.contentDescription = holder.view.context.getString(R.string.a11y_checked)
|
||||||
|
} else {
|
||||||
|
holder.checkboxImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_checkbox_off))
|
||||||
|
holder.checkboxImage.contentDescription = holder.view.context.getString(R.string.a11y_unchecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.suggestedText.isVisible = suggested
|
||||||
|
|
||||||
|
holder.view.setOnClickListener {
|
||||||
|
itemClickListener?.onClick(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : VectorEpoxyHolder() {
|
||||||
|
val avatarImageView by bind<ImageView>(R.id.itemAddRoomRoomAvatar)
|
||||||
|
val titleText by bind<TextView>(R.id.itemAddRoomRoomNameText)
|
||||||
|
val suggestedText by bind<TextView>(R.id.itemManageRoomSuggested)
|
||||||
|
val checkboxImage by bind<ImageView>(R.id.itemAddRoomRoomCheckBox)
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,6 +54,11 @@ class SpaceAddRoomsViewModel @AssistedInject constructor(
|
||||||
private val session: Session
|
private val session: Session
|
||||||
) : VectorViewModel<SpaceAddRoomsState, SpaceAddRoomActions, SpaceAddRoomsViewEvents>(initialState) {
|
) : VectorViewModel<SpaceAddRoomsState, SpaceAddRoomActions, SpaceAddRoomsViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(initialState: SpaceAddRoomsState): SpaceAddRoomsViewModel
|
||||||
|
}
|
||||||
|
|
||||||
val updatableLiveSpacePageResult: UpdatableLivePageResult by lazy {
|
val updatableLiveSpacePageResult: UpdatableLivePageResult by lazy {
|
||||||
session.getFilteredPagedRoomSummariesLive(
|
session.getFilteredPagedRoomSummariesLive(
|
||||||
roomSummaryQueryParams {
|
roomSummaryQueryParams {
|
||||||
|
@ -106,11 +111,6 @@ class SpaceAddRoomsViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AssistedFactory
|
|
||||||
interface Factory {
|
|
||||||
fun create(initialState: SpaceAddRoomsState): SpaceAddRoomsViewModel
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object : MvRxViewModelFactory<SpaceAddRoomsViewModel, SpaceAddRoomsState> {
|
companion object : MvRxViewModelFactory<SpaceAddRoomsViewModel, SpaceAddRoomsState> {
|
||||||
override fun create(viewModelContext: ViewModelContext, state: SpaceAddRoomsState): SpaceAddRoomsViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: SpaceAddRoomsState): SpaceAddRoomsViewModel? {
|
||||||
val factory = when (viewModelContext) {
|
val factory = when (viewModelContext) {
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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 io.reactivex.functions.Predicate
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||||
|
|
||||||
|
class SpaceChildInfoMatchFilter : Predicate<SpaceChildInfo> {
|
||||||
|
var filter: String = ""
|
||||||
|
|
||||||
|
override fun test(spaceChildInfo: SpaceChildInfo): Boolean {
|
||||||
|
if (filter.isEmpty()) {
|
||||||
|
// No filter
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// if filter is "Jo Do", it should match "John Doe"
|
||||||
|
return filter.split(" ").all {
|
||||||
|
spaceChildInfo.name?.contains(it, ignoreCase = true).orFalse()
|
||||||
|
|| spaceChildInfo.topic?.contains(it, ignoreCase = true).orFalse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,28 +20,37 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.mvrx.MvRx
|
import com.airbnb.mvrx.MvRx
|
||||||
import com.airbnb.mvrx.viewModel
|
import com.airbnb.mvrx.viewModel
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ScreenComponent
|
import im.vector.app.core.di.ScreenComponent
|
||||||
import im.vector.app.core.extensions.addFragmentToBackstack
|
import im.vector.app.core.extensions.addFragmentToBackstack
|
||||||
import im.vector.app.core.extensions.commitTransaction
|
import im.vector.app.core.extensions.commitTransaction
|
||||||
|
import im.vector.app.core.extensions.hideKeyboard
|
||||||
|
import im.vector.app.core.platform.ToolbarConfigurable
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
import im.vector.app.databinding.ActivitySimpleBinding
|
import im.vector.app.databinding.ActivitySimpleLoadingBinding
|
||||||
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.roomdirectory.createroom.CreateRoomArgs
|
import im.vector.app.features.roomdirectory.createroom.CreateRoomArgs
|
||||||
import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment
|
import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment
|
||||||
|
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class SpaceManageArgs(
|
data class SpaceManageArgs(
|
||||||
val spaceId: String
|
val spaceId: String,
|
||||||
|
val manageType: ManageType
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
class SpaceManageActivity : VectorBaseActivity<ActivitySimpleBinding>(), SpaceManageSharedViewModel.Factory {
|
class SpaceManageActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>(),
|
||||||
|
ToolbarConfigurable,
|
||||||
|
SpaceManageSharedViewModel.Factory {
|
||||||
|
|
||||||
@Inject lateinit var sharedViewModelFactory: SpaceManageSharedViewModel.Factory
|
@Inject lateinit var sharedViewModelFactory: SpaceManageSharedViewModel.Factory
|
||||||
private lateinit var sharedDirectoryActionViewModel: RoomDirectorySharedActionViewModel
|
private lateinit var sharedDirectoryActionViewModel: RoomDirectorySharedActionViewModel
|
||||||
|
@ -50,12 +59,26 @@ class SpaceManageActivity : VectorBaseActivity<ActivitySimpleBinding>(), SpaceMa
|
||||||
injector.inject(this)
|
injector.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getBinding(): ActivitySimpleBinding = ActivitySimpleBinding.inflate(layoutInflater)
|
override fun getBinding(): ActivitySimpleLoadingBinding = ActivitySimpleLoadingBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
override fun getTitleRes(): Int = R.string.space_add_existing_rooms
|
override fun getTitleRes(): Int = R.string.space_add_existing_rooms
|
||||||
|
|
||||||
val sharedViewModel: SpaceManageSharedViewModel by viewModel()
|
val sharedViewModel: SpaceManageSharedViewModel by viewModel()
|
||||||
|
|
||||||
|
override fun showWaitingView(text: String?) {
|
||||||
|
hideKeyboard()
|
||||||
|
views.waitingView.waitingStatusText.isGone = views.waitingView.waitingStatusText.text.isNullOrBlank()
|
||||||
|
super.showWaitingView(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hideWaitingView() {
|
||||||
|
views.waitingView.waitingStatusText.text = null
|
||||||
|
views.waitingView.waitingStatusText.isGone = true
|
||||||
|
views.waitingView.waitingHorizontalProgress.progress = 0
|
||||||
|
views.waitingView.waitingHorizontalProgress.isVisible = false
|
||||||
|
super.hideWaitingView()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -72,14 +95,35 @@ class SpaceManageActivity : VectorBaseActivity<ActivitySimpleBinding>(), SpaceMa
|
||||||
|
|
||||||
val args = intent?.getParcelableExtra<SpaceManageArgs>(MvRx.KEY_ARG)
|
val args = intent?.getParcelableExtra<SpaceManageArgs>(MvRx.KEY_ARG)
|
||||||
if (isFirstCreation()) {
|
if (isFirstCreation()) {
|
||||||
val simpleName = SpaceAddRoomFragment::class.java.simpleName
|
withState(sharedViewModel) {
|
||||||
if (supportFragmentManager.findFragmentByTag(simpleName) == null) {
|
when (it.manageType) {
|
||||||
supportFragmentManager.commitTransaction {
|
ManageType.AddRooms -> {
|
||||||
replace(R.id.simpleFragmentContainer,
|
val simpleName = SpaceAddRoomFragment::class.java.simpleName
|
||||||
SpaceAddRoomFragment::class.java,
|
if (supportFragmentManager.findFragmentByTag(simpleName) == null) {
|
||||||
Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) },
|
supportFragmentManager.commitTransaction {
|
||||||
simpleName
|
replace(R.id.simpleFragmentContainer,
|
||||||
)
|
SpaceAddRoomFragment::class.java,
|
||||||
|
Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) },
|
||||||
|
simpleName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ManageType.Settings -> {
|
||||||
|
val simpleName = SpaceSettingsFragment::class.java.simpleName
|
||||||
|
if (supportFragmentManager.findFragmentByTag(simpleName) == null && args?.spaceId != null) {
|
||||||
|
supportFragmentManager.commitTransaction {
|
||||||
|
replace(R.id.simpleFragmentContainer,
|
||||||
|
SpaceSettingsFragment::class.java,
|
||||||
|
Bundle().apply { this.putParcelable(MvRx.KEY_ARG, RoomProfileArgs(args.spaceId)) },
|
||||||
|
simpleName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ManageType.ManageRooms -> {
|
||||||
|
// no direct access for now
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,10 +134,10 @@ class SpaceManageActivity : VectorBaseActivity<ActivitySimpleBinding>(), SpaceMa
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
SpaceManagedSharedViewEvents.HideLoading -> {
|
SpaceManagedSharedViewEvents.HideLoading -> {
|
||||||
views.simpleActivityWaitingView.isVisible = false
|
hideWaitingView()
|
||||||
}
|
}
|
||||||
SpaceManagedSharedViewEvents.ShowLoading -> {
|
SpaceManagedSharedViewEvents.ShowLoading -> {
|
||||||
views.simpleActivityWaitingView.isVisible = true
|
showWaitingView()
|
||||||
}
|
}
|
||||||
SpaceManagedSharedViewEvents.NavigateToCreateRoom -> {
|
SpaceManagedSharedViewEvents.NavigateToCreateRoom -> {
|
||||||
addFragmentToBackstack(
|
addFragmentToBackstack(
|
||||||
|
@ -102,17 +146,30 @@ class SpaceManageActivity : VectorBaseActivity<ActivitySimpleBinding>(), SpaceMa
|
||||||
CreateRoomArgs("", parentSpaceId = args?.spaceId)
|
CreateRoomArgs("", parentSpaceId = args?.spaceId)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
SpaceManagedSharedViewEvents.NavigateToManageRooms -> {
|
||||||
|
args?.spaceId?.let { spaceId ->
|
||||||
|
addFragmentToBackstack(
|
||||||
|
R.id.simpleFragmentContainer,
|
||||||
|
SpaceManageRoomsFragment::class.java,
|
||||||
|
SpaceManageArgs(spaceId, ManageType.ManageRooms)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newIntent(context: Context, spaceId: String): Intent {
|
fun newIntent(context: Context, spaceId: String, manageType: ManageType): Intent {
|
||||||
return Intent(context, SpaceManageActivity::class.java).apply {
|
return Intent(context, SpaceManageActivity::class.java).apply {
|
||||||
putExtra(MvRx.KEY_ARG, SpaceManageArgs(spaceId))
|
putExtra(MvRx.KEY_ARG, SpaceManageArgs(spaceId, manageType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun create(initialState: SpaceManageViewState) = sharedViewModelFactory.create(initialState)
|
override fun create(initialState: SpaceManageViewState) = sharedViewModelFactory.create(initialState)
|
||||||
|
|
||||||
|
override fun configure(toolbar: Toolbar) {
|
||||||
|
configureToolbar(toolbar)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* 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 im.vector.app.core.platform.VectorViewModelAction
|
||||||
|
|
||||||
|
sealed class SpaceManageRoomViewAction : VectorViewModelAction {
|
||||||
|
data class ToggleSelection(val roomId: String) : SpaceManageRoomViewAction()
|
||||||
|
data class UpdateFilter(val filter: String) : SpaceManageRoomViewAction()
|
||||||
|
object ClearSelection : SpaceManageRoomViewAction()
|
||||||
|
data class MarkAllAsSuggested(val suggested: Boolean) : SpaceManageRoomViewAction()
|
||||||
|
object BulkRemove : SpaceManageRoomViewAction()
|
||||||
|
object RefreshFromServer : SpaceManageRoomViewAction()
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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 im.vector.app.core.platform.VectorViewEvents
|
||||||
|
|
||||||
|
sealed class SpaceManageRoomViewEvents : VectorViewEvents {
|
||||||
|
// object BulkActionSuccess: SpaceManageRoomViewEvents()
|
||||||
|
data class BulkActionFailure(val errorList: List<Throwable>) : SpaceManageRoomViewEvents()
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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 com.airbnb.mvrx.Async
|
||||||
|
import com.airbnb.mvrx.MvRxState
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||||
|
|
||||||
|
data class SpaceManageRoomViewState(
|
||||||
|
val spaceId: String,
|
||||||
|
val spaceSummary: Async<RoomSummary> = Uninitialized,
|
||||||
|
val childrenInfo: Async<List<SpaceChildInfo>> = Uninitialized,
|
||||||
|
val selectedRooms: List<String> = emptyList(),
|
||||||
|
val currentFilter: String = "",
|
||||||
|
val actionState: Async<Unit> = Uninitialized
|
||||||
|
) : MvRxState {
|
||||||
|
constructor(args: SpaceManageArgs) : this(
|
||||||
|
spaceId = args.spaceId
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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 com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import com.airbnb.mvrx.Fail
|
||||||
|
import com.airbnb.mvrx.Incomplete
|
||||||
|
import im.vector.app.core.epoxy.errorWithRetryItem
|
||||||
|
import im.vector.app.core.epoxy.loadingItem
|
||||||
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
|
import im.vector.app.core.utils.DebouncedClickListener
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SpaceManageRoomsController @Inject constructor(
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
private val errorFormatter: ErrorFormatter
|
||||||
|
) : TypedEpoxyController<SpaceManageRoomViewState>() {
|
||||||
|
|
||||||
|
interface Listener {
|
||||||
|
fun toggleSelection(childInfo: SpaceChildInfo)
|
||||||
|
fun retry()
|
||||||
|
}
|
||||||
|
|
||||||
|
var listener: Listener? = null
|
||||||
|
private val matchFilter = SpaceChildInfoMatchFilter()
|
||||||
|
|
||||||
|
override fun buildModels(data: SpaceManageRoomViewState?) {
|
||||||
|
val roomListAsync = data?.childrenInfo
|
||||||
|
if (roomListAsync is Incomplete) {
|
||||||
|
loadingItem { id("loading") }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (roomListAsync is Fail) {
|
||||||
|
errorWithRetryItem {
|
||||||
|
id("Api Error")
|
||||||
|
text(errorFormatter.toHumanReadable(roomListAsync.error))
|
||||||
|
listener { listener?.retry() }
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val roomList = roomListAsync?.invoke() ?: return
|
||||||
|
|
||||||
|
val directChildren = roomList.filter {
|
||||||
|
it.parentRoomId == data.spaceId
|
||||||
|
/** Only direct children **/
|
||||||
|
}
|
||||||
|
matchFilter.filter = data.currentFilter
|
||||||
|
val filteredResult = directChildren.filter { matchFilter.test(it) }
|
||||||
|
|
||||||
|
filteredResult.forEach { childInfo ->
|
||||||
|
roomManageSelectionItem {
|
||||||
|
id(childInfo.childRoomId)
|
||||||
|
matrixItem(childInfo.toMatrixItem())
|
||||||
|
avatarRenderer(avatarRenderer)
|
||||||
|
suggested(childInfo.suggested ?: false)
|
||||||
|
space(childInfo.roomType == RoomType.SPACE)
|
||||||
|
selected(data.selectedRooms.contains(childInfo.childRoomId))
|
||||||
|
itemClickListener(DebouncedClickListener({
|
||||||
|
listener?.toggleSelection(childInfo)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
* 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.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.view.ActionMode
|
||||||
|
import androidx.appcompat.view.ActionMode.Callback
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.transition.TransitionManager
|
||||||
|
import com.airbnb.mvrx.Loading
|
||||||
|
import com.airbnb.mvrx.activityViewModel
|
||||||
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
|
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.cleanup
|
||||||
|
import im.vector.app.core.extensions.configureWith
|
||||||
|
import im.vector.app.core.platform.OnBackPressed
|
||||||
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.app.core.utils.toast
|
||||||
|
import im.vector.app.databinding.FragmentSpaceAddRoomsBinding
|
||||||
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SpaceManageRoomsFragment @Inject constructor(
|
||||||
|
private val viewModelFactory: SpaceManageRoomsViewModel.Factory,
|
||||||
|
private val epoxyController: SpaceManageRoomsController
|
||||||
|
) : VectorBaseFragment<FragmentSpaceAddRoomsBinding>(),
|
||||||
|
SpaceManageRoomsViewModel.Factory,
|
||||||
|
OnBackPressed,
|
||||||
|
SpaceManageRoomsController.Listener,
|
||||||
|
Callback {
|
||||||
|
|
||||||
|
private val viewModel by fragmentViewModel(SpaceManageRoomsViewModel::class)
|
||||||
|
private val sharedViewModel: SpaceManageSharedViewModel by activityViewModel()
|
||||||
|
|
||||||
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentSpaceAddRoomsBinding.inflate(inflater)
|
||||||
|
|
||||||
|
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||||
|
parentFragmentManager.popBackStack()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setupToolbar(views.addRoomToSpaceToolbar)
|
||||||
|
views.appBarTitle.text = getString(R.string.space_manage_rooms_and_spaces)
|
||||||
|
views.createNewRoom.isVisible = false
|
||||||
|
epoxyController.listener = this
|
||||||
|
views.roomList.configureWith(epoxyController, hasFixedSize = true, showDivider = true)
|
||||||
|
|
||||||
|
views.publicRoomsFilter.queryTextChanges()
|
||||||
|
.debounce(200, TimeUnit.MILLISECONDS)
|
||||||
|
.subscribeBy {
|
||||||
|
viewModel.handle(SpaceManageRoomViewAction.UpdateFilter(it.toString()))
|
||||||
|
}
|
||||||
|
.disposeOnDestroyView()
|
||||||
|
|
||||||
|
viewModel.selectSubscribe(SpaceManageRoomViewState::actionState) { actionState ->
|
||||||
|
when (actionState) {
|
||||||
|
is Loading -> {
|
||||||
|
sharedViewModel.handle(SpaceManagedSharedAction.ShowLoading)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
sharedViewModel.handle(SpaceManagedSharedAction.HideLoading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.observeViewEvents {
|
||||||
|
when (it) {
|
||||||
|
is SpaceManageRoomViewEvents.BulkActionFailure -> {
|
||||||
|
vectorBaseActivity.toast(errorFormatter.toHumanReadable(it.errorList.firstOrNull()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
epoxyController.listener = null
|
||||||
|
views.roomList.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create(initialState: SpaceManageRoomViewState) = viewModelFactory.create(initialState)
|
||||||
|
|
||||||
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
|
epoxyController.setData(state)
|
||||||
|
|
||||||
|
state.spaceSummary.invoke()?.let {
|
||||||
|
views.appBarSpaceInfo.text = it.displayName
|
||||||
|
}
|
||||||
|
if (state.selectedRooms.isNotEmpty()) {
|
||||||
|
if (currentActionMode == null) {
|
||||||
|
views.addRoomToSpaceToolbar.isVisible = true
|
||||||
|
vectorBaseActivity.startSupportActionMode(this)
|
||||||
|
} else {
|
||||||
|
currentActionMode?.title = "${state.selectedRooms.size} selected"
|
||||||
|
}
|
||||||
|
// views.addRoomToSpaceToolbar.isVisible = false
|
||||||
|
// views.addRoomToSpaceToolbar.startActionMode(this)
|
||||||
|
} else {
|
||||||
|
currentActionMode?.finish()
|
||||||
|
}
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentActionMode: ActionMode? = null
|
||||||
|
|
||||||
|
override fun toggleSelection(childInfo: SpaceChildInfo) {
|
||||||
|
viewModel.handle(SpaceManageRoomViewAction.ToggleSelection(childInfo.childRoomId))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun retry() {
|
||||||
|
viewModel.handle(SpaceManageRoomViewAction.RefreshFromServer)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
|
val inflater = mode?.menuInflater
|
||||||
|
inflater?.inflate(R.menu.menu_manage_space, menu)
|
||||||
|
withState(viewModel) {
|
||||||
|
mode?.title = resources.getQuantityString(R.plurals.room_details_selected, it.selectedRooms.size, it.selectedRooms.size)
|
||||||
|
}
|
||||||
|
currentActionMode = mode
|
||||||
|
views.addRoomToSpaceToolbar.isVisible = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
|
||||||
|
withState(viewModel) { state ->
|
||||||
|
// check if we show mark as suggested or not
|
||||||
|
val areAllSuggested = state.childrenInfo.invoke().orEmpty().filter { state.selectedRooms.contains(it.childRoomId) }
|
||||||
|
.all { it.suggested == true }
|
||||||
|
menu?.findItem(R.id.action_mark_as_suggested)?.isVisible = !areAllSuggested
|
||||||
|
menu?.findItem(R.id.action_mark_as_not_suggested)?.isVisible = areAllSuggested
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
|
||||||
|
when (item?.itemId) {
|
||||||
|
R.id.action_delete -> {
|
||||||
|
handleDeleteSelection()
|
||||||
|
}
|
||||||
|
R.id.action_mark_as_suggested -> {
|
||||||
|
viewModel.handle(SpaceManageRoomViewAction.MarkAllAsSuggested(true))
|
||||||
|
}
|
||||||
|
R.id.action_mark_as_not_suggested -> {
|
||||||
|
viewModel.handle(SpaceManageRoomViewAction.MarkAllAsSuggested(false))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mode?.finish()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleDeleteSelection() {
|
||||||
|
viewModel.handle(SpaceManageRoomViewAction.BulkRemove)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyActionMode(mode: ActionMode?) {
|
||||||
|
// should force a refresh
|
||||||
|
currentActionMode = null
|
||||||
|
viewModel.handle(SpaceManageRoomViewAction.ClearSelection)
|
||||||
|
views.coordinatorLayout.post {
|
||||||
|
if (isAdded) {
|
||||||
|
TransitionManager.beginDelayedTransition(views.coordinatorLayout)
|
||||||
|
views.addRoomToSpaceToolbar.isVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* 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 androidx.lifecycle.viewModelScope
|
||||||
|
import com.airbnb.mvrx.ActivityViewModelContext
|
||||||
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
|
import com.airbnb.mvrx.Loading
|
||||||
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.core.mvrx.runCatchingToAsync
|
||||||
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.features.session.coroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
|
||||||
|
class SpaceManageRoomsViewModel @AssistedInject constructor(
|
||||||
|
@Assisted val initialState: SpaceManageRoomViewState,
|
||||||
|
private val session: Session
|
||||||
|
) : VectorViewModel<SpaceManageRoomViewState, SpaceManageRoomViewAction, SpaceManageRoomViewEvents>(initialState) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
val spaceSummary = session.getRoomSummary(initialState.spaceId)
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
spaceSummary = spaceSummary?.let { Success(it) } ?: Uninitialized,
|
||||||
|
childrenInfo = Loading()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val apiResult = runCatchingToAsync {
|
||||||
|
session.spaceService().querySpaceChildren(spaceId = initialState.spaceId).second
|
||||||
|
}
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
childrenInfo = apiResult
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(initialState: SpaceManageRoomViewState): SpaceManageRoomsViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MvRxViewModelFactory<SpaceManageRoomsViewModel, SpaceManageRoomViewState> {
|
||||||
|
override fun create(viewModelContext: ViewModelContext, state: SpaceManageRoomViewState): SpaceManageRoomsViewModel? {
|
||||||
|
val factory = when (viewModelContext) {
|
||||||
|
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
|
||||||
|
is ActivityViewModelContext -> viewModelContext.activity as? Factory
|
||||||
|
}
|
||||||
|
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handle(action: SpaceManageRoomViewAction) {
|
||||||
|
when (action) {
|
||||||
|
is SpaceManageRoomViewAction.ToggleSelection -> handleToggleSelection(action)
|
||||||
|
is SpaceManageRoomViewAction.UpdateFilter -> {
|
||||||
|
setState { copy(currentFilter = action.filter) }
|
||||||
|
}
|
||||||
|
SpaceManageRoomViewAction.ClearSelection -> {
|
||||||
|
setState { copy(selectedRooms = emptyList()) }
|
||||||
|
}
|
||||||
|
SpaceManageRoomViewAction.BulkRemove -> {
|
||||||
|
handleBulkRemove()
|
||||||
|
}
|
||||||
|
is SpaceManageRoomViewAction.MarkAllAsSuggested -> {
|
||||||
|
handleBulkMarkAsSuggested(action.suggested)
|
||||||
|
}
|
||||||
|
SpaceManageRoomViewAction.RefreshFromServer -> {
|
||||||
|
refreshSummaryAPI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleBulkRemove() = withState { state ->
|
||||||
|
setState { copy(actionState = Loading()) }
|
||||||
|
val selection = state.selectedRooms
|
||||||
|
session.coroutineScope.launch(Dispatchers.IO) {
|
||||||
|
val errorList = mutableListOf<Throwable>()
|
||||||
|
selection.forEach {
|
||||||
|
try {
|
||||||
|
session.spaceService().getSpace(state.spaceId)?.removeChildren(it)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
errorList.add(failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorList.isEmpty()) {
|
||||||
|
// success
|
||||||
|
} else {
|
||||||
|
_viewEvents.post(SpaceManageRoomViewEvents.BulkActionFailure(errorList))
|
||||||
|
}
|
||||||
|
refreshSummaryAPI()
|
||||||
|
setState { copy(actionState = Uninitialized) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleBulkMarkAsSuggested(suggested: Boolean) = withState { state ->
|
||||||
|
setState { copy(actionState = Loading()) }
|
||||||
|
val selection = state.childrenInfo.invoke()?.filter {
|
||||||
|
state.selectedRooms.contains(it.childRoomId)
|
||||||
|
}.orEmpty()
|
||||||
|
session.coroutineScope.launch(Dispatchers.IO) {
|
||||||
|
val errorList = mutableListOf<Throwable>()
|
||||||
|
selection.forEach { info ->
|
||||||
|
try {
|
||||||
|
session.spaceService().getSpace(state.spaceId)?.addChildren(
|
||||||
|
roomId = info.childRoomId,
|
||||||
|
viaServers = info.viaServers,
|
||||||
|
order = info.order,
|
||||||
|
suggested = suggested,
|
||||||
|
autoJoin = info.autoJoin
|
||||||
|
)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
errorList.add(failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorList.isEmpty()) {
|
||||||
|
// success
|
||||||
|
} else {
|
||||||
|
_viewEvents.post(SpaceManageRoomViewEvents.BulkActionFailure(errorList))
|
||||||
|
}
|
||||||
|
refreshSummaryAPI()
|
||||||
|
setState { copy(actionState = Uninitialized) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshSummaryAPI() {
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
childrenInfo = Loading()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val apiResult = runCatchingToAsync {
|
||||||
|
session.spaceService().querySpaceChildren(spaceId = initialState.spaceId).second
|
||||||
|
}
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
childrenInfo = apiResult
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleToggleSelection(action: SpaceManageRoomViewAction.ToggleSelection) = withState { state ->
|
||||||
|
val existing = state.selectedRooms.toMutableList()
|
||||||
|
if (existing.contains(action.roomId)) {
|
||||||
|
existing.remove(action.roomId)
|
||||||
|
} else {
|
||||||
|
existing.add(action.roomId)
|
||||||
|
}
|
||||||
|
setState {
|
||||||
|
copy(
|
||||||
|
selectedRooms = existing.toList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,6 +55,7 @@ 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.ManageRooms -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToManageRooms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,17 @@ package im.vector.app.features.spaces.manage
|
||||||
|
|
||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
|
|
||||||
|
enum class ManageType {
|
||||||
|
AddRooms,
|
||||||
|
Settings,
|
||||||
|
ManageRooms
|
||||||
|
}
|
||||||
data class SpaceManageViewState(
|
data class SpaceManageViewState(
|
||||||
val spaceId: String = ""
|
val spaceId: String = "",
|
||||||
|
val manageType: ManageType
|
||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
constructor(args: SpaceManageArgs) : this(
|
constructor(args: SpaceManageArgs) : this(
|
||||||
spaceId = args.spaceId
|
spaceId = args.spaceId,
|
||||||
|
manageType = args.manageType
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,4 +23,5 @@ sealed class SpaceManagedSharedAction : VectorViewModelAction {
|
||||||
object ShowLoading : SpaceManagedSharedAction()
|
object ShowLoading : SpaceManagedSharedAction()
|
||||||
object HideLoading : SpaceManagedSharedAction()
|
object HideLoading : SpaceManagedSharedAction()
|
||||||
object CreateRoom : SpaceManagedSharedAction()
|
object CreateRoom : SpaceManagedSharedAction()
|
||||||
|
object ManageRooms : SpaceManagedSharedAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,4 +23,5 @@ sealed class SpaceManagedSharedViewEvents : VectorViewEvents {
|
||||||
object ShowLoading : SpaceManagedSharedViewEvents()
|
object ShowLoading : SpaceManagedSharedViewEvents()
|
||||||
object HideLoading : SpaceManagedSharedViewEvents()
|
object HideLoading : SpaceManagedSharedViewEvents()
|
||||||
object NavigateToCreateRoom : SpaceManagedSharedViewEvents()
|
object NavigateToCreateRoom : SpaceManagedSharedViewEvents()
|
||||||
|
object NavigateToManageRooms : SpaceManagedSharedViewEvents()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* 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 com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.profiles.buildProfileAction
|
||||||
|
import im.vector.app.core.epoxy.profiles.buildProfileSection
|
||||||
|
import im.vector.app.core.resources.ColorProvider
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
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.formSwitchItem
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.roomprofile.settings.RoomSettingsViewState
|
||||||
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SpaceSettingsController @Inject constructor(
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
colorProvider: ColorProvider,
|
||||||
|
private val vectorPreferences: VectorPreferences
|
||||||
|
) : TypedEpoxyController<RoomSettingsViewState>() {
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
// Delete the avatar, or cancel an avatar change
|
||||||
|
fun onAvatarDelete()
|
||||||
|
fun onAvatarChange()
|
||||||
|
fun onNameChanged(name: String)
|
||||||
|
fun onTopicChanged(topic: String)
|
||||||
|
fun onHistoryVisibilityClicked()
|
||||||
|
fun onJoinRuleClicked()
|
||||||
|
fun onToggleGuestAccess()
|
||||||
|
fun onDevTools()
|
||||||
|
fun onDevRoomSettings()
|
||||||
|
fun onManageRooms()
|
||||||
|
fun setIsPublic(public: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color)
|
||||||
|
|
||||||
|
var callback: Callback? = null
|
||||||
|
|
||||||
|
override fun buildModels(data: RoomSettingsViewState?) {
|
||||||
|
val roomSummary = data?.roomSummary?.invoke() ?: return
|
||||||
|
|
||||||
|
formEditableSquareAvatarItem {
|
||||||
|
id("avatar")
|
||||||
|
enabled(data.actionPermissions.canChangeAvatar)
|
||||||
|
when (val avatarAction = data.avatarAction) {
|
||||||
|
RoomSettingsViewState.AvatarAction.None -> {
|
||||||
|
// Use the current value
|
||||||
|
avatarRenderer(avatarRenderer)
|
||||||
|
// We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar.
|
||||||
|
matrixItem(roomSummary.toMatrixItem().copy(avatarUrl = data.currentRoomAvatarUrl))
|
||||||
|
}
|
||||||
|
RoomSettingsViewState.AvatarAction.DeleteAvatar ->
|
||||||
|
imageUri(null)
|
||||||
|
is RoomSettingsViewState.AvatarAction.UpdateAvatar ->
|
||||||
|
imageUri(avatarAction.newAvatarUri)
|
||||||
|
}
|
||||||
|
clickListener { callback?.onAvatarChange() }
|
||||||
|
deleteListener { callback?.onAvatarDelete() }
|
||||||
|
}
|
||||||
|
|
||||||
|
buildProfileSection(
|
||||||
|
stringProvider.getString(R.string.settings)
|
||||||
|
)
|
||||||
|
|
||||||
|
formEditTextItem {
|
||||||
|
id("name")
|
||||||
|
enabled(data.actionPermissions.canChangeName)
|
||||||
|
value(data.newName ?: roomSummary.displayName)
|
||||||
|
hint(stringProvider.getString(R.string.create_room_name_hint))
|
||||||
|
showBottomSeparator(false)
|
||||||
|
onTextChange { text ->
|
||||||
|
callback?.onNameChanged(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formMultiLineEditTextItem {
|
||||||
|
id("topic")
|
||||||
|
enabled(data.actionPermissions.canChangeTopic)
|
||||||
|
value(data.newTopic ?: roomSummary.topic)
|
||||||
|
hint(stringProvider.getString(R.string.create_space_topic_hint))
|
||||||
|
showBottomSeparator(false)
|
||||||
|
onTextChange { text ->
|
||||||
|
callback?.onTopicChanged(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vectorPreferences.labsUseExperimentalRestricted()) {
|
||||||
|
buildProfileAction(
|
||||||
|
id = "joinRule",
|
||||||
|
title = stringProvider.getString(R.string.room_settings_room_access_title),
|
||||||
|
subtitle = data.getJoinRuleWording(stringProvider),
|
||||||
|
dividerColor = dividerColor,
|
||||||
|
divider = true,
|
||||||
|
editable = data.actionPermissions.canChangeJoinRule,
|
||||||
|
action = { if (data.actionPermissions.canChangeJoinRule) callback?.onJoinRuleClicked() }
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val isPublic = (data.newRoomJoinRules.newJoinRules ?: data.currentRoomJoinRules) == RoomJoinRules.PUBLIC
|
||||||
|
formSwitchItem {
|
||||||
|
id("isPublic")
|
||||||
|
enabled(data.actionPermissions.canChangeJoinRule)
|
||||||
|
title(stringProvider.getString(R.string.make_this_space_public))
|
||||||
|
switchChecked(isPublic)
|
||||||
|
|
||||||
|
listener { value ->
|
||||||
|
callback?.setIsPublic(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildProfileAction(
|
||||||
|
id = "manage_rooms",
|
||||||
|
title = stringProvider.getString(R.string.space_settings_manage_rooms),
|
||||||
|
// subtitle = data.getJoinRuleWording(stringProvider),
|
||||||
|
dividerColor = dividerColor,
|
||||||
|
divider = vectorPreferences.developerMode(),
|
||||||
|
editable = data.actionPermissions.canAddChildren,
|
||||||
|
action = {
|
||||||
|
if (data.actionPermissions.canAddChildren) callback?.onManageRooms()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (vectorPreferences.developerMode()) {
|
||||||
|
buildProfileAction(
|
||||||
|
id = "dev_tools",
|
||||||
|
title = stringProvider.getString(R.string.settings_dev_tools),
|
||||||
|
icon = R.drawable.ic_verification_glasses,
|
||||||
|
tintIcon = false,
|
||||||
|
dividerColor = dividerColor,
|
||||||
|
divider = true,
|
||||||
|
action = {
|
||||||
|
callback?.onDevTools()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
buildProfileAction(
|
||||||
|
id = "room_tools",
|
||||||
|
title = stringProvider.getString(R.string.room_list_quick_actions_room_settings),
|
||||||
|
icon = R.drawable.ic_verification_glasses,
|
||||||
|
tintIcon = false,
|
||||||
|
dividerColor = dividerColor,
|
||||||
|
divider = false,
|
||||||
|
action = {
|
||||||
|
callback?.onDevRoomSettings()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
/*
|
||||||
|
* 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.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.airbnb.mvrx.activityViewModel
|
||||||
|
import com.airbnb.mvrx.args
|
||||||
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
|
||||||
|
import im.vector.app.core.extensions.cleanup
|
||||||
|
import im.vector.app.core.extensions.configureWith
|
||||||
|
import im.vector.app.core.extensions.exhaustive
|
||||||
|
import im.vector.app.core.intent.getFilenameFromUri
|
||||||
|
import im.vector.app.core.platform.OnBackPressed
|
||||||
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.app.core.resources.ColorProvider
|
||||||
|
import im.vector.app.core.resources.DrawableProvider
|
||||||
|
import im.vector.app.core.utils.toast
|
||||||
|
import im.vector.app.databinding.FragmentRoomSettingGenericBinding
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||||
|
import im.vector.app.features.roomprofile.settings.RoomSettingsAction
|
||||||
|
import im.vector.app.features.roomprofile.settings.RoomSettingsViewEvents
|
||||||
|
import im.vector.app.features.roomprofile.settings.RoomSettingsViewModel
|
||||||
|
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.RoomJoinRuleSharedActionViewModel
|
||||||
|
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.RoomJoinRules
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SpaceSettingsFragment @Inject constructor(
|
||||||
|
private val epoxyController: SpaceSettingsController,
|
||||||
|
private val colorProvider: ColorProvider,
|
||||||
|
val viewModelFactory: RoomSettingsViewModel.Factory,
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
private val drawableProvider: DrawableProvider
|
||||||
|
) : VectorBaseFragment<FragmentRoomSettingGenericBinding>(),
|
||||||
|
RoomSettingsViewModel.Factory,
|
||||||
|
SpaceSettingsController.Callback,
|
||||||
|
GalleryOrCameraDialogHelper.Listener,
|
||||||
|
OnBackPressed {
|
||||||
|
|
||||||
|
private val viewModel: RoomSettingsViewModel by fragmentViewModel()
|
||||||
|
private val sharedViewModel: SpaceManageSharedViewModel by activityViewModel()
|
||||||
|
|
||||||
|
private lateinit var roomJoinRuleSharedActionViewModel: RoomJoinRuleSharedActionViewModel
|
||||||
|
|
||||||
|
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider)
|
||||||
|
|
||||||
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentRoomSettingGenericBinding.inflate(inflater)
|
||||||
|
|
||||||
|
private val roomProfileArgs: RoomProfileArgs by args()
|
||||||
|
|
||||||
|
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)
|
||||||
|
setupToolbar(views.roomSettingsToolbar)
|
||||||
|
// roomProfileSharedActionViewModel = activityViewModelProvider.get(RoomProfileSharedActionViewModel::class.java)
|
||||||
|
// setupRoomHistoryVisibilitySharedActionViewModel()
|
||||||
|
setupRoomJoinRuleSharedActionViewModel()
|
||||||
|
epoxyController.callback = this
|
||||||
|
views.roomSettingsRecyclerView.configureWith(epoxyController, hasFixedSize = true)
|
||||||
|
views.waitingView.waitingStatusText.setText(R.string.please_wait)
|
||||||
|
views.waitingView.waitingStatusText.isVisible = true
|
||||||
|
|
||||||
|
viewModel.observeViewEvents {
|
||||||
|
when (it) {
|
||||||
|
is RoomSettingsViewEvents.Failure -> showFailure(it.throwable)
|
||||||
|
RoomSettingsViewEvents.Success -> showSuccess()
|
||||||
|
RoomSettingsViewEvents.GoBack -> {
|
||||||
|
ignoreChanges = true
|
||||||
|
vectorBaseActivity.onBackPressed()
|
||||||
|
}
|
||||||
|
}.exhaustive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
epoxyController.callback = null
|
||||||
|
views.roomSettingsRecyclerView.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||||
|
withState(viewModel) { state ->
|
||||||
|
menu.findItem(R.id.roomSettingsSaveAction).isVisible = state.showSaveAction
|
||||||
|
}
|
||||||
|
super.onPrepareOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
if (item.itemId == R.id.roomSettingsSaveAction) {
|
||||||
|
viewModel.handle(RoomSettingsAction.Save)
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderRoomSummary(state: RoomSettingsViewState) {
|
||||||
|
views.waitingView.root.isVisible = state.isLoading
|
||||||
|
|
||||||
|
state.roomSummary()?.let {
|
||||||
|
views.roomSettingsToolbarTitleView.text = it.displayName
|
||||||
|
views.roomSettingsToolbarTitleView.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
drawableProvider.getDrawable(R.drawable.ic_beta_pill),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
avatarRenderer.renderSpace(it.toMatrixItem(), views.roomSettingsToolbarAvatarImageView)
|
||||||
|
views.roomSettingsDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateOptionsMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
|
epoxyController.setData(state)
|
||||||
|
renderRoomSummary(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRoomJoinRuleSharedActionViewModel() {
|
||||||
|
roomJoinRuleSharedActionViewModel = activityViewModelProvider.get(RoomJoinRuleSharedActionViewModel::class.java)
|
||||||
|
roomJoinRuleSharedActionViewModel
|
||||||
|
.observe()
|
||||||
|
.subscribe { action ->
|
||||||
|
viewModel.handle(RoomSettingsAction.SetRoomJoinRule(action.roomJoinRule))
|
||||||
|
}
|
||||||
|
.disposeOnDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var ignoreChanges = false
|
||||||
|
|
||||||
|
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||||
|
if (ignoreChanges) return false
|
||||||
|
|
||||||
|
return withState(viewModel) {
|
||||||
|
return@withState if (it.showSaveAction) {
|
||||||
|
AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.dialog_title_warning)
|
||||||
|
.setMessage(R.string.warning_unsaved_change)
|
||||||
|
.setPositiveButton(R.string.warning_unsaved_change_discard) { _, _ ->
|
||||||
|
viewModel.handle(RoomSettingsAction.Cancel)
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSuccess() {
|
||||||
|
activity?.toast(R.string.room_settings_save_success)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNameChanged(name: String) {
|
||||||
|
viewModel.handle(RoomSettingsAction.SetRoomName(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTopicChanged(topic: String) {
|
||||||
|
viewModel.handle(RoomSettingsAction.SetRoomTopic(topic))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onHistoryVisibilityClicked() {
|
||||||
|
// N/A for space settings screen
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onJoinRuleClicked() = withState(viewModel) { state ->
|
||||||
|
val currentJoinRule = state.newRoomJoinRules.newJoinRules ?: state.currentRoomJoinRules
|
||||||
|
RoomJoinRuleBottomSheet.newInstance(currentJoinRule)
|
||||||
|
.show(childFragmentManager, "RoomJoinRuleBottomSheet")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onToggleGuestAccess() = withState(viewModel) { state ->
|
||||||
|
val currentGuestAccess = state.newRoomJoinRules.newGuestAccess ?: state.currentGuestAccess
|
||||||
|
val toggled = if (currentGuestAccess == GuestAccess.Forbidden) GuestAccess.CanJoin else GuestAccess.Forbidden
|
||||||
|
viewModel.handle(RoomSettingsAction.SetRoomGuestAccess(toggled))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDevTools() = withState(viewModel) { state ->
|
||||||
|
navigator.openDevTools(requireContext(), state.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDevRoomSettings() = withState(viewModel) { state ->
|
||||||
|
navigator.openRoomProfile(requireContext(), state.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onManageRooms() {
|
||||||
|
sharedViewModel.handle(SpaceManagedSharedAction.ManageRooms)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setIsPublic(public: Boolean) {
|
||||||
|
if (public) {
|
||||||
|
viewModel.handle(RoomSettingsAction.SetRoomJoinRule(RoomJoinRules.PUBLIC))
|
||||||
|
viewModel.handle(RoomSettingsAction.SetRoomHistoryVisibility(RoomHistoryVisibility.WORLD_READABLE))
|
||||||
|
} else {
|
||||||
|
viewModel.handle(RoomSettingsAction.SetRoomJoinRule(RoomJoinRules.INVITE))
|
||||||
|
viewModel.handle(RoomSettingsAction.SetRoomHistoryVisibility(RoomHistoryVisibility.INVITED))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onImageReady(uri: Uri?) {
|
||||||
|
uri ?: return
|
||||||
|
viewModel.handle(
|
||||||
|
RoomSettingsAction.SetAvatarAction(
|
||||||
|
RoomSettingsViewState.AvatarAction.UpdateAvatar(
|
||||||
|
newAvatarUri = uri,
|
||||||
|
newAvatarFileName = getFilenameFromUri(requireContext(), uri) ?: UUID.randomUUID().toString())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAvatarDelete() {
|
||||||
|
withState(viewModel) {
|
||||||
|
when (it.avatarAction) {
|
||||||
|
RoomSettingsViewState.AvatarAction.None -> {
|
||||||
|
viewModel.handle(RoomSettingsAction.SetAvatarAction(RoomSettingsViewState.AvatarAction.DeleteAvatar))
|
||||||
|
}
|
||||||
|
RoomSettingsViewState.AvatarAction.DeleteAvatar -> {
|
||||||
|
/* Should not happen */
|
||||||
|
}
|
||||||
|
is RoomSettingsViewState.AvatarAction.UpdateAvatar -> {
|
||||||
|
// Cancel the update of the avatar
|
||||||
|
viewModel.handle(RoomSettingsAction.SetAvatarAction(RoomSettingsViewState.AvatarAction.None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAvatarChange() {
|
||||||
|
galleryOrCameraDialogHelper.show()
|
||||||
|
}
|
||||||
|
}
|
|
@ -94,12 +94,10 @@
|
||||||
android:id="@+id/spaceSettings"
|
android:id="@+id/spaceSettings"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"
|
|
||||||
app:actionTitle="@string/settings"
|
app:actionTitle="@string/settings"
|
||||||
app:leftIcon="@drawable/ic_settings_root_general"
|
app:leftIcon="@drawable/ic_settings_root_general"
|
||||||
app:tint="?attr/riotx_text_primary"
|
app:tint="?attr/riotx_text_primary"
|
||||||
app:titleTextColor="?attr/riotx_text_primary"
|
app:titleTextColor="?attr/riotx_text_primary"/>
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<im.vector.app.core.ui.views.BottomSheetActionButton
|
<im.vector.app.core.ui.views.BottomSheetActionButton
|
||||||
android:id="@+id/exploreRooms"
|
android:id="@+id/exploreRooms"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
android:id="@+id/rootConstraintLayout"
|
android:id="@+id/rootConstraintLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?riotx_header_panel_background">
|
android:background="?riotx_background">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/roomSettingsToolbar"
|
android:id="@+id/roomSettingsToolbar"
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
android:id="@+id/formSwitchDivider"
|
android:id="@+id/formSwitchDivider"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:background="?riotx_header_panel_border_mobile"
|
android:background="?vctr_list_divider_color"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/itemPublicRoomLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?riotx_background"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:minHeight="50dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/itemAddRoomRoomAvatar"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:contentDescription="@string/avatar"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@tools:sample/avatars" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/itemAddRoomRoomNameText"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="17dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textColor="?riotx_text_primary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/itemManageRoomSuggested"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/itemAddRoomRoomAvatar"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="@sample/matrix.json/data/roomName" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/itemManageRoomSuggested"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?riotx_text_secondary"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/itemAddRoomRoomCheckBox"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/itemAddRoomRoomNameText"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
android:text="@string/space_suggested" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/itemAddRoomRoomCheckBox"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:contentDescription="@string/a11y_checked"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/itemManageRoomSuggested"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@drawable/ic_checkbox_on" />
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_delete"
|
||||||
|
android:title="@string/delete"
|
||||||
|
android:icon="@drawable/ic_delete_unsent_messages"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_mark_as_suggested"
|
||||||
|
android:title="@string/space_mark_as_suggested"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_mark_as_not_suggested"
|
||||||
|
android:title="@string/space_mark_as_not_suggested"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
</menu>
|
|
@ -3356,4 +3356,10 @@
|
||||||
<string name="user_invites_you">%s invites you</string>
|
<string name="user_invites_you">%s invites you</string>
|
||||||
|
|
||||||
<string name="looking_for_someone_not_in_space">Looking for someone not in %s?</string>
|
<string name="looking_for_someone_not_in_space">Looking for someone not in %s?</string>
|
||||||
|
<string name="space_settings_manage_rooms">Manage rooms</string>
|
||||||
|
<string name="make_this_space_public">Make this space public</string>
|
||||||
|
<string name="space_suggested">Suggested</string>
|
||||||
|
<string name="space_mark_as_suggested">Mark as suggested</string>
|
||||||
|
<string name="space_mark_as_not_suggested">Mark as not suggested</string>
|
||||||
|
<string name="space_manage_rooms_and_spaces">Manage rooms and spaces</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -11,6 +11,13 @@
|
||||||
<item name="android:background">?riotx_background</item>
|
<item name="android:background">?riotx_background</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="ActionModeTheme" parent="Widget.AppCompat.ActionMode">
|
||||||
|
<item name="background">?riotx_background</item>
|
||||||
|
<item name="titleTextStyle">@style/Vector.Toolbar.Title</item>
|
||||||
|
<item name="subtitleTextStyle">@style/Vector.Toolbar.SubTitle</item>
|
||||||
|
<item name="actionMenuTextColor">?colorOnPrimary</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<style name="VectorToolbarStyle" parent="VectorToolbarStyleWithPadding">
|
<style name="VectorToolbarStyle" parent="VectorToolbarStyleWithPadding">
|
||||||
<item name="contentInsetStartWithNavigation">0dp</item>
|
<item name="contentInsetStartWithNavigation">0dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
@ -65,7 +72,7 @@
|
||||||
|
|
||||||
<!-- actionbar icons color -->
|
<!-- actionbar icons color -->
|
||||||
<style name="Vector.ActionBarTheme" parent="ThemeOverlay.MaterialComponents.ActionBar">
|
<style name="Vector.ActionBarTheme" parent="ThemeOverlay.MaterialComponents.ActionBar">
|
||||||
<item name="colorControlNormal">@android:color/white</item>
|
<item name="colorControlNormal">?android:attr/textColorPrimary</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- custom action bar -->
|
<!-- custom action bar -->
|
||||||
|
|
|
@ -181,6 +181,8 @@
|
||||||
|
|
||||||
<!-- chat effect -->
|
<!-- chat effect -->
|
||||||
<item name="vctr_chat_effect_snow_background">@android:color/transparent</item>
|
<item name="vctr_chat_effect_snow_background">@android:color/transparent</item>
|
||||||
|
|
||||||
|
<item name="actionModeStyle">@style/ActionModeTheme</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.Dark" parent="AppTheme.Base.Dark" />
|
<style name="AppTheme.Dark" parent="AppTheme.Base.Dark" />
|
||||||
|
|
|
@ -183,6 +183,9 @@
|
||||||
|
|
||||||
<!-- chat effect -->
|
<!-- chat effect -->
|
||||||
<item name="vctr_chat_effect_snow_background">@color/black_alpha</item>
|
<item name="vctr_chat_effect_snow_background">@color/black_alpha</item>
|
||||||
|
|
||||||
|
<item name="actionModeStyle">@style/ActionModeTheme</item>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="AppTheme.Light" parent="AppTheme.Base.Light" />
|
<style name="AppTheme.Light" parent="AppTheme.Base.Light" />
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
<im.vector.app.core.preference.VectorSwitchPreference
|
<im.vector.app.core.preference.VectorSwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
android:key="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
||||||
|
android:icon="@drawable/ic_verification_glasses"
|
||||||
android:summary="@string/settings_developer_mode_summary"
|
android:summary="@string/settings_developer_mode_summary"
|
||||||
android:title="@string/settings_developer_mode" />
|
android:title="@string/settings_developer_mode" />
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue