Merge branch 'develop' into live_location_labs_flag
* develop: Space explore rooms screen alignment with design in figma (#5834) leaving space aligned with ios (#5942) Fix usage of System.currentTimeMillis(). This a bit mocky but anyway it's better to use SystemClock.elapsedRealtime() for this case.
This commit is contained in:
commit
2e2dffd9c9
1
changelog.d/5658.feature
Normal file
1
changelog.d/5658.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Space explore screen changes: removed space card, added rooms filtering
|
1
changelog.d/5728.misc
Normal file
1
changelog.d/5728.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
leaving space experience changed to be aligned with iOS
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package im.vector.lib.core.utils.flow
|
package im.vector.lib.core.utils.flow
|
||||||
|
|
||||||
|
import android.os.SystemClock
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
@ -68,10 +69,10 @@ fun <T> Flow<T>.chunk(durationInMillis: Long): Flow<List<T>> {
|
|||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
@ExperimentalCoroutinesApi
|
||||||
fun <T> Flow<T>.throttleFirst(windowDuration: Long): Flow<T> = flow {
|
fun <T> Flow<T>.throttleFirst(windowDuration: Long): Flow<T> = flow {
|
||||||
var windowStartTime = System.currentTimeMillis()
|
var windowStartTime = SystemClock.elapsedRealtime()
|
||||||
var emitted = false
|
var emitted = false
|
||||||
collect { value ->
|
collect { value ->
|
||||||
val currentTime = System.currentTimeMillis()
|
val currentTime = SystemClock.elapsedRealtime()
|
||||||
val delta = currentTime - windowStartTime
|
val delta = currentTime - windowStartTime
|
||||||
if (delta >= windowDuration) {
|
if (delta >= windowDuration) {
|
||||||
windowStartTime += delta / windowDuration * windowDuration
|
windowStartTime += delta / windowDuration * windowDuration
|
||||||
|
@ -104,11 +104,10 @@ class SpaceMenuRobot {
|
|||||||
|
|
||||||
fun leaveSpace() {
|
fun leaveSpace() {
|
||||||
clickOnSheet(R.id.leaveSpace)
|
clickOnSheet(R.id.leaveSpace)
|
||||||
waitUntilDialogVisible(ViewMatchers.withId(R.id.leaveButton))
|
|
||||||
clickOn(R.id.leave_selected)
|
|
||||||
waitUntilActivityVisible<SpaceLeaveAdvancedActivity> {
|
waitUntilActivityVisible<SpaceLeaveAdvancedActivity> {
|
||||||
waitUntilViewVisible(ViewMatchers.withId(R.id.roomList))
|
waitUntilViewVisible(ViewMatchers.withId(R.id.roomList))
|
||||||
}
|
}
|
||||||
|
clickOn(R.id.spaceLeaveSelectAll)
|
||||||
clickOn(R.id.spaceLeaveButton)
|
clickOn(R.id.spaceLeaveButton)
|
||||||
waitUntilViewVisible(ViewMatchers.withId(R.id.groupListView))
|
waitUntilViewVisible(ViewMatchers.withId(R.id.groupListView))
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AppBarLayout.Behavior] subclass with a possibility to disable behavior.
|
||||||
|
* Useful for cases when in some view state we want prevent toolbar from collapsing/expanding by scroll events
|
||||||
|
*/
|
||||||
|
class ToggleableAppBarLayoutBehavior : AppBarLayout.Behavior {
|
||||||
|
constructor() : super()
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||||
|
|
||||||
|
var isEnabled = true
|
||||||
|
|
||||||
|
override fun onStartNestedScroll(parent: CoordinatorLayout,
|
||||||
|
child: AppBarLayout,
|
||||||
|
directTargetChild: View,
|
||||||
|
target: View,
|
||||||
|
nestedScrollAxes: Int,
|
||||||
|
type: Int): Boolean {
|
||||||
|
return isEnabled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNestedScroll(coordinatorLayout: CoordinatorLayout,
|
||||||
|
child: AppBarLayout,
|
||||||
|
target: View,
|
||||||
|
dxConsumed: Int,
|
||||||
|
dyConsumed: Int,
|
||||||
|
dxUnconsumed: Int,
|
||||||
|
dyUnconsumed: Int,
|
||||||
|
type: Int,
|
||||||
|
consumed: IntArray) {
|
||||||
|
if (!isEnabled) return
|
||||||
|
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout,
|
||||||
|
child: AppBarLayout,
|
||||||
|
target: View,
|
||||||
|
dx: Int,
|
||||||
|
dy: Int,
|
||||||
|
consumed: IntArray,
|
||||||
|
type: Int) {
|
||||||
|
if (!isEnabled) return
|
||||||
|
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.home.room.list
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||||
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_space_directory_filter_no_results)
|
||||||
|
abstract class SpaceDirectoryFilterNoResults : VectorEpoxyModel<SpaceDirectoryFilterNoResults.Holder>() {
|
||||||
|
class Holder : VectorEpoxyHolder()
|
||||||
|
}
|
@ -1,194 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.Parcelable
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.text.toSpannable
|
|
||||||
import androidx.core.view.isInvisible
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import com.airbnb.mvrx.Fail
|
|
||||||
import com.airbnb.mvrx.Loading
|
|
||||||
import com.airbnb.mvrx.args
|
|
||||||
import com.airbnb.mvrx.parentFragmentViewModel
|
|
||||||
import com.airbnb.mvrx.withState
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import im.vector.app.R
|
|
||||||
import im.vector.app.core.error.ErrorFormatter
|
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
|
||||||
import im.vector.app.core.extensions.setTextOrHide
|
|
||||||
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
|
|
||||||
import im.vector.app.core.resources.ColorProvider
|
|
||||||
import im.vector.app.core.utils.styleMatchingText
|
|
||||||
import im.vector.app.databinding.BottomSheetLeaveSpaceBinding
|
|
||||||
import im.vector.app.features.displayname.getBestName
|
|
||||||
import im.vector.app.features.spaces.leave.SpaceLeaveAdvancedActivity
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import me.gujun.android.span.span
|
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
|
||||||
import reactivecircus.flowbinding.android.widget.checkedChanges
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class LeaveSpaceBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetLeaveSpaceBinding>() {
|
|
||||||
|
|
||||||
val settingsViewModel: SpaceMenuViewModel by parentFragmentViewModel()
|
|
||||||
|
|
||||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetLeaveSpaceBinding {
|
|
||||||
return BottomSheetLeaveSpaceBinding.inflate(inflater, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject lateinit var colorProvider: ColorProvider
|
|
||||||
@Inject lateinit var errorFormatter: ErrorFormatter
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
data class Args(
|
|
||||||
val spaceId: String
|
|
||||||
) : Parcelable
|
|
||||||
|
|
||||||
override val showExpanded = true
|
|
||||||
|
|
||||||
private val spaceArgs: SpaceBottomSheetSettingsArgs by args()
|
|
||||||
|
|
||||||
private val cherryPickLeaveActivityResult = registerStartForActivityResult { activityResult ->
|
|
||||||
if (activityResult.resultCode == Activity.RESULT_OK) {
|
|
||||||
// nothing actually?
|
|
||||||
} else {
|
|
||||||
// move back to default
|
|
||||||
settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveAll)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
views.autoLeaveRadioGroup.checkedChanges()
|
|
||||||
.onEach {
|
|
||||||
when (it) {
|
|
||||||
views.leaveAll.id -> {
|
|
||||||
settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveAll)
|
|
||||||
}
|
|
||||||
views.leaveNone.id -> {
|
|
||||||
settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveNone)
|
|
||||||
}
|
|
||||||
views.leaveSelected.id -> {
|
|
||||||
settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveSelected)
|
|
||||||
// launch dedicated activity
|
|
||||||
cherryPickLeaveActivityResult.launch(
|
|
||||||
SpaceLeaveAdvancedActivity.newIntent(requireContext(), spaceArgs.spaceId)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
|
||||||
|
|
||||||
views.leaveButton.debouncedClicks {
|
|
||||||
settingsViewModel.handle(SpaceLeaveViewAction.LeaveSpace)
|
|
||||||
}
|
|
||||||
|
|
||||||
views.cancelButton.debouncedClicks {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun invalidate() = withState(settingsViewModel) { state ->
|
|
||||||
super.invalidate()
|
|
||||||
|
|
||||||
val spaceSummary = state.spaceSummary ?: return@withState
|
|
||||||
val bestName = spaceSummary.toMatrixItem().getBestName()
|
|
||||||
val commonText = getString(R.string.space_leave_prompt_msg_with_name, bestName)
|
|
||||||
.toSpannable().styleMatchingText(bestName, Typeface.BOLD)
|
|
||||||
|
|
||||||
val warningMessage: CharSequence = if (spaceSummary.otherMemberIds.isEmpty()) {
|
|
||||||
span {
|
|
||||||
+commonText
|
|
||||||
+"\n\n"
|
|
||||||
span(getString(R.string.space_leave_prompt_msg_only_you)) {
|
|
||||||
textColor = colorProvider.getColorFromAttribute(R.attr.colorError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (state.isLastAdmin) {
|
|
||||||
span {
|
|
||||||
+commonText
|
|
||||||
+"\n\n"
|
|
||||||
span(getString(R.string.space_leave_prompt_msg_as_admin)) {
|
|
||||||
textColor = colorProvider.getColorFromAttribute(R.attr.colorError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!spaceSummary.isPublic) {
|
|
||||||
span {
|
|
||||||
+commonText
|
|
||||||
+"\n\n"
|
|
||||||
span(getString(R.string.space_leave_prompt_msg_private)) {
|
|
||||||
textColor = colorProvider.getColorFromAttribute(R.attr.colorError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
commonText
|
|
||||||
}
|
|
||||||
|
|
||||||
views.bottomLeaveSpaceWarningText.setTextOrHide(warningMessage)
|
|
||||||
|
|
||||||
views.inlineErrorText.setTextOrHide(null)
|
|
||||||
if (state.leavingState is Loading) {
|
|
||||||
views.leaveButton.isInvisible = true
|
|
||||||
views.cancelButton.isInvisible = true
|
|
||||||
views.leaveProgress.isVisible = true
|
|
||||||
} else {
|
|
||||||
views.leaveButton.isInvisible = false
|
|
||||||
views.cancelButton.isInvisible = false
|
|
||||||
views.leaveProgress.isVisible = false
|
|
||||||
if (state.leavingState is Fail) {
|
|
||||||
views.inlineErrorText.setTextOrHide(errorFormatter.toHumanReadable(state.leavingState.error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val hasChildren = (spaceSummary.spaceChildren?.size ?: 0) > 0
|
|
||||||
if (hasChildren) {
|
|
||||||
views.autoLeaveRadioGroup.isVisible = true
|
|
||||||
when (state.leaveMode) {
|
|
||||||
SpaceMenuState.LeaveMode.LEAVE_ALL -> {
|
|
||||||
views.autoLeaveRadioGroup.check(views.leaveAll.id)
|
|
||||||
}
|
|
||||||
SpaceMenuState.LeaveMode.LEAVE_NONE -> {
|
|
||||||
views.autoLeaveRadioGroup.check(views.leaveNone.id)
|
|
||||||
}
|
|
||||||
SpaceMenuState.LeaveMode.LEAVE_SELECTED -> {
|
|
||||||
views.autoLeaveRadioGroup.check(views.leaveSelected.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
views.autoLeaveRadioGroup.isVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
fun newInstance(spaceId: String): LeaveSpaceBottomSheet {
|
|
||||||
return LeaveSpaceBottomSheet().apply {
|
|
||||||
setArguments(SpaceBottomSheetSettingsArgs(spaceId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -35,6 +35,7 @@ import im.vector.app.features.home.AvatarRenderer
|
|||||||
import im.vector.app.features.navigation.Navigator
|
import im.vector.app.features.navigation.Navigator
|
||||||
import im.vector.app.features.rageshake.BugReporter
|
import im.vector.app.features.rageshake.BugReporter
|
||||||
import im.vector.app.features.roomprofile.RoomProfileActivity
|
import im.vector.app.features.roomprofile.RoomProfileActivity
|
||||||
|
import im.vector.app.features.spaces.leave.SpaceLeaveAdvancedActivity
|
||||||
import im.vector.app.features.spaces.manage.ManageType
|
import im.vector.app.features.spaces.manage.ManageType
|
||||||
import im.vector.app.features.spaces.manage.SpaceManageActivity
|
import im.vector.app.features.spaces.manage.SpaceManageActivity
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
@ -109,7 +110,7 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
|
|||||||
}
|
}
|
||||||
|
|
||||||
views.leaveSpace.views.bottomSheetActionClickableZone.debouncedClicks {
|
views.leaveSpace.views.bottomSheetActionClickableZone.debouncedClicks {
|
||||||
LeaveSpaceBottomSheet.newInstance(spaceArgs.spaceId).show(childFragmentManager, "LOGOUT")
|
startActivity(SpaceLeaveAdvancedActivity.newIntent(requireContext(), spaceArgs.spaceId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ import im.vector.app.core.ui.list.genericEmptyWithActionItem
|
|||||||
import im.vector.app.core.ui.list.genericPillItem
|
import im.vector.app.core.ui.list.genericPillItem
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.home.room.list.spaceChildInfoItem
|
import im.vector.app.features.home.room.list.spaceChildInfoItem
|
||||||
|
import im.vector.app.features.home.room.list.spaceDirectoryFilterNoResults
|
||||||
|
import im.vector.app.features.spaces.manage.SpaceChildInfoMatchFilter
|
||||||
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
|
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
|
||||||
import me.gujun.android.span.span
|
import me.gujun.android.span.span
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
@ -53,6 +55,7 @@ class SpaceDirectoryController @Inject constructor(
|
|||||||
) : TypedEpoxyController<SpaceDirectoryState>() {
|
) : TypedEpoxyController<SpaceDirectoryState>() {
|
||||||
|
|
||||||
interface InteractionListener {
|
interface InteractionListener {
|
||||||
|
fun onFilterQueryChanged(query: String?)
|
||||||
fun onButtonClick(spaceChildInfo: SpaceChildInfo)
|
fun onButtonClick(spaceChildInfo: SpaceChildInfo)
|
||||||
fun onSpaceChildClick(spaceChildInfo: SpaceChildInfo)
|
fun onSpaceChildClick(spaceChildInfo: SpaceChildInfo)
|
||||||
fun onRoomClick(spaceChildInfo: SpaceChildInfo)
|
fun onRoomClick(spaceChildInfo: SpaceChildInfo)
|
||||||
@ -62,6 +65,7 @@ class SpaceDirectoryController @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var listener: InteractionListener? = null
|
var listener: InteractionListener? = null
|
||||||
|
private val matchFilter = SpaceChildInfoMatchFilter()
|
||||||
|
|
||||||
override fun buildModels(data: SpaceDirectoryState?) {
|
override fun buildModels(data: SpaceDirectoryState?) {
|
||||||
val host = this
|
val host = this
|
||||||
@ -76,7 +80,7 @@ class SpaceDirectoryController @Inject constructor(
|
|||||||
val failure = results.error
|
val failure = results.error
|
||||||
if (failure is Failure.ServerError && failure.error.code == M_UNRECOGNIZED) {
|
if (failure is Failure.ServerError && failure.error.code == M_UNRECOGNIZED) {
|
||||||
genericPillItem {
|
genericPillItem {
|
||||||
id("HS no Support")
|
id("hs_no_support")
|
||||||
imageRes(R.drawable.error)
|
imageRes(R.drawable.error)
|
||||||
tintIcon(false)
|
tintIcon(false)
|
||||||
text(
|
text(
|
||||||
@ -132,7 +136,15 @@ class SpaceDirectoryController @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
flattenChildInfo.forEach { info ->
|
matchFilter.filter = data?.currentFilter ?: ""
|
||||||
|
val filteredChildInfo = flattenChildInfo.filter { matchFilter.test(it) }
|
||||||
|
|
||||||
|
if (filteredChildInfo.isEmpty()) {
|
||||||
|
spaceDirectoryFilterNoResults {
|
||||||
|
id("no_results")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filteredChildInfo.forEach { info ->
|
||||||
val isSpace = info.roomType == RoomType.SPACE
|
val isSpace = info.roomType == RoomType.SPACE
|
||||||
val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true
|
val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true
|
||||||
val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false
|
val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false
|
||||||
@ -172,6 +184,7 @@ class SpaceDirectoryController @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (hierarchySummary?.nextToken != null) {
|
if (hierarchySummary?.nextToken != null) {
|
||||||
val paginationStatus = data.paginationStatus[currentRootId] ?: Uninitialized
|
val paginationStatus = data.paginationStatus[currentRootId] ?: Uninitialized
|
||||||
if (paginationStatus is Fail) {
|
if (paginationStatus is Fail) {
|
||||||
|
@ -23,6 +23,7 @@ import android.view.Menu
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.text.toSpannable
|
import androidx.core.text.toSpannable
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
@ -44,7 +45,6 @@ import im.vector.app.core.utils.openUrlInExternalBrowser
|
|||||||
import im.vector.app.databinding.FragmentSpaceDirectoryBinding
|
import im.vector.app.databinding.FragmentSpaceDirectoryBinding
|
||||||
import im.vector.app.features.analytics.plan.MobileScreen
|
import im.vector.app.features.analytics.plan.MobileScreen
|
||||||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
|
||||||
import im.vector.app.features.matrixto.SpaceCardRenderer
|
|
||||||
import im.vector.app.features.permalink.PermalinkHandler
|
import im.vector.app.features.permalink.PermalinkHandler
|
||||||
import im.vector.app.features.spaces.manage.ManageType
|
import im.vector.app.features.spaces.manage.ManageType
|
||||||
import im.vector.app.features.spaces.manage.SpaceAddRoomSpaceChooserBottomSheet
|
import im.vector.app.features.spaces.manage.SpaceAddRoomSpaceChooserBottomSheet
|
||||||
@ -63,7 +63,6 @@ data class SpaceDirectoryArgs(
|
|||||||
class SpaceDirectoryFragment @Inject constructor(
|
class SpaceDirectoryFragment @Inject constructor(
|
||||||
private val epoxyController: SpaceDirectoryController,
|
private val epoxyController: SpaceDirectoryController,
|
||||||
private val permalinkHandler: PermalinkHandler,
|
private val permalinkHandler: PermalinkHandler,
|
||||||
private val spaceCardRenderer: SpaceCardRenderer,
|
|
||||||
private val colorProvider: ColorProvider
|
private val colorProvider: ColorProvider
|
||||||
) : VectorBaseFragment<FragmentSpaceDirectoryBinding>(),
|
) : VectorBaseFragment<FragmentSpaceDirectoryBinding>(),
|
||||||
SpaceDirectoryController.InteractionListener,
|
SpaceDirectoryController.InteractionListener,
|
||||||
@ -123,9 +122,6 @@ class SpaceDirectoryFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
views.spaceCard.matrixToCardMainButton.isVisible = false
|
|
||||||
views.spaceCard.matrixToCardSecondaryButton.isVisible = false
|
|
||||||
|
|
||||||
// Hide FAB when list is scrolling
|
// Hide FAB when list is scrolling
|
||||||
views.spaceDirectoryList.addOnScrollListener(
|
views.spaceDirectoryList.addOnScrollListener(
|
||||||
object : RecyclerView.OnScrollListener() {
|
object : RecyclerView.OnScrollListener() {
|
||||||
@ -167,18 +163,37 @@ class SpaceDirectoryFragment @Inject constructor(
|
|||||||
// it's the root
|
// it's the root
|
||||||
toolbar?.setTitle(R.string.space_explore_activity_title)
|
toolbar?.setTitle(R.string.space_explore_activity_title)
|
||||||
} else {
|
} else {
|
||||||
toolbar?.title = state.currentRootSummary?.name
|
val spaceName = state.currentRootSummary?.name
|
||||||
?: state.currentRootSummary?.canonicalAlias
|
?: state.currentRootSummary?.canonicalAlias
|
||||||
?: getString(R.string.space_explore_activity_title)
|
|
||||||
|
if (spaceName != null) {
|
||||||
|
toolbar?.title = spaceName
|
||||||
|
toolbar?.subtitle = getString(R.string.space_explore_activity_title)
|
||||||
|
} else {
|
||||||
|
toolbar?.title = getString(R.string.space_explore_activity_title)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spaceCardRenderer.render(state.currentRootSummary, emptyList(), this, views.spaceCard, showDescription = false)
|
|
||||||
views.addOrCreateChatRoomButton.isVisible = state.canAddRooms
|
views.addOrCreateChatRoomButton.isVisible = state.canAddRooms
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state ->
|
override fun onPrepareOptionsMenu(menu: Menu) = withState(viewModel) { state ->
|
||||||
menu.findItem(R.id.spaceAddRoom)?.isVisible = state.canAddRooms
|
menu.findItem(R.id.spaceAddRoom)?.isVisible = state.canAddRooms
|
||||||
menu.findItem(R.id.spaceCreateRoom)?.isVisible = false // Not yet implemented
|
menu.findItem(R.id.spaceCreateRoom)?.isVisible = false // Not yet implemented
|
||||||
|
|
||||||
|
menu.findItem(R.id.spaceSearch)?.let { searchItem ->
|
||||||
|
val searchView = searchItem.actionView as SearchView
|
||||||
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
|
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String?): Boolean {
|
||||||
|
onFilterQueryChanged(newText)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
super.onPrepareOptionsMenu(menu)
|
super.onPrepareOptionsMenu(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +213,10 @@ class SpaceDirectoryFragment @Inject constructor(
|
|||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFilterQueryChanged(query: String?) {
|
||||||
|
viewModel.handle(SpaceDirectoryViewAction.FilterRooms(query))
|
||||||
|
}
|
||||||
|
|
||||||
override fun onButtonClick(spaceChildInfo: SpaceChildInfo) {
|
override fun onButtonClick(spaceChildInfo: SpaceChildInfo) {
|
||||||
viewModel.handle(SpaceDirectoryViewAction.JoinOrOpen(spaceChildInfo))
|
viewModel.handle(SpaceDirectoryViewAction.JoinOrOpen(spaceChildInfo))
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
|||||||
sealed class SpaceDirectoryViewAction : VectorViewModelAction {
|
sealed class SpaceDirectoryViewAction : VectorViewModelAction {
|
||||||
data class ExploreSubSpace(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
|
data class ExploreSubSpace(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
|
||||||
data class JoinOrOpen(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
|
data class JoinOrOpen(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
|
||||||
|
data class FilterRooms(val query: String?) : SpaceDirectoryViewAction()
|
||||||
data class ShowDetails(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
|
data class ShowDetails(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
|
||||||
data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewAction()
|
data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewAction()
|
||||||
object CreateNewRoom : SpaceDirectoryViewAction()
|
object CreateNewRoom : SpaceDirectoryViewAction()
|
||||||
|
@ -225,8 +225,15 @@ class SpaceDirectoryViewModel @AssistedInject constructor(
|
|||||||
_viewEvents.post(SpaceDirectoryViewEvents.NavigateToCreateNewRoom(state.currentRootSummary?.roomId ?: initialState.spaceId))
|
_viewEvents.post(SpaceDirectoryViewEvents.NavigateToCreateNewRoom(state.currentRootSummary?.roomId ?: initialState.spaceId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is SpaceDirectoryViewAction.FilterRooms -> {
|
||||||
|
filter(action.query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filter(query: String?) {
|
||||||
|
setState { copy(currentFilter = query.orEmpty()) }
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleBack() = withState { state ->
|
private fun handleBack() = withState { state ->
|
||||||
if (state.hierarchyStack.isEmpty()) {
|
if (state.hierarchyStack.isEmpty()) {
|
||||||
|
@ -21,6 +21,9 @@ import im.vector.app.core.platform.VectorViewModelAction
|
|||||||
sealed class SpaceLeaveAdvanceViewAction : VectorViewModelAction {
|
sealed class SpaceLeaveAdvanceViewAction : VectorViewModelAction {
|
||||||
data class ToggleSelection(val roomId: String) : SpaceLeaveAdvanceViewAction()
|
data class ToggleSelection(val roomId: String) : SpaceLeaveAdvanceViewAction()
|
||||||
data class UpdateFilter(val filter: String) : SpaceLeaveAdvanceViewAction()
|
data class UpdateFilter(val filter: String) : SpaceLeaveAdvanceViewAction()
|
||||||
|
data class SetFilteringEnabled(val isEnabled: Boolean) : SpaceLeaveAdvanceViewAction()
|
||||||
object DoLeave : SpaceLeaveAdvanceViewAction()
|
object DoLeave : SpaceLeaveAdvanceViewAction()
|
||||||
object ClearError : SpaceLeaveAdvanceViewAction()
|
object ClearError : SpaceLeaveAdvanceViewAction()
|
||||||
|
object SelectAll : SpaceLeaveAdvanceViewAction()
|
||||||
|
object SelectNone : SpaceLeaveAdvanceViewAction()
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,11 @@ data class SpaceLeaveAdvanceViewState(
|
|||||||
val allChildren: Async<List<RoomSummary>> = Uninitialized,
|
val allChildren: Async<List<RoomSummary>> = Uninitialized,
|
||||||
val selectedRooms: List<String> = emptyList(),
|
val selectedRooms: List<String> = emptyList(),
|
||||||
val currentFilter: String = "",
|
val currentFilter: String = "",
|
||||||
val leaveState: Async<Unit> = Uninitialized
|
val leaveState: Async<Unit> = Uninitialized,
|
||||||
|
val isFilteringEnabled: Boolean = false,
|
||||||
|
val isLastAdmin: Boolean = false
|
||||||
) : MavericksState {
|
) : MavericksState {
|
||||||
|
|
||||||
constructor(args: SpaceBottomSheetSettingsArgs) : this(
|
constructor(args: SpaceBottomSheetSettingsArgs) : this(
|
||||||
spaceId = args.spaceId
|
spaceId = args.spaceId
|
||||||
)
|
)
|
||||||
|
@ -18,20 +18,23 @@ package im.vector.app.features.spaces.leave
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.appcompat.widget.SearchView
|
||||||
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.activityViewModel
|
import com.airbnb.mvrx.activityViewModel
|
||||||
import com.airbnb.mvrx.withState
|
import com.airbnb.mvrx.withState
|
||||||
|
import im.vector.app.R
|
||||||
import im.vector.app.core.extensions.cleanup
|
import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.configureWith
|
import im.vector.app.core.extensions.configureWith
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.app.core.utils.ToggleableAppBarLayoutBehavior
|
||||||
import im.vector.app.databinding.FragmentSpaceLeaveAdvancedBinding
|
import im.vector.app.databinding.FragmentSpaceLeaveAdvancedBinding
|
||||||
import kotlinx.coroutines.flow.debounce
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
import reactivecircus.flowbinding.appcompat.queryTextChanges
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class SpaceLeaveAdvancedFragment @Inject constructor(
|
class SpaceLeaveAdvancedFragment @Inject constructor(
|
||||||
@ -44,11 +47,33 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
|
|||||||
|
|
||||||
val viewModel: SpaceLeaveAdvancedViewModel by activityViewModel()
|
val viewModel: SpaceLeaveAdvancedViewModel by activityViewModel()
|
||||||
|
|
||||||
|
override fun getMenuRes() = R.menu.menu_space_leave
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
setupToolbar(views.toolbar)
|
|
||||||
.allowBack()
|
|
||||||
controller.listener = this
|
controller.listener = this
|
||||||
|
|
||||||
|
withState(viewModel) { state ->
|
||||||
|
setupToolbar(views.toolbar)
|
||||||
|
.setSubtitle(state.spaceSummary?.name)
|
||||||
|
.allowBack()
|
||||||
|
|
||||||
|
state.spaceSummary?.let { summary ->
|
||||||
|
val warningMessage: CharSequence? = when {
|
||||||
|
summary.otherMemberIds.isEmpty() -> getString(R.string.space_leave_prompt_msg_only_you)
|
||||||
|
state.isLastAdmin -> getString(R.string.space_leave_prompt_msg_as_admin)
|
||||||
|
!summary.isPublic -> getString(R.string.space_leave_prompt_msg_private)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
views.spaceLeavePromptDescription.isVisible = warningMessage != null
|
||||||
|
views.spaceLeavePromptDescription.text = warningMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
views.spaceLeavePromptTitle.text = getString(R.string.space_leave_prompt_msg_with_name, state.spaceSummary?.name ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
views.roomList.configureWith(controller)
|
views.roomList.configureWith(controller)
|
||||||
views.spaceLeaveCancel.debouncedClicks { requireActivity().finish() }
|
views.spaceLeaveCancel.debouncedClicks { requireActivity().finish() }
|
||||||
|
|
||||||
@ -56,12 +81,23 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
|
|||||||
viewModel.handle(SpaceLeaveAdvanceViewAction.DoLeave)
|
viewModel.handle(SpaceLeaveAdvanceViewAction.DoLeave)
|
||||||
}
|
}
|
||||||
|
|
||||||
views.publicRoomsFilter.queryTextChanges()
|
views.spaceLeaveSelectGroup.setOnCheckedChangeListener { _, optionId ->
|
||||||
.debounce(100)
|
when (optionId) {
|
||||||
.onEach {
|
R.id.spaceLeaveSelectAll -> viewModel.handle(SpaceLeaveAdvanceViewAction.SelectAll)
|
||||||
viewModel.handle(SpaceLeaveAdvanceViewAction.UpdateFilter(it.toString()))
|
R.id.spaceLeaveSelectNone -> viewModel.handle(SpaceLeaveAdvanceViewAction.SelectNone)
|
||||||
}
|
}
|
||||||
.launchIn(viewLifecycleOwner.lifecycleScope)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||||
|
menu.findItem(R.id.menu_space_leave_search)?.let { searchItem ->
|
||||||
|
searchItem.bind(
|
||||||
|
onExpanded = { viewModel.handle(SpaceLeaveAdvanceViewAction.SetFilteringEnabled(isEnabled = true)) },
|
||||||
|
onCollapsed = { viewModel.handle(SpaceLeaveAdvanceViewAction.SetFilteringEnabled(isEnabled = false)) },
|
||||||
|
onTextChanged = { viewModel.handle(SpaceLeaveAdvanceViewAction.UpdateFilter(it)) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
super.onPrepareOptionsMenu(menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
@ -72,10 +108,63 @@ class SpaceLeaveAdvancedFragment @Inject constructor(
|
|||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
super.invalidate()
|
super.invalidate()
|
||||||
|
|
||||||
|
if (state.isFilteringEnabled) {
|
||||||
|
views.appBarLayout.setExpanded(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAppBarBehaviorState(state)
|
||||||
|
updateRadioButtonsState(state)
|
||||||
|
|
||||||
controller.setData(state)
|
controller.setData(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemSelected(roomSummary: RoomSummary) {
|
override fun onItemSelected(roomSummary: RoomSummary) {
|
||||||
viewModel.handle(SpaceLeaveAdvanceViewAction.ToggleSelection(roomSummary.roomId))
|
viewModel.handle(SpaceLeaveAdvanceViewAction.ToggleSelection(roomSummary.roomId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateAppBarBehaviorState(state: SpaceLeaveAdvanceViewState) {
|
||||||
|
val behavior = (views.appBarLayout.layoutParams as CoordinatorLayout.LayoutParams).behavior as ToggleableAppBarLayoutBehavior
|
||||||
|
behavior.isEnabled = !state.isFilteringEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateRadioButtonsState(state: SpaceLeaveAdvanceViewState) {
|
||||||
|
(state.allChildren as? Success)?.invoke()?.size?.let { allChildrenCount ->
|
||||||
|
when (state.selectedRooms.size) {
|
||||||
|
0 -> views.spaceLeaveSelectNone.isChecked = true
|
||||||
|
allChildrenCount -> views.spaceLeaveSelectAll.isChecked = true
|
||||||
|
else -> views.spaceLeaveSelectSemi.isChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MenuItem.bind(
|
||||||
|
onExpanded: () -> Unit,
|
||||||
|
onCollapsed: () -> Unit,
|
||||||
|
onTextChanged: (String) -> Unit) {
|
||||||
|
setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
||||||
|
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||||
|
onExpanded()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||||
|
onCollapsed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
val searchView = actionView as SearchView
|
||||||
|
|
||||||
|
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
|
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String?): Boolean {
|
||||||
|
onTextChanged(newText ?: "")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,9 +36,14 @@ import okhttp3.internal.toImmutableList
|
|||||||
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
|
import org.matrix.android.sdk.api.query.ActiveSpaceFilter
|
||||||
import org.matrix.android.sdk.api.query.RoomCategoryFilter
|
import org.matrix.android.sdk.api.query.RoomCategoryFilter
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.getRoomSummary
|
import org.matrix.android.sdk.api.session.room.getStateEvent
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
|
||||||
|
import org.matrix.android.sdk.api.session.room.powerlevels.Role
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
import org.matrix.android.sdk.flow.flow
|
import org.matrix.android.sdk.flow.flow
|
||||||
import org.matrix.android.sdk.flow.unwrap
|
import org.matrix.android.sdk.flow.unwrap
|
||||||
@ -50,52 +55,24 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor(
|
|||||||
private val appStateHandler: AppStateHandler
|
private val appStateHandler: AppStateHandler
|
||||||
) : VectorViewModel<SpaceLeaveAdvanceViewState, SpaceLeaveAdvanceViewAction, EmptyViewEvents>(initialState) {
|
) : VectorViewModel<SpaceLeaveAdvanceViewState, SpaceLeaveAdvanceViewAction, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
override fun handle(action: SpaceLeaveAdvanceViewAction) = withState { state ->
|
|
||||||
when (action) {
|
|
||||||
is SpaceLeaveAdvanceViewAction.ToggleSelection -> {
|
|
||||||
val existing = state.selectedRooms.toMutableList()
|
|
||||||
if (existing.contains(action.roomId)) {
|
|
||||||
existing.remove(action.roomId)
|
|
||||||
} else {
|
|
||||||
existing.add(action.roomId)
|
|
||||||
}
|
|
||||||
setState {
|
|
||||||
copy(
|
|
||||||
selectedRooms = existing.toImmutableList()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is SpaceLeaveAdvanceViewAction.UpdateFilter -> {
|
|
||||||
setState { copy(currentFilter = action.filter) }
|
|
||||||
}
|
|
||||||
SpaceLeaveAdvanceViewAction.DoLeave -> {
|
|
||||||
setState { copy(leaveState = Loading()) }
|
|
||||||
viewModelScope.launch {
|
|
||||||
try {
|
|
||||||
state.selectedRooms.forEach {
|
|
||||||
try {
|
|
||||||
session.roomService().leaveRoom(it)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
// silently ignore?
|
|
||||||
Timber.e(failure, "Fail to leave sub rooms/spaces")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
session.spaceService().leaveSpace(initialState.spaceId)
|
|
||||||
// We observe the membership and to dismiss when we have remote echo of leaving
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
setState { copy(leaveState = Fail(failure)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SpaceLeaveAdvanceViewAction.ClearError -> {
|
|
||||||
setState { copy(leaveState = Uninitialized) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val spaceSummary = session.getRoomSummary(initialState.spaceId)
|
val space = session.getRoom(initialState.spaceId)
|
||||||
|
val spaceSummary = space?.roomSummary()
|
||||||
|
|
||||||
|
val powerLevelsEvent = space?.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS)
|
||||||
|
powerLevelsEvent?.content?.toModel<PowerLevelsContent>()?.let { powerLevelsContent ->
|
||||||
|
val powerLevelsHelper = PowerLevelsHelper(powerLevelsContent)
|
||||||
|
val isAdmin = powerLevelsHelper.getUserRole(session.myUserId) is Role.Admin
|
||||||
|
val otherAdminCount = spaceSummary?.otherMemberIds
|
||||||
|
?.map { powerLevelsHelper.getUserRole(it) }
|
||||||
|
?.count { it is Role.Admin }
|
||||||
|
?: 0
|
||||||
|
val isLastAdmin = isAdmin && otherAdminCount == 0
|
||||||
|
setState {
|
||||||
|
copy(isLastAdmin = isLastAdmin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setState { copy(spaceSummary = spaceSummary) }
|
setState { copy(spaceSummary = spaceSummary) }
|
||||||
session.getRoom(initialState.spaceId)?.let { room ->
|
session.getRoom(initialState.spaceId)?.let { room ->
|
||||||
room.flow().liveRoomSummary()
|
room.flow().liveRoomSummary()
|
||||||
@ -127,6 +104,62 @@ class SpaceLeaveAdvancedViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun handle(action: SpaceLeaveAdvanceViewAction) {
|
||||||
|
when (action) {
|
||||||
|
is SpaceLeaveAdvanceViewAction.UpdateFilter -> setState { copy(currentFilter = action.filter) }
|
||||||
|
SpaceLeaveAdvanceViewAction.ClearError -> setState { copy(leaveState = Uninitialized) }
|
||||||
|
SpaceLeaveAdvanceViewAction.SelectNone -> setState { copy(selectedRooms = emptyList()) }
|
||||||
|
is SpaceLeaveAdvanceViewAction.SetFilteringEnabled -> setState { copy(isFilteringEnabled = action.isEnabled) }
|
||||||
|
is SpaceLeaveAdvanceViewAction.ToggleSelection -> handleSelectionToggle(action)
|
||||||
|
SpaceLeaveAdvanceViewAction.DoLeave -> handleLeave()
|
||||||
|
SpaceLeaveAdvanceViewAction.SelectAll -> handleSelectAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSelectAll() = withState { state ->
|
||||||
|
val filteredRooms = (state.allChildren as? Success)?.invoke()?.filter {
|
||||||
|
it.name.contains(state.currentFilter, true)
|
||||||
|
}
|
||||||
|
filteredRooms?.let {
|
||||||
|
setState { copy(selectedRooms = it.map { it.roomId }) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLeave() = withState { state ->
|
||||||
|
setState { copy(leaveState = Loading()) }
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
state.selectedRooms.forEach {
|
||||||
|
try {
|
||||||
|
session.roomService().leaveRoom(it)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
// silently ignore?
|
||||||
|
Timber.e(failure, "Fail to leave sub rooms/spaces")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.spaceService().leaveSpace(initialState.spaceId)
|
||||||
|
// We observe the membership and to dismiss when we have remote echo of leaving
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
setState { copy(leaveState = Fail(failure)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSelectionToggle(action: SpaceLeaveAdvanceViewAction.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.toImmutableList(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory : MavericksAssistedViewModelFactory<SpaceLeaveAdvancedViewModel, SpaceLeaveAdvanceViewState> {
|
interface Factory : MavericksAssistedViewModelFactory<SpaceLeaveAdvancedViewModel, SpaceLeaveAdvanceViewState> {
|
||||||
override fun create(initialState: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel
|
override fun create(initialState: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?android:colorBackground"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/bottom_leave_space_warning_text"
|
|
||||||
style="@style/Widget.Vector.TextView.Subtitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:textColor="?vctr_content_primary"
|
|
||||||
tools:text="@string/space_leave_prompt_msg_with_name" />
|
|
||||||
|
|
||||||
<RadioGroup
|
|
||||||
android:id="@+id/autoLeaveRadioGroup"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingStart="?dialogPreferredPadding"
|
|
||||||
android:paddingTop="12dp"
|
|
||||||
android:paddingEnd="?dialogPreferredPadding"
|
|
||||||
android:paddingBottom="12dp">
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/leave_all"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/leave_all_rooms_and_spaces"
|
|
||||||
tools:checked="true" />
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/leave_none"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minWidth="180dp"
|
|
||||||
android:text="@string/dont_leave_any" />
|
|
||||||
|
|
||||||
<RadioButton
|
|
||||||
android:id="@+id/leave_selected"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minWidth="180dp"
|
|
||||||
android:text="@string/leave_specific_ones" />
|
|
||||||
|
|
||||||
</RadioGroup>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/inlineErrorText"
|
|
||||||
style="@style/Widget.Vector.TextView.Body"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_marginEnd="@dimen/layout_horizontal_margin"
|
|
||||||
android:textColor="?colorError"
|
|
||||||
tools:visibility="visible"
|
|
||||||
tools:text="@string/error_no_network"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:layout_marginEnd="16dp">
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/leaveProgress"
|
|
||||||
style="?android:attr/progressBarStyle"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/leaveButton"
|
|
||||||
style="@style/Widget.Vector.Button.Destructive"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:text="@string/leave_space" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/cancelButton"
|
|
||||||
style="@style/Widget.Vector.Button.Text"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:text="@string/action_cancel" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -11,35 +11,12 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
|
||||||
android:id="@+id/spaceExploreCollapsingToolbarLayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:contentScrim="?android:colorBackground"
|
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
|
|
||||||
app:scrimAnimationDuration="250"
|
|
||||||
app:scrimVisibleHeightTrigger="120dp"
|
|
||||||
app:titleEnabled="false"
|
|
||||||
app:toolbarId="@id/toolbar">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="40dp">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/spaceCard"
|
|
||||||
layout="@layout/fragment_matrix_to_room_space_card" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:layout_collapseMode="pin" />
|
app:contentInsetStart="0dp">
|
||||||
|
</com.google.android.material.appbar.MaterialToolbar>
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
@ -57,7 +34,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|end"
|
android:layout_gravity="bottom|end"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="16dp "
|
android:layout_marginBottom="16dp"
|
||||||
android:contentDescription="@string/a11y_create_room"
|
android:contentDescription="@string/a11y_create_room"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
android:src="@drawable/ic_fab_add"
|
android:src="@drawable/ic_fab_add"
|
||||||
|
@ -16,41 +16,107 @@
|
|||||||
tools:listitem="@layout/item_room_to_add_in_space" />
|
tools:listitem="@layout/item_room_to_add_in_space" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appBarLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_behavior="im.vector.app.core.utils.ToggleableAppBarLayoutBehavior">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
|
android:id="@+id/spaceExploreCollapsingToolbarLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:contentScrim="?android:colorBackground"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|enterAlways|snap"
|
||||||
|
app:scrimAnimationDuration="250"
|
||||||
|
app:scrimVisibleHeightTrigger="120dp"
|
||||||
|
app:titleEnabled="false"
|
||||||
|
app:toolbarId="@id/toolbar">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="?attr/actionBarSize"
|
||||||
|
android:minHeight="0dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/spaceLeavePromptTitle"
|
||||||
|
style="@style/TextAppearance.Vector.Body.Medium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="@string/space_leave_prompt_msg_with_name" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/spaceLeavePromptDescription"
|
||||||
|
style="@style/TextAppearance.Vector.Body"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/space_leave_prompt_msg_only_you"
|
||||||
|
android:textColor="?vctr_content_secondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/spaceLeaveRadioButtonsTitle"
|
||||||
|
style="@style/TextAppearance.Vector.Subtitle.Medium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/space_leave_radio_buttons_title"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="?vctr_content_primary" />
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/spaceLeaveSelectGroup"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:weightSum="2">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatRadioButton
|
||||||
|
android:id="@+id/spaceLeaveSelectAll"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/space_leave_radio_button_all" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatRadioButton
|
||||||
|
android:id="@+id/spaceLeaveSelectNone"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/space_leave_radio_button_none" />
|
||||||
|
|
||||||
|
<!-- This view should never be visible! There are three possible states but only two buttons by design.-->
|
||||||
|
<!-- Third button is needed to make radiogroup work as expected, it's selected, but never shown-->
|
||||||
|
<androidx.appcompat.widget.AppCompatRadioButton
|
||||||
|
android:id="@+id/spaceLeaveSelectSemi"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
<!-- minHeight="0dp" is important to collapse on scroll -->
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:minHeight="0dp"
|
app:layout_collapseMode="pin"
|
||||||
app:title="@string/pick_tings_to_leave"
|
app:title="Leave space" />
|
||||||
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways"/>
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
|
||||||
<androidx.appcompat.widget.SearchView
|
|
||||||
android:id="@+id/publicRoomsFilter"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/addRoomToSpaceToolbar"
|
|
||||||
app:queryHint="@string/search_hint_room_name" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/spacePreviewButtonBar"
|
|
||||||
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:background="?vctr_system"
|
android:background="@color/palette_white"
|
||||||
android:elevation="2dp"
|
android:elevation="2dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="8dp">
|
android:padding="8dp"
|
||||||
|
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/spaceLeaveCancel"
|
android:id="@+id/spaceLeaveCancel"
|
||||||
@ -68,7 +134,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/leave_space" />
|
android:text="@string/leave_space" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -136,8 +136,8 @@
|
|||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:background="?vctr_list_separator_system"
|
android:background="?vctr_list_separator_system"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="@id/joinSuggestedRoomButton"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="@id/roomNameView"
|
||||||
app:layout_constraintTop_toBottomOf="@id/inlineErrorText" />
|
app:layout_constraintTop_toBottomOf="@id/inlineErrorText" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/space_explore_rooms_root"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:colorBackground"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="32dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
tools:viewBindingIgnore="true">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/Widget.Vector.TextView.Body.Medium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/space_explore_filter_no_result_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/Widget.Vector.TextView.Body"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/space_explore_filter_no_result_description"
|
||||||
|
android:textColor="?vctr_content_secondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/Widget.Vector.TextView.Body.Medium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/create_new_room"
|
||||||
|
android:textColor="?colorSecondary" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
@ -1,6 +1,16 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/spaceSearch"
|
||||||
|
android:icon="@drawable/ic_filter"
|
||||||
|
android:title="@string/search"
|
||||||
|
app:searchIcon="@drawable/ic_filter"
|
||||||
|
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||||
|
app:showAsAction="ifRoom|collapseActionView" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/spaceAddRoom"
|
android:id="@+id/spaceAddRoom"
|
||||||
android:title="@string/space_add_existing_rooms"
|
android:title="@string/space_add_existing_rooms"
|
||||||
|
13
vector/src/main/res/menu/menu_space_leave.xml
Normal file
13
vector/src/main/res/menu/menu_space_leave.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?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/menu_space_leave_search"
|
||||||
|
android:icon="@drawable/ic_filter"
|
||||||
|
android:title="@string/search"
|
||||||
|
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||||
|
app:searchIcon="@drawable/ic_filter"
|
||||||
|
app:showAsAction="ifRoom|collapseActionView" />
|
||||||
|
|
||||||
|
</menu>
|
@ -2837,14 +2837,27 @@
|
|||||||
<string name="space_explore_activity_title">Explore rooms</string>
|
<string name="space_explore_activity_title">Explore rooms</string>
|
||||||
<string name="space_add_child_title">Add rooms</string>
|
<string name="space_add_child_title">Add rooms</string>
|
||||||
<string name="leave_space">Leave</string>
|
<string name="leave_space">Leave</string>
|
||||||
|
|
||||||
|
<string name="space_leave_radio_buttons_title">Things in this space</string>
|
||||||
|
<string name="space_leave_radio_button_all">Leave all</string>
|
||||||
|
<string name="space_leave_radio_button_none">Leave none</string>
|
||||||
|
|
||||||
<string name="space_leave_prompt_msg_with_name">Are you sure you want to leave %s?</string>
|
<string name="space_leave_prompt_msg_with_name">Are you sure you want to leave %s?</string>
|
||||||
<string name="space_leave_prompt_msg_only_you">You are the only person here. If you leave, no one will be able to join in the future, including you.</string>
|
<string name="space_leave_prompt_msg_only_you">You are the only person here. If you leave, no one will be able to join in the future, including you.</string>
|
||||||
<string name="space_leave_prompt_msg_private">You won\'t be able to rejoin unless you are re-invited.</string>
|
<string name="space_leave_prompt_msg_private">You won\'t be able to rejoin unless you are re-invited.</string>
|
||||||
<string name="space_leave_prompt_msg_as_admin">You\'re the only admin of this space. Leaving it will mean no one has control over it.</string>
|
<string name="space_leave_prompt_msg_as_admin">You\'re the only admin of this space. Leaving it will mean no one has control over it.</string>
|
||||||
<string name="leave_all_rooms_and_spaces">Leave all rooms and spaces</string>
|
|
||||||
<string name="dont_leave_any">Don’t leave any rooms and spaces</string>
|
<!-- TODO delete -->
|
||||||
<string name="leave_specific_ones">Leave specific rooms and spaces…</string>
|
<string name="leave_all_rooms_and_spaces" tools:ignore="UnusedResources">Leave all rooms and spaces</string>
|
||||||
<string name="pick_tings_to_leave">Pick things to leave</string>
|
<!-- TODO delete -->
|
||||||
|
<string name="dont_leave_any" tools:ignore="UnusedResources">Don’t leave any rooms and spaces</string>
|
||||||
|
<!-- TODO delete -->
|
||||||
|
<string name="leave_specific_ones" tools:ignore="UnusedResources">Leave specific rooms and spaces…</string>
|
||||||
|
<!-- TODO delete -->
|
||||||
|
<string name="pick_tings_to_leave" tools:ignore="UnusedResources">Pick things to leave</string>
|
||||||
|
|
||||||
|
<string name="space_explore_filter_no_result_title">No results found</string>
|
||||||
|
<string name="space_explore_filter_no_result_description">Some results may be hidden because they’re private and you need an invite to them.</string>
|
||||||
|
|
||||||
<string name="space_add_existing_rooms">Add existing rooms and space</string>
|
<string name="space_add_existing_rooms">Add existing rooms and space</string>
|
||||||
<string name="space_add_existing_rooms_only">Add existing rooms</string>
|
<string name="space_add_existing_rooms_only">Add existing rooms</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user