Code review

This commit is contained in:
Valere 2021-05-05 14:55:02 +02:00
parent ecceb0fb03
commit 168633b466
7 changed files with 293 additions and 103 deletions

View File

@ -81,7 +81,7 @@ import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet
import im.vector.app.features.spaces.ShareSpaceBottomSheet import im.vector.app.features.spaces.ShareSpaceBottomSheet
import im.vector.app.features.spaces.SpaceCreationActivity import im.vector.app.features.spaces.SpaceCreationActivity
import im.vector.app.features.spaces.SpaceExploreActivity import im.vector.app.features.spaces.SpaceExploreActivity
import im.vector.app.features.spaces.SpaceInviteBottomSheet import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet
import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet
import im.vector.app.features.spaces.manage.SpaceManageActivity import im.vector.app.features.spaces.manage.SpaceManageActivity
import im.vector.app.features.terms.ReviewTermsActivity import im.vector.app.features.terms.ReviewTermsActivity

View File

@ -60,9 +60,9 @@ import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.VectorSettingsActivity
import im.vector.app.features.spaces.ShareSpaceBottomSheet import im.vector.app.features.spaces.ShareSpaceBottomSheet
import im.vector.app.features.spaces.SpaceCreationActivity import im.vector.app.features.spaces.SpaceCreationActivity
import im.vector.app.features.spaces.SpaceInviteBottomSheet
import im.vector.app.features.spaces.SpacePreviewActivity import im.vector.app.features.spaces.SpacePreviewActivity
import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet
import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import im.vector.app.features.workers.signout.ServerBackupStatusViewState import im.vector.app.features.workers.signout.ServerBackupStatusViewState
@ -89,7 +89,8 @@ class HomeActivity :
UnknownDeviceDetectorSharedViewModel.Factory, UnknownDeviceDetectorSharedViewModel.Factory,
ServerBackupStatusViewModel.Factory, ServerBackupStatusViewModel.Factory,
UnreadMessagesSharedViewModel.Factory, UnreadMessagesSharedViewModel.Factory,
NavigationInterceptor { NavigationInterceptor,
SpaceInviteBottomSheet.InteractionListener {
private lateinit var sharedActionViewModel: HomeSharedActionViewModel private lateinit var sharedActionViewModel: HomeSharedActionViewModel
@ -212,15 +213,7 @@ class HomeActivity :
.show(supportFragmentManager, "SPACE_SETTINGS") .show(supportFragmentManager, "SPACE_SETTINGS")
} }
is HomeActivitySharedAction.OpenSpaceInvite -> { is HomeActivitySharedAction.OpenSpaceInvite -> {
SpaceInviteBottomSheet.newInstance(sharedAction.spaceId, object : SpaceInviteBottomSheet.InteractionListener { SpaceInviteBottomSheet.newInstance(sharedAction.spaceId)
override fun onAccept(spaceId: String) {
navigator.switchToSpace(this@HomeActivity, spaceId, Navigator.PostSwitchSpaceAction.None)
}
override fun onDecline(spaceId: String) {
// nop
}
})
.show(supportFragmentManager, "SPACE_INVITE") .show(supportFragmentManager, "SPACE_INVITE")
} }
}.exhaustive }.exhaustive
@ -527,6 +520,14 @@ class HomeActivity :
return true return true
} }
override fun spaceInviteBottomSheetOnAccept(spaceId: String) {
navigator.switchToSpace(this, spaceId, Navigator.PostSwitchSpaceAction.None)
}
override fun spaceInviteBottomSheetOnDecline(spaceId: String) {
// nop
}
companion object { companion object {
fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent { fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent {
val args = HomeActivityArgs( val args = HomeActivityArgs(

View File

@ -14,8 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.features.spaces package im.vector.app.features.spaces.invite
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater import android.view.LayoutInflater
@ -23,30 +24,30 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.args import com.airbnb.mvrx.args
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenComponent import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.ButtonStateView import im.vector.app.core.platform.ButtonStateView
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.databinding.BottomSheetInvitedToSpaceBinding import im.vector.app.databinding.BottomSheetInvitedToSpaceBinding
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject import javax.inject.Inject
class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetInvitedToSpaceBinding>() { class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetInvitedToSpaceBinding>(), SpaceInviteBottomSheetViewModel.Factory {
interface InteractionListener { interface InteractionListener {
fun onAccept(spaceId: String) fun spaceInviteBottomSheetOnAccept(spaceId: String)
fun onDecline(spaceId: String) fun spaceInviteBottomSheetOnDecline(spaceId: String)
} }
var interactionListener: InteractionListener? = null var interactionListener: InteractionListener? = null
@ -56,14 +57,14 @@ class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetIn
val spaceId: String val spaceId: String
) : Parcelable ) : Parcelable
@Inject
lateinit var activeSessionHolder: ActiveSessionHolder
@Inject @Inject
lateinit var avatarRenderer: AvatarRenderer lateinit var avatarRenderer: AvatarRenderer
@Inject private val viewModel: SpaceInviteBottomSheetViewModel by fragmentViewModel(SpaceInviteBottomSheetViewModel::class)
lateinit var errorFormatter: ErrorFormatter
@Inject lateinit var viewModelFactory: SpaceInviteBottomSheetViewModel.Factory
override fun create(initialState: SpaceInviteBottomSheetState) = viewModelFactory.create(initialState)
override fun injectWith(injector: ScreenComponent) { override fun injectWith(injector: ScreenComponent) {
injector.inject(this) injector.inject(this)
@ -75,13 +76,50 @@ class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetIn
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val session = activeSessionHolder.getSafeActiveSession() ?: return
val summary = session.getRoomSummary(inviteArgs.spaceId) ?: return Unit.also { views.spaceCard.matrixToCardMainButton.callback = object : ButtonStateView.Callback {
dismiss() override fun onButtonClicked() {
// quick local echo
views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Loading)
views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = true
viewModel.handle(SpaceInviteBottomSheetAction.DoJoin)
}
override fun onRetryClicked() = onButtonClicked()
}
views.spaceCard.matrixToCardSecondaryButton.callback = object : ButtonStateView.Callback {
override fun onButtonClicked() {
views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Loading)
views.spaceCard.matrixToCardMainButton.button.isEnabled = true
viewModel.handle(SpaceInviteBottomSheetAction.DoReject)
}
override fun onRetryClicked() = onButtonClicked()
} }
val inviter = summary.inviterId?.let { session.getUser(it) }?.toMatrixItem() viewModel.observeViewEvents {
when (it) {
is SpaceInviteBottomSheetEvents.ShowError -> requireActivity().toast(it.message)
}
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is InteractionListener) {
interactionListener = context
}
}
override fun onDetach() {
interactionListener = null
super.onDetach()
}
override fun invalidate() = withState(viewModel) { state ->
super.invalidate()
val summary = state.summary.invoke()
val inviter = state.inviterUser.invoke()?.toMatrixItem()
if (inviter != null) { if (inviter != null) {
views.inviterAvatarImage.isVisible = true views.inviterAvatarImage.isVisible = true
views.inviterText.isVisible = true views.inviterText.isVisible = true
@ -96,85 +134,52 @@ class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetIn
} }
views.spaceCard.matrixToCardContentVisibility.isVisible = true views.spaceCard.matrixToCardContentVisibility.isVisible = true
avatarRenderer.renderSpace(summary.toMatrixItem(), views.spaceCard.matrixToCardAvatar) summary?.toMatrixItem()?.let { avatarRenderer.renderSpace(it, views.spaceCard.matrixToCardAvatar) }
views.spaceCard.matrixToCardNameText.text = summary.displayName views.spaceCard.matrixToCardNameText.text = summary?.displayName
views.spaceCard.matrixToBetaTag.isVisible = true views.spaceCard.matrixToBetaTag.isVisible = true
views.spaceCard.matrixToCardAliasText.setTextOrHide(summary.canonicalAlias) views.spaceCard.matrixToCardAliasText.setTextOrHide(summary?.canonicalAlias)
views.spaceCard.matrixToCardDescText.setTextOrHide(summary.topic) views.spaceCard.matrixToCardDescText.setTextOrHide(summary?.topic)
views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Button)
views.spaceCard.matrixToCardMainButton.button.text = getString(R.string.accept) views.spaceCard.matrixToCardMainButton.button.text = getString(R.string.accept)
views.spaceCard.matrixToCardMainButton.callback = object : ButtonStateView.Callback { views.spaceCard.matrixToCardSecondaryButton.button.text = getString(R.string.decline)
override fun onButtonClicked() {
doJoin()
}
override fun onRetryClicked() { when (state.joinActionState) {
doJoin() Uninitialized -> {
views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Button)
} }
is Loading -> {
private fun doJoin() {
views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Loading) views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Loading)
views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = false views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = false
GlobalScope.launch(Dispatchers.IO) { }
try { is Success -> {
activeSessionHolder.getSafeActiveSession()?.getRoom(inviteArgs.spaceId)?.join() interactionListener?.spaceInviteBottomSheetOnAccept(inviteArgs.spaceId)
withContext(Dispatchers.Main) { dismiss()
if (!isAdded) return@withContext }
views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Loaded) is Fail -> {
views.spaceCard.matrixToCardSecondaryButton.isEnabled = true views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Error)
interactionListener?.onAccept(inviteArgs.spaceId) views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = true
dismiss()
}
} catch (failure: Throwable) {
withContext(Dispatchers.Main) {
if (!isAdded) return@withContext
requireActivity().toast(errorFormatter.toHumanReadable(failure))
views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Error)
views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = true
}
}
}
} }
} }
views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Button) when (state.rejectActionState) {
views.spaceCard.matrixToCardSecondaryButton.button.text = getString(R.string.reject) Uninitialized -> {
views.spaceCard.matrixToCardSecondaryButton.callback = object : ButtonStateView.Callback { views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Button)
override fun onButtonClicked() {
doReject()
} }
is Loading -> {
override fun onRetryClicked() {
doReject()
}
private fun doReject() {
views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Loading) views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Loading)
views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = false views.spaceCard.matrixToCardMainButton.button.isEnabled = false
GlobalScope.launch(Dispatchers.IO) { }
try { is Success -> {
activeSessionHolder.getSafeActiveSession()?.getRoom(inviteArgs.spaceId)?.leave() interactionListener?.spaceInviteBottomSheetOnDecline(inviteArgs.spaceId)
withContext(Dispatchers.Main) { dismiss()
if (!isAdded) return@withContext }
views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Loaded) is Fail -> {
views.spaceCard.matrixToCardMainButton.button.isEnabled = true views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Error)
interactionListener?.onDecline(inviteArgs.spaceId) views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = true
dismiss()
}
} catch (failure: Throwable) {
withContext(Dispatchers.Main) {
if (!isAdded) return@withContext
requireActivity().toast(errorFormatter.toHumanReadable(failure))
views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Error)
views.spaceCard.matrixToCardMainButton.button.isEnabled = true
}
}
}
} }
} }
val memberCount = summary.otherMemberIds.size val memberCount = summary?.otherMemberIds?.size ?: 0
if (memberCount != 0) { if (memberCount != 0) {
views.spaceCard.matrixToMemberPills.isVisible = true views.spaceCard.matrixToMemberPills.isVisible = true
views.spaceCard.spaceChildMemberCountText.text = resources.getQuantityString(R.plurals.room_title_members, memberCount, memberCount) views.spaceCard.spaceChildMemberCountText.text = resources.getQuantityString(R.plurals.room_title_members, memberCount, memberCount)
@ -183,12 +188,7 @@ class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetIn
views.spaceCard.matrixToMemberPills.isVisible = false views.spaceCard.matrixToMemberPills.isVisible = false
} }
val knownMembers = summary.otherMemberIds.filter { val peopleYouKnow = state.peopleYouKnow.invoke().orEmpty()
session.getExistingDirectRoomWithUser(it) != null
}.mapNotNull { session.getUser(it) }
// put one with avatar first, and take 5
val peopleYouKnow = (knownMembers.filter { it.avatarUrl != null } + knownMembers.filter { it.avatarUrl == null })
.take(5)
val images = listOf( val images = listOf(
views.spaceCard.knownMember1, views.spaceCard.knownMember1,
@ -220,10 +220,9 @@ class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetIn
companion object { companion object {
fun newInstance(spaceId: String, interactionListener: InteractionListener) fun newInstance(spaceId: String)
: SpaceInviteBottomSheet { : SpaceInviteBottomSheet {
return SpaceInviteBottomSheet().apply { return SpaceInviteBottomSheet().apply {
this.interactionListener = interactionListener
setArguments(Args(spaceId)) setArguments(Args(spaceId))
} }
} }

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.spaces.invite
import im.vector.app.core.platform.VectorViewModelAction
sealed class SpaceInviteBottomSheetAction : VectorViewModelAction {
object DoJoin : SpaceInviteBottomSheetAction()
object DoReject : SpaceInviteBottomSheetAction()
}

View File

@ -0,0 +1,23 @@
/*
* 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.invite
import im.vector.app.core.platform.VectorViewEvents
sealed class SpaceInviteBottomSheetEvents : VectorViewEvents {
data class ShowError(val message: String) : SpaceInviteBottomSheetEvents()
}

View File

@ -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.invite
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.user.model.User
data class SpaceInviteBottomSheetState(
val spaceId: String,
val summary: Async<RoomSummary> = Uninitialized,
val inviterUser: Async<User> = Uninitialized,
val peopleYouKnow: Async<List<User>> = Uninitialized,
val joinActionState: Async<Unit> = Uninitialized,
val rejectActionState: Async<Unit> = Uninitialized
) : MvRxState {
constructor(args: SpaceInviteBottomSheet.Args) : this(
spaceId = args.spaceId
)
}

View File

@ -0,0 +1,107 @@
/*
* 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.invite
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.core.error.ErrorFormatter
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
class SpaceInviteBottomSheetViewModel @AssistedInject constructor(
@Assisted private val initialState: SpaceInviteBottomSheetState,
private val session: Session,
private val errorFormatter: ErrorFormatter
) : VectorViewModel<SpaceInviteBottomSheetState, SpaceInviteBottomSheetAction, SpaceInviteBottomSheetEvents>(initialState) {
init {
session.getRoomSummary(initialState.spaceId)?.let { roomSummary ->
val knownMembers = roomSummary.otherMemberIds.filter {
session.getExistingDirectRoomWithUser(it) != null
}.mapNotNull { session.getUser(it) }
// put one with avatar first, and take 5
val peopleYouKnow = (knownMembers.filter { it.avatarUrl != null } + knownMembers.filter { it.avatarUrl == null })
.take(5)
setState {
copy(
summary = Success(roomSummary),
inviterUser = roomSummary.inviterId?.let { session.getUser(it) }?.let { Success(it) } ?: Uninitialized,
peopleYouKnow = Success(peopleYouKnow)
)
}
}
}
@AssistedFactory
interface Factory {
fun create(initialState: SpaceInviteBottomSheetState): SpaceInviteBottomSheetViewModel
}
companion object : MvRxViewModelFactory<SpaceInviteBottomSheetViewModel, SpaceInviteBottomSheetState> {
override fun create(viewModelContext: ViewModelContext, state: SpaceInviteBottomSheetState): SpaceInviteBottomSheetViewModel? {
val factory = when (viewModelContext) {
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
is ActivityViewModelContext -> viewModelContext.activity as? Factory
}
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
}
}
override fun handle(action: SpaceInviteBottomSheetAction) {
when (action) {
SpaceInviteBottomSheetAction.DoJoin -> {
setState { copy(joinActionState = Loading()) }
session.coroutineScope.launch(Dispatchers.IO) {
try {
session.getRoom(initialState.spaceId)?.join()
setState { copy(joinActionState = Success(Unit)) }
} catch (failure: Throwable) {
setState { copy(joinActionState = Fail(failure)) }
_viewEvents.post(SpaceInviteBottomSheetEvents.ShowError(errorFormatter.toHumanReadable(failure)))
}
}
}
SpaceInviteBottomSheetAction.DoReject -> {
setState { copy(rejectActionState = Loading()) }
session.coroutineScope.launch(Dispatchers.IO) {
try {
session.getRoom(initialState.spaceId)?.leave()
setState { copy(rejectActionState = Success(Unit)) }
} catch (failure: Throwable) {
setState { copy(rejectActionState = Fail(failure)) }
_viewEvents.post(SpaceInviteBottomSheetEvents.ShowError(errorFormatter.toHumanReadable(failure)))
}
}
}
}
}
}