diff --git a/changelog.d/3692.feature b/changelog.d/3692.feature
new file mode 100644
index 00000000000..5e6178eeb5b
--- /dev/null
+++ b/changelog.d/3692.feature
@@ -0,0 +1 @@
+Allow to also leave rooms when leaving a space
\ No newline at end of file
diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt
index 391140b9f32..4b0dd1f0a3f 100644
--- a/tools/check/forbidden_strings_in_code.txt
+++ b/tools/check/forbidden_strings_in_code.txt
@@ -162,7 +162,7 @@ Formatter\.formatShortFileSize===1
# android\.text\.TextUtils
### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
-enum class===105
+enum class===106
### Do not import temporary legacy classes
import org.matrix.android.sdk.internal.legacy.riot===3
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index 5aec41e46f9..6c9453a5641 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -319,6 +319,7 @@
+ () {
+
+ 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
+
+ override fun injectWith(injector: ScreenComponent) {
+ injector.inject(this)
+ }
+
+ @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()
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ 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)
+ )
+ }
+ }
+ }
+ .disposeOnDestroyView()
+
+ 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))
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceLeaveViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceLeaveViewAction.kt
new file mode 100644
index 00000000000..0d17dddca90
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceLeaveViewAction.kt
@@ -0,0 +1,26 @@
+/*
+ * 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 im.vector.app.core.platform.VectorViewModelAction
+
+sealed class SpaceLeaveViewAction : VectorViewModelAction {
+ object SetAutoLeaveAll : SpaceLeaveViewAction()
+ object SetAutoLeaveNone : SpaceLeaveViewAction()
+ object SetAutoLeaveSelected : SpaceLeaveViewAction()
+ object LeaveSpace : SpaceLeaveViewAction()
+}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt
new file mode 100644
index 00000000000..395fcc9df1a
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt
@@ -0,0 +1,39 @@
+/*
+ * 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 com.airbnb.mvrx.Async
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.Uninitialized
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+
+data class SpaceMenuState(
+ val spaceId: String,
+ val spaceSummary: RoomSummary? = null,
+ val canEditSettings: Boolean = false,
+ val canInvite: Boolean = false,
+ val canAddChild: Boolean = false,
+ val isLastAdmin: Boolean = false,
+ val leaveMode: LeaveMode = LeaveMode.LEAVE_NONE,
+ val leavingState: Async = Uninitialized
+) : MvRxState {
+ constructor(args: SpaceBottomSheetSettingsArgs) : this(spaceId = args.spaceId)
+
+ enum class LeaveMode {
+ LEAVE_ALL, LEAVE_NONE, LEAVE_SELECTED
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt
new file mode 100644
index 00000000000..24ca2189425
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt
@@ -0,0 +1,171 @@
+/*
+ * 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 com.airbnb.mvrx.ActivityViewModelContext
+import com.airbnb.mvrx.Fail
+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.AppStateHandler
+import im.vector.app.core.platform.EmptyViewEvents
+import im.vector.app.core.platform.VectorViewModel
+import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
+import im.vector.app.features.session.coroutineScope
+import kotlinx.coroutines.launch
+import org.matrix.android.sdk.api.query.ActiveSpaceFilter
+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.room.model.Membership
+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.rx.rx
+import timber.log.Timber
+
+class SpaceMenuViewModel @AssistedInject constructor(
+ @Assisted val initialState: SpaceMenuState,
+ val session: Session,
+ val appStateHandler: AppStateHandler
+) : VectorViewModel(initialState) {
+
+ @AssistedFactory
+ interface Factory {
+ fun create(initialState: SpaceMenuState): SpaceMenuViewModel
+ }
+
+ companion object : MvRxViewModelFactory {
+
+ @JvmStatic
+ override fun create(viewModelContext: ViewModelContext, state: SpaceMenuState): SpaceMenuViewModel? {
+ 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")
+ }
+ }
+
+ init {
+ val roomSummary = session.getRoomSummary(initialState.spaceId)
+
+ setState {
+ copy(spaceSummary = roomSummary)
+ }
+
+ session.getRoom(initialState.spaceId)?.let { room ->
+
+ room.rx().liveRoomSummary().subscribe {
+ it.getOrNull()?.let {
+ if (it.membership == Membership.LEAVE) {
+ setState { copy(leavingState = Success(Unit)) }
+ if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) {
+ // switch to home?
+ appStateHandler.setCurrentSpace(null, session)
+ }
+ }
+ }
+ }.disposeOnClear()
+
+ PowerLevelsObservableFactory(room)
+ .createObservable()
+ .subscribe {
+ val powerLevelsHelper = PowerLevelsHelper(it)
+
+ val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId)
+ 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)
+
+ val isAdmin = powerLevelsHelper.getUserRole(session.myUserId) is Role.Admin
+ val otherAdminCount = roomSummary?.otherMemberIds
+ ?.map { powerLevelsHelper.getUserRole(it) }
+ ?.count { it is Role.Admin }
+ ?: 0
+ val isLastAdmin = isAdmin && otherAdminCount == 0
+
+ setState {
+ copy(
+ canEditSettings = canChangeAvatar || canChangeName || canChangeTopic,
+ canInvite = canInvite,
+ canAddChild = canAddChild,
+ isLastAdmin = isLastAdmin
+ )
+ }
+ }
+ .disposeOnClear()
+ }
+ }
+
+ override fun handle(action: SpaceLeaveViewAction) {
+ when (action) {
+ SpaceLeaveViewAction.SetAutoLeaveAll -> setState {
+ copy(leaveMode = SpaceMenuState.LeaveMode.LEAVE_ALL, leavingState = Uninitialized)
+ }
+ SpaceLeaveViewAction.SetAutoLeaveNone -> setState {
+ copy(leaveMode = SpaceMenuState.LeaveMode.LEAVE_NONE, leavingState = Uninitialized)
+ }
+ SpaceLeaveViewAction.SetAutoLeaveSelected -> setState {
+ copy(leaveMode = SpaceMenuState.LeaveMode.LEAVE_SELECTED, leavingState = Uninitialized)
+ }
+ SpaceLeaveViewAction.LeaveSpace -> handleLeaveSpace()
+ }
+ }
+
+ private fun handleLeaveSpace() = withState { state ->
+
+ setState { copy(leavingState = Loading()) }
+
+ session.coroutineScope.launch {
+ try {
+ if (state.leaveMode == SpaceMenuState.LeaveMode.LEAVE_NONE) {
+ session.getRoom(initialState.spaceId)?.leave(null)
+ } else if (state.leaveMode == SpaceMenuState.LeaveMode.LEAVE_ALL) {
+ // need to find all child rooms that i have joined
+
+ session.getRoomSummaries(
+ roomSummaryQueryParams {
+ excludeType = null
+ activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(initialState.spaceId)
+ memberships = listOf(Membership.JOIN)
+ }
+ ).forEach {
+ try {
+ session.getRoom(it.roomId)?.leave(null)
+ } catch (failure: Throwable) {
+ // silently ignore?
+ Timber.e(failure, "Fail to leave sub rooms/spaces")
+ }
+ }
+ session.getRoom(initialState.spaceId)?.leave(null)
+ }
+
+ // We observe the membership and to dismiss when we have remote echo of leaving
+ } catch (failure: Throwable) {
+ setState { copy(leavingState = Fail(failure)) }
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt
index 37c5088123b..fa035bebc94 100644
--- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt
+++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt
@@ -22,35 +22,24 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
+import com.airbnb.mvrx.Success
import com.airbnb.mvrx.args
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import im.vector.app.R
-import im.vector.app.core.di.ActiveSessionHolder
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
import im.vector.app.core.di.ScreenComponent
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.databinding.BottomSheetSpaceSettingsBinding
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.navigation.Navigator
-import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import im.vector.app.features.rageshake.BugReporter
import im.vector.app.features.rageshake.ReportType
import im.vector.app.features.roomprofile.RoomProfileActivity
-import im.vector.app.features.session.coroutineScope
-import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.spaces.manage.ManageType
import im.vector.app.features.spaces.manage.SpaceManageActivity
-import io.reactivex.android.schedulers.AndroidSchedulers
-import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
-import me.gujun.android.span.span
import org.matrix.android.sdk.api.extensions.orFalse
-import org.matrix.android.sdk.api.session.events.model.EventType
-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.util.toMatrixItem
-import timber.log.Timber
import javax.inject.Inject
@Parcelize
@@ -58,15 +47,12 @@ data class SpaceBottomSheetSettingsArgs(
val spaceId: String
) : Parcelable
-// XXX make proper view model before leaving beta
-class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment() {
+class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment(), SpaceMenuViewModel.Factory {
@Inject lateinit var navigator: Navigator
- @Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var avatarRenderer: AvatarRenderer
- @Inject lateinit var vectorPreferences: VectorPreferences
@Inject lateinit var bugReporter: BugReporter
- @Inject lateinit var colorProvider: ColorProvider
+ @Inject lateinit var viewModelFactory: SpaceMenuViewModel.Factory
private val spaceArgs: SpaceBottomSheetSettingsArgs by args()
@@ -74,6 +60,8 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment
- val powerLevelsHelper = PowerLevelsHelper(powerLevelContent)
- val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId)
- 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 || roomSummary?.isPublic.orFalse()
- views.addRooms.isVisible = canAddChild
-
- val isAdmin = powerLevelsHelper.getUserRole(session.myUserId) is Role.Admin
- val otherAdminCount = roomSummary?.otherMemberIds
- ?.map { powerLevelsHelper.getUserRole(it) }
- ?.count { it is Role.Admin }
- ?: 0
- isLastAdmin = isAdmin && otherAdminCount == 0
- }.disposeOnDestroyView()
-
views.spaceBetaTag.debouncedClicks {
bugReporter.openBugReportScreen(requireActivity(), ReportType.SPACE_BETA_FEEDBACK)
}
@@ -154,40 +107,27 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment
+ super.invalidate()
- MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive)
- .setMessage(warningMessage)
- .setTitle(getString(R.string.space_leave_prompt_msg))
- .setPositiveButton(R.string.leave) { _, _ ->
- session.coroutineScope.launch {
- try {
- session.getRoom(spaceArgs.spaceId)?.leave(null)
- } catch (failure: Throwable) {
- Timber.e(failure, "Failed to leave space")
- }
- }
- dismiss()
- }
- .setNegativeButton(R.string.cancel, null)
- .show()
+ if (state.leavingState is Success) {
+ dismiss()
}
+
+ state.spaceSummary?.toMatrixItem()?.let {
+ avatarRenderer.render(it, views.spaceAvatarImageView)
+ }
+ views.spaceNameView.text = state.spaceSummary?.displayName
+ views.spaceDescription.setTextOrHide(state.spaceSummary?.topic?.takeIf { it.isNotEmpty() })
+
+ views.spaceSettings.isVisible = state.canEditSettings
+
+ views.invitePeople.isVisible = state.canInvite || state.spaceSummary?.isPublic.orFalse()
+ views.addRooms.isVisible = state.canAddChild
}
companion object {
@@ -198,4 +138,8 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment() {
+
+ interface Listener {
+ fun onItemSelected(roomSummary: RoomSummary)
+ }
+
+ var listener: Listener? = null
+ private val matchFilter = RoomSearchMatchFilter()
+ override fun buildModels(data: SpaceLeaveAdvanceViewState?) {
+ val children = data?.allChildren ?: return
+ val host = this
+ when (children) {
+ Uninitialized -> return
+ is Loading -> {
+ loadingItem {
+ id("loading")
+ }
+ }
+ is Success -> {
+ matchFilter.filter = data.currentFilter
+ val roomList = children.invoke().filter { matchFilter.test(it) }
+
+ if (roomList.isEmpty()) {
+ noResultItem {
+ id("empty")
+ text(host.stringProvider.getString(R.string.no_result_placeholder))
+ }
+ } else {
+ roomList.forEach { item ->
+ roomSelectionItem {
+ id(item.roomId)
+ matrixItem(item.toMatrixItem())
+ avatarRenderer(host.avatarRenderer)
+ selected(data.selectedRooms.contains(item.roomId))
+ itemClickListener {
+ host.listener?.onItemSelected(item)
+ }
+ }
+ }
+ }
+ }
+ is Fail -> {
+// errorWithRetryItem {
+// id("failed_to_load")
+// }
+ }
+ }
+ }
+
+ class RoomSearchMatchFilter : Predicate {
+ var filter: String = ""
+
+ override fun test(roomSummary: RoomSummary): Boolean {
+ if (filter.isEmpty()) {
+ // No filter
+ return true
+ }
+ // if filter is "Jo Do", it should match "John Doe"
+ return filter.split(" ").all {
+ roomSummary.name.contains(it, ignoreCase = true).orFalse()
+ || roomSummary.topic.contains(it, ignoreCase = true).orFalse()
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt
new file mode 100644
index 00000000000..68b313ec7f1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.leave
+
+import im.vector.app.core.platform.VectorViewModelAction
+
+sealed class SpaceLeaveAdvanceViewAction : VectorViewModelAction {
+ data class ToggleSelection(val roomId: String) : SpaceLeaveAdvanceViewAction()
+ data class UpdateFilter(val filter: String) : SpaceLeaveAdvanceViewAction()
+ object DoLeave : SpaceLeaveAdvanceViewAction()
+ object ClearError : SpaceLeaveAdvanceViewAction()
+}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt
new file mode 100644
index 00000000000..f7802d2a311
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt
@@ -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.leave
+
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.MvRxState
+import com.airbnb.mvrx.Uninitialized
+import im.vector.app.features.spaces.SpaceBottomSheetSettingsArgs
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+
+data class SpaceLeaveAdvanceViewState(
+ val spaceId: String,
+ val spaceSummary: RoomSummary? = null,
+ val allChildren: Async> = Uninitialized,
+ val selectedRooms: List = emptyList(),
+ val currentFilter: String = "",
+ val leaveState: Async = Uninitialized
+) : MvRxState {
+ constructor(args: SpaceBottomSheetSettingsArgs) : this(
+ spaceId = args.spaceId
+ )
+}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt
new file mode 100644
index 00000000000..cb667083243
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.leave
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.core.view.isGone
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.Fail
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.MvRx
+import com.airbnb.mvrx.Success
+import com.airbnb.mvrx.viewModel
+import com.google.android.material.appbar.MaterialToolbar
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import im.vector.app.R
+import im.vector.app.core.di.ScreenComponent
+import im.vector.app.core.error.ErrorFormatter
+import im.vector.app.core.extensions.commitTransaction
+import im.vector.app.core.extensions.hideKeyboard
+import im.vector.app.core.extensions.setTextOrHide
+import im.vector.app.core.platform.ToolbarConfigurable
+import im.vector.app.core.platform.VectorBaseActivity
+import im.vector.app.databinding.ActivitySimpleLoadingBinding
+import im.vector.app.features.spaces.SpaceBottomSheetSettingsArgs
+import javax.inject.Inject
+
+class SpaceLeaveAdvancedActivity : VectorBaseActivity(),
+ SpaceLeaveAdvancedViewModel.Factory,
+ ToolbarConfigurable {
+
+ override fun getBinding(): ActivitySimpleLoadingBinding = ActivitySimpleLoadingBinding.inflate(layoutInflater)
+
+ val leaveViewModel: SpaceLeaveAdvancedViewModel by viewModel()
+
+ @Inject lateinit var viewModelFactory: SpaceLeaveAdvancedViewModel.Factory
+ @Inject lateinit var errorFormatter: ErrorFormatter
+
+ override fun create(initialState: SpaceLeaveAdvanceViewState) = viewModelFactory.create(initialState)
+
+ override fun injectWith(injector: ScreenComponent) {
+ super.injectWith(injector)
+ injector.inject(this)
+ }
+
+ override fun showWaitingView(text: String?) {
+ hideKeyboard()
+ views.waitingView.waitingStatusText.isGone = views.waitingView.waitingStatusText.text.isNullOrBlank()
+ super.showWaitingView(text)
+ }
+
+ override fun hideWaitingView() {
+ views.waitingView.waitingStatusText.setTextOrHide(null)
+ views.waitingView.waitingHorizontalProgress.progress = 0
+ views.waitingView.waitingHorizontalProgress.isVisible = false
+ super.hideWaitingView()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val args = intent?.getParcelableExtra(MvRx.KEY_ARG)
+
+ if (isFirstCreation()) {
+ val simpleName = SpaceLeaveAdvancedFragment::class.java.simpleName
+ if (supportFragmentManager.findFragmentByTag(simpleName) == null) {
+ supportFragmentManager.commitTransaction {
+ replace(
+ R.id.simpleFragmentContainer,
+ SpaceLeaveAdvancedFragment::class.java,
+ Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) },
+ simpleName
+ )
+ }
+ }
+ }
+ }
+
+ override fun initUiAndData() {
+ super.initUiAndData()
+ waitingView = views.waitingView.waitingView
+ leaveViewModel.subscribe(this) { state ->
+ when (state.leaveState) {
+ is Loading -> {
+ showWaitingView()
+ }
+ is Success -> {
+ setResult(RESULT_OK)
+ finish()
+ }
+ is Fail -> {
+ hideWaitingView()
+ MaterialAlertDialogBuilder(this)
+ .setTitle(R.string.dialog_title_error)
+ .setMessage(errorFormatter.toHumanReadable(state.leaveState.error))
+ .setPositiveButton(R.string.ok) { _, _ ->
+ leaveViewModel.handle(SpaceLeaveAdvanceViewAction.ClearError)
+ }
+ .show()
+ }
+ else -> {
+ hideWaitingView()
+ }
+ }
+ }
+ }
+
+ companion object {
+ fun newIntent(context: Context, spaceId: String): Intent {
+ return Intent(context, SpaceLeaveAdvancedActivity::class.java).apply {
+ putExtra(MvRx.KEY_ARG, SpaceBottomSheetSettingsArgs(spaceId))
+ }
+ }
+ }
+
+ override fun configure(toolbar: MaterialToolbar) {
+ configureToolbar(toolbar)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt
new file mode 100644
index 00000000000..e78d90c6d94
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.leave
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.airbnb.mvrx.activityViewModel
+import com.airbnb.mvrx.withState
+import com.jakewharton.rxbinding3.appcompat.queryTextChanges
+import im.vector.app.core.extensions.cleanup
+import im.vector.app.core.extensions.configureWith
+import im.vector.app.core.platform.VectorBaseFragment
+import im.vector.app.databinding.FragmentSpaceLeaveAdvancedBinding
+import io.reactivex.rxkotlin.subscribeBy
+import org.matrix.android.sdk.api.session.room.model.RoomSummary
+import java.util.concurrent.TimeUnit
+import javax.inject.Inject
+
+class SpaceLeaveAdvancedFragment @Inject constructor(
+ val controller: SelectChildrenController
+) : VectorBaseFragment(),
+ SelectChildrenController.Listener {
+
+ override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
+ FragmentSpaceLeaveAdvancedBinding.inflate(layoutInflater, container, false)
+
+ val viewModel: SpaceLeaveAdvancedViewModel by activityViewModel()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ setupToolbar(views.toolbar)
+ controller.listener = this
+ views.roomList.configureWith(controller)
+ views.spaceLeaveCancel.debouncedClicks { requireActivity().finish() }
+
+ views.spaceLeaveButton.debouncedClicks {
+ viewModel.handle(SpaceLeaveAdvanceViewAction.DoLeave)
+ }
+
+ views.publicRoomsFilter.queryTextChanges()
+ .debounce(100, TimeUnit.MILLISECONDS)
+ .subscribeBy {
+ viewModel.handle(SpaceLeaveAdvanceViewAction.UpdateFilter(it.toString()))
+ }
+ .disposeOnDestroyView()
+ }
+
+ override fun onDestroyView() {
+ controller.listener = null
+ views.roomList.cleanup()
+ super.onDestroyView()
+ }
+
+ override fun invalidate() = withState(viewModel) { state ->
+ super.invalidate()
+ controller.setData(state)
+ }
+
+ override fun onItemSelected(roomSummary: RoomSummary) {
+ viewModel.handle(SpaceLeaveAdvanceViewAction.ToggleSelection(roomSummary.roomId))
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt
new file mode 100644
index 00000000000..7461d09b8b3
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt
@@ -0,0 +1,141 @@
+/*
+ * 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.leave
+
+import androidx.lifecycle.viewModelScope
+import com.airbnb.mvrx.ActivityViewModelContext
+import com.airbnb.mvrx.Fail
+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.AppStateHandler
+import im.vector.app.core.platform.EmptyViewEvents
+import im.vector.app.core.platform.VectorViewModel
+import kotlinx.coroutines.launch
+import okhttp3.internal.toImmutableList
+import org.matrix.android.sdk.api.query.ActiveSpaceFilter
+import org.matrix.android.sdk.api.query.RoomCategoryFilter
+import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.room.model.Membership
+import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
+import org.matrix.android.sdk.rx.rx
+import timber.log.Timber
+
+class SpaceLeaveAdvancedViewModel @AssistedInject constructor(
+ @Assisted val initialState: SpaceLeaveAdvanceViewState,
+ private val session: Session,
+ private val appStateHandler: AppStateHandler
+) : VectorViewModel(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.getRoom(it)?.leave(null)
+ } catch (failure: Throwable) {
+ // silently ignore?
+ Timber.e(failure, "Fail to leave sub rooms/spaces")
+ }
+ }
+
+ session.getRoom(initialState.spaceId)?.leave(null)
+ // 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 {
+ val spaceSummary = session.getRoomSummary(initialState.spaceId)
+ setState { copy(spaceSummary = spaceSummary) }
+ session.getRoom(initialState.spaceId)?.let { room ->
+ room.rx().liveRoomSummary().subscribe {
+ it.getOrNull()?.let {
+ if (it.membership == Membership.LEAVE) {
+ setState { copy(leaveState = Success(Unit)) }
+ if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) {
+ // switch to home?
+ appStateHandler.setCurrentSpace(null, session)
+ }
+ }
+ }
+ }
+ }
+
+ viewModelScope.launch {
+ val children = session.getRoomSummaries(
+ roomSummaryQueryParams {
+ includeType = null
+ memberships = listOf(Membership.JOIN)
+ activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(initialState.spaceId)
+ roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
+ }
+ )
+
+ setState {
+ copy(allChildren = Success(children))
+ }
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(initialState: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel
+ }
+
+ companion object : MvRxViewModelFactory {
+ override fun create(viewModelContext: ViewModelContext, state: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel? {
+ 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")
+ }
+ }
+}
diff --git a/vector/src/main/res/layout/bottom_sheet_leave_space.xml b/vector/src/main/res/layout/bottom_sheet_leave_space.xml
new file mode 100644
index 00000000000..b9626bf7853
--- /dev/null
+++ b/vector/src/main/res/layout/bottom_sheet_leave_space.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/layout/fragment_space_leave_advanced.xml b/vector/src/main/res/layout/fragment_space_leave_advanced.xml
new file mode 100644
index 00000000000..4985ca5aeb6
--- /dev/null
+++ b/vector/src/main/res/layout/fragment_space_leave_advanced.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/values-ar/strings.xml b/vector/src/main/res/values-ar/strings.xml
index 39dc53140eb..f1452e559b2 100644
--- a/vector/src/main/res/values-ar/strings.xml
+++ b/vector/src/main/res/values-ar/strings.xml
@@ -1165,6 +1165,7 @@
استكشِف الغُرفأضف غُرفغادر المساحة
+
هل أنت متأكد أنك تريد مغادرة المساحة؟انت الشخص الوحيد هنا إذا غادرت، فلن يتمكن أي شخص من الانضمام في المستقبل، بما في ذلك أنت.هذه المساحة ليست عامة. لن تتمكن من الانضمام مرة أخرى بدون دعوة.
diff --git a/vector/src/main/res/values-cs/strings.xml b/vector/src/main/res/values-cs/strings.xml
index ca31bad4082..7d004fb3e0f 100644
--- a/vector/src/main/res/values-cs/strings.xml
+++ b/vector/src/main/res/values-cs/strings.xml
@@ -2737,6 +2737,7 @@
Jste zvániVítejte v prostorech!Přidat existující místnosti a prostor
+
Jste si jisti, že chcete opustit tento prostor\?Opustit prostorPřidat místnosti
diff --git a/vector/src/main/res/values-de/strings.xml b/vector/src/main/res/values-de/strings.xml
index bfa44cf467c..0710840c513 100644
--- a/vector/src/main/res/values-de/strings.xml
+++ b/vector/src/main/res/values-de/strings.xml
@@ -2770,6 +2770,7 @@
Verlasse den Raum mit der angegebenen ID (oder den aktuellen Raum, wenn keine ID angegeben wird)Name suchenDu wurdest eingeladen
+
Bist du dir sicher, dass du den Space verlassen willst\?Space verlassenRäume hinzufügen
diff --git a/vector/src/main/res/values-eo/strings.xml b/vector/src/main/res/values-eo/strings.xml
index e7ede2e9000..c5e1149baad 100644
--- a/vector/src/main/res/values-eo/strings.xml
+++ b/vector/src/main/res/values-eo/strings.xml
@@ -2674,6 +2674,7 @@
Vi estas administranto de ĉi tiu aro. Certigu, ke vi transdonis administrajn rajtojn al alia ano, antaŭ ol vi vere foriros.Ĉi tiu aro ne estas publika. Vi ne povos ree aliĝi sen invito.Vi estas la sola persono ĉi tie. Se vi foriros, neniu plu povos aliĝi, inkluzive vin mem.
+
Ĉu vi certe volas foriri de la aro\?Foriri de aroAldoni ĉambrojn
diff --git a/vector/src/main/res/values-es/strings.xml b/vector/src/main/res/values-es/strings.xml
index 7d7a24febec..7af1373a53d 100644
--- a/vector/src/main/res/values-es/strings.xml
+++ b/vector/src/main/res/values-es/strings.xml
@@ -2619,6 +2619,7 @@ Por favor permite el acceso en la próxima ventana emergente para descubrir usua
Espacio Experimental - Sala Restringida.Estas invitadoAñadir salas
+
Estas seguro de que quieres salir de este espacio\?Salir de este espacioAñadir salas
diff --git a/vector/src/main/res/values-et/strings.xml b/vector/src/main/res/values-et/strings.xml
index 4413737c236..161f57357ec 100644
--- a/vector/src/main/res/values-et/strings.xml
+++ b/vector/src/main/res/values-et/strings.xml
@@ -2679,6 +2679,7 @@
Kogukonnakeskused on uus võimalus siduda jututubasid ja inimesi.Tere tulemast kasutama kogukonnakeskuseid!Lisa olemasolevaid jututubasid ja kogukonnakeskuseid
+
Kas oled kindel, et soovid lahkuda kogukonnakeskusest\?Lahku kogukonnakeskusestLisa jututuba
diff --git a/vector/src/main/res/values-fa/strings.xml b/vector/src/main/res/values-fa/strings.xml
index ec45a40abfa..f59512f8cfc 100644
--- a/vector/src/main/res/values-fa/strings.xml
+++ b/vector/src/main/res/values-fa/strings.xml
@@ -2679,6 +2679,7 @@
فضاها شیوهای جدید برای گروهبندی اتاقها و افراد است.به فضاها خوش آمدید!افزودن فضا و اتاقهای موجود
+
مطمئنید که میخواهید فضا را ترک کنید؟ترک فضاافزودن اتاق
diff --git a/vector/src/main/res/values-fr-rCA/strings.xml b/vector/src/main/res/values-fr-rCA/strings.xml
index ab6e9888705..204580c842f 100644
--- a/vector/src/main/res/values-fr-rCA/strings.xml
+++ b/vector/src/main/res/values-fr-rCA/strings.xml
@@ -2748,6 +2748,7 @@
Vous êtes admin de cet espace, assurez-vous d’avoir transféré les droits d’admin à un autre membre avant de partir.Cet espace n’est pas public. Vous ne pourrez pas le rejoindre sans invitation.Vous êtes la seule personne ici. Si vous partez, personne ne pourra entrer à l’avenir, même pas vous.
+
Voulez-vous vraiment quitter l’espace\?Quitter l’espaceAjouter des salons
diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml
index 57c19f04884..1ffcbe67453 100644
--- a/vector/src/main/res/values-fr/strings.xml
+++ b/vector/src/main/res/values-fr/strings.xml
@@ -2708,6 +2708,7 @@
Les espaces sont une nouvelle manière de regrouper les salons et les gens.Bienvenue dans les espaces !Ajouter des salons et espaces existants
+
Voulez-vous vraiment quitter l’espace \?Quitter l’espaceAjouter des salons
diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml
index 4113f38efed..19bb84ff539 100644
--- a/vector/src/main/res/values-hu/strings.xml
+++ b/vector/src/main/res/values-hu/strings.xml
@@ -2490,6 +2490,7 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
Most te vagy a tér adminisztrátora, bizonyosodj meg arról, hogy kineveztél mást adminisztrátornak mielőtt elhagyod.Ez a tér nem nyilvános. Kilépés után csak újabb meghívóval lehet újra belépni.Csak te van itt. Ha kilépsz, akkor a jövőben senki nem tud majd ide belépni, beleértve téged is.
+
Biztos el akarod hagyni a teret\?Tér elhagyásaSzobák hozzáadása
diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml
index 8d69b4ded94..d39e96946f5 100644
--- a/vector/src/main/res/values-it/strings.xml
+++ b/vector/src/main/res/values-it/strings.xml
@@ -2732,6 +2732,7 @@
Gli Spazi sono un nuovo modo per raggruppare stanze e contatti.Benvenuto negli Spazi!Aggiungi stanze e Spazi esistenti
+
Vuoi veramente uscire dallo Spazio\?Esci dallo SpazioAggiungi stanze
diff --git a/vector/src/main/res/values-pt-rBR/strings.xml b/vector/src/main/res/values-pt-rBR/strings.xml
index 0cec8007b55..8239b68b978 100644
--- a/vector/src/main/res/values-pt-rBR/strings.xml
+++ b/vector/src/main/res/values-pt-rBR/strings.xml
@@ -2815,6 +2815,7 @@
Você é admin deste espaço, assegure-se que você tem transferido direito de admin a um outro membro antes de sair.Este espaço não é público. Você não vai ser capaz de se rejuntar sem um convite.Você é a única pessoa aqui. Se você sair, ninguém vai ser capaz de se juntar no futuro, incluindo você.
+
Você tem certeza que você quer sair do espaço\?Sair de EspaçoAdicionar salas
diff --git a/vector/src/main/res/values-ru/strings.xml b/vector/src/main/res/values-ru/strings.xml
index 92202737677..3d829e9cbc8 100644
--- a/vector/src/main/res/values-ru/strings.xml
+++ b/vector/src/main/res/values-ru/strings.xml
@@ -2868,6 +2868,7 @@
Вы являетесь администратором этого пространства, перед уходом убедитесь, что передали права администратора другому пользователю.Это пространство не является публичным. Вы не сможете присоединиться к нему без приглашения.Вы здесь единственный человек. Если вы уйдёте, никто не сможет присоединиться в будущем, включая вас.
+
Вы уверены, что хотите покинуть пространство\?Покинуть пространствоДобавить комнаты
diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml
index f8d08d2c6a3..8f7144d965b 100644
--- a/vector/src/main/res/values-sq/strings.xml
+++ b/vector/src/main/res/values-sq/strings.xml
@@ -2668,6 +2668,7 @@
Jeni ftuarMirë se vini te Hapësira!Shtoni dhoma ekzistuese dhe hapësira
+
Jeni i sigurt se doni të dilni nga hapësira\?Braktiseni HapësirënShtoni dhoma
diff --git a/vector/src/main/res/values-sv/strings.xml b/vector/src/main/res/values-sv/strings.xml
index 9a560e56116..0310c056ebf 100644
--- a/vector/src/main/res/values-sv/strings.xml
+++ b/vector/src/main/res/values-sv/strings.xml
@@ -2737,6 +2737,7 @@
Utrymmen är ett nytt sätt att gruppera rum och personer.Välkommen till utrymmen!Lägg till existerande rum och utrymme
+
Är du säker på att du vill lämna utrymmet\?Lämna utrymmeLägg till rum
diff --git a/vector/src/main/res/values-zh-rCN/strings.xml b/vector/src/main/res/values-zh-rCN/strings.xml
index 3e6451b3c79..148790d93ae 100644
--- a/vector/src/main/res/values-zh-rCN/strings.xml
+++ b/vector/src/main/res/values-zh-rCN/strings.xml
@@ -2602,6 +2602,7 @@
你是此空间的管理员,请确保你在离开前已将管理权限转让给另一位成员。此空间并非公开空间。你将无法在没有邀请的情况下重新加入。你是这唯一的人。如果你离开,包括你在内的所有人都将无法加入此空间。
+
你确定你想要离开此空间吗?离开空间添加聊天室
diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml
index e05a234dea2..b1c60f51fd7 100644
--- a/vector/src/main/res/values-zh-rTW/strings.xml
+++ b/vector/src/main/res/values-zh-rTW/strings.xml
@@ -2628,6 +2628,7 @@
空間是將聊天室與人們分組的新方式。歡迎使用空間!新增既有的聊天室與空間
+
您確定您想要離開空間嗎?離開空間新增聊天室
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index cc8bc1e99b4..6a7e05479ae 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -3479,11 +3479,17 @@
Explore roomsAdd roomsLeave Space
+ Are you sure you want to leave %s?
+
Are you sure you want to leave the space?You are the only person here. If you leave, no one will be able to join in the future, including you.
- This space is not public. You will not be able to rejoin without an invite.
- You are admin of this space, ensure that you have transferred admin right to another member before leaving.
-
+ You won\'t be able to rejoin unless you are re-invited.
+ You\'re the only admin of this space. Leaving it will mean no one has control over it.
+ Leave all rooms and spaces
+ You will leave all rooms and spaces in %s.
+ Don’t leave any rooms and spaces
+ Leave specific rooms and spaces…
+ Pick things to leaveAdd existing rooms and spaceAdd rooms