From b9b3832ee340f796781302bd8bd1c86410f9310e Mon Sep 17 00:00:00 2001 From: Valere Date: Fri, 30 Apr 2021 11:59:10 +0200 Subject: [PATCH] Invite bottom sheet + fixes + beta tag --- .../java/im/vector/app/AppStateHandler.kt | 3 +- .../im/vector/app/core/di/ScreenComponent.kt | 2 + .../vector/app/features/home/HomeActivity.kt | 13 + .../features/home/HomeActivitySharedAction.kt | 1 + .../matrixto/MatrixToRoomSpaceFragment.kt | 21 +- .../features/spaces/SpaceInviteBottomSheet.kt | 231 ++++++++++ .../app/features/spaces/SpaceListFragment.kt | 1 + .../features/spaces/SpaceListViewEvents.kt | 1 + .../features/spaces/SpacesListViewModel.kt | 2 +- .../spaces/preview/SpacePreviewController.kt | 7 +- .../spaces/preview/SpacePreviewFragment.kt | 9 +- .../spaces/preview/SpacePreviewState.kt | 3 +- .../spaces/preview/SpacePreviewViewEvents.kt | 1 - .../spaces/preview/SpacePreviewViewModel.kt | 49 ++- .../layout/bottom_sheet_invited_to_space.xml | 76 ++++ .../fragment_matrix_to_room_space_card.xml | 415 ++++++++++-------- .../src/main/res/layout/view_button_state.xml | 6 +- vector/src/main/res/values/strings.xml | 1 + vector/src/main/res/values/styles_riot.xml | 4 +- 19 files changed, 617 insertions(+), 229 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/spaces/SpaceInviteBottomSheet.kt create mode 100644 vector/src/main/res/layout/bottom_sheet_invited_to_space.xml diff --git a/vector/src/main/java/im/vector/app/AppStateHandler.kt b/vector/src/main/java/im/vector/app/AppStateHandler.kt index 0df41d106d..a2a242a3d9 100644 --- a/vector/src/main/java/im/vector/app/AppStateHandler.kt +++ b/vector/src/main/java/im/vector/app/AppStateHandler.kt @@ -24,6 +24,7 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.utils.BehaviorDataSource import im.vector.app.features.ui.UiStateRepository import io.reactivex.disposables.CompositeDisposable +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull @@ -68,7 +69,7 @@ class AppStateHandler @Inject constructor( val spaceSum = spaceId?.let { uSession?.getRoomSummary(spaceId) } selectedSpaceDataSource.post(Option.just(RoomGroupingMethod.BySpace(spaceSum))) if (spaceId != null) { - GlobalScope.launch { + GlobalScope.launch(Dispatchers.IO) { tryOrNull { uSession?.getRoom(spaceId)?.loadRoomMembersIfNeeded() } diff --git a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt index b6c75beb02..188e96d7dd 100644 --- a/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/ScreenComponent.kt @@ -81,6 +81,7 @@ import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet import im.vector.app.features.spaces.ShareSpaceBottomSheet import im.vector.app.features.spaces.SpaceCreationActivity import im.vector.app.features.spaces.SpaceExploreActivity +import im.vector.app.features.spaces.SpaceInviteBottomSheet import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet import im.vector.app.features.spaces.manage.SpaceManageActivity import im.vector.app.features.terms.ReviewTermsActivity @@ -185,6 +186,7 @@ interface ScreenComponent { fun inject(bottomSheet: ShareSpaceBottomSheet) fun inject(bottomSheet: SpaceSettingsMenuBottomSheet) fun inject(bottomSheet: InviteRoomSpaceChooserBottomSheet) + fun inject(bottomSheet: SpaceInviteBottomSheet) /* ========================================================================================== * Others diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index f01c27c30e..28ee36604d 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -60,6 +60,7 @@ import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.spaces.ShareSpaceBottomSheet 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.SpaceSettingsMenuBottomSheet import im.vector.app.features.themes.ThemeUtils @@ -210,6 +211,18 @@ class HomeActivity : }) .show(supportFragmentManager, "SPACE_SETTINGS") } + is HomeActivitySharedAction.OpenSpaceInvite -> { + SpaceInviteBottomSheet.newInstance(sharedAction.spaceId, object : SpaceInviteBottomSheet.InteractionListener { + override fun onAccept(spaceId: String) { + navigator.switchToSpace(this@HomeActivity, spaceId, Navigator.PostSwitchSpaceAction.None) + } + + override fun onDecline(spaceId: String) { + // nop + } + }) + .show(supportFragmentManager, "SPACE_INVITE") + } }.exhaustive } .disposeOnDestroy() diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivitySharedAction.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivitySharedAction.kt index db0a9ba9eb..d79f24fc4c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivitySharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivitySharedAction.kt @@ -27,5 +27,6 @@ sealed class HomeActivitySharedAction : VectorSharedAction { data class OpenGroup(val clearFragment: Boolean) : HomeActivitySharedAction() object AddSpace : HomeActivitySharedAction() data class OpenSpacePreview(val spaceId: String) : HomeActivitySharedAction() + data class OpenSpaceInvite(val spaceId: String) : HomeActivitySharedAction() data class ShowSpaceSettings(val spaceId: String) : HomeActivitySharedAction() } diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToRoomSpaceFragment.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToRoomSpaceFragment.kt index 7d303cfc5e..7a5a740b93 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToRoomSpaceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToRoomSpaceFragment.kt @@ -20,6 +20,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.view.isGone import androidx.core.view.isInvisible import androidx.core.view.isVisible import com.airbnb.mvrx.Fail @@ -71,8 +72,10 @@ class MatrixToRoomSpaceFragment @Inject constructor( is RoomInfoResult.FullInfo -> { val matrixItem = peek.roomItem if (peek.roomType == RoomType.SPACE) { + views.matrixToBetaTag.isVisible = true avatarRenderer.renderSpace(matrixItem, views.matrixToCardAvatar) } else { + views.matrixToBetaTag.isVisible = false avatarRenderer.render(matrixItem, views.matrixToCardAvatar) } views.matrixToCardNameText.setTextOrHide(peek.name) @@ -97,19 +100,19 @@ class MatrixToRoomSpaceFragment @Inject constructor( Membership.LEAVE, Membership.NONE -> { views.matrixToCardMainButton.isVisible = true - views.matrixToCardMainButton.text = getString(joinTextRes) + views.matrixToCardMainButton.button.text = getString(joinTextRes) views.matrixToCardSecondaryButton.isVisible = false } Membership.INVITE -> { views.matrixToCardMainButton.isVisible = true views.matrixToCardSecondaryButton.isVisible = true - views.matrixToCardMainButton.text = getString(joinTextRes) - views.matrixToCardSecondaryButton.text = getString(R.string.decline) + views.matrixToCardMainButton.button.text = getString(joinTextRes) + views.matrixToCardSecondaryButton.button.text = getString(R.string.decline) } Membership.JOIN -> { views.matrixToCardMainButton.isVisible = true views.matrixToCardSecondaryButton.isVisible = false - views.matrixToCardMainButton.text = getString(R.string.action_open) + views.matrixToCardMainButton.button.text = getString(R.string.action_open) } Membership.KNOCK, Membership.BAN -> { @@ -126,7 +129,7 @@ class MatrixToRoomSpaceFragment @Inject constructor( views.matrixToMemberPills.isVisible = false views.matrixToCardDescText.setTextOrHide(getString(R.string.room_preview_no_preview)) - views.matrixToCardMainButton.text = getString(R.string.join_anyway) + views.matrixToCardMainButton.button.text = getString(R.string.join_anyway) views.matrixToCardSecondaryButton.isVisible = false } RoomInfoResult.NotFound -> { @@ -156,12 +159,10 @@ class MatrixToRoomSpaceFragment @Inject constructor( } } + val images = listOf(views.knownMember1, views.knownMember2, views.knownMember3, views.knownMember4, views.knownMember5) + .onEach { it.isGone = true } when (state.peopleYouKnow) { is Success -> { - views.matrixToCardPeopleYouKnowVisibility.isVisible = true - val images = listOf(views.knownMember1, views.knownMember2, views.knownMember3, views.knownMember4, views.knownMember5) - .onEach { it.isVisible = false } - val someYouKnow = state.peopleYouKnow.invoke() someYouKnow.forEachIndexed { index, item -> images[index].isVisible = true @@ -175,7 +176,7 @@ class MatrixToRoomSpaceFragment @Inject constructor( ) } else -> { - views.matrixToCardPeopleYouKnowVisibility.isVisible = false + views.peopleYouMayKnowText.isVisible = false } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceInviteBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceInviteBottomSheet.kt new file mode 100644 index 0000000000..05909e5684 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceInviteBottomSheet.kt @@ -0,0 +1,231 @@ +/* + * 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.os.Bundle +import android.os.Parcelable +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isGone +import androidx.core.view.isVisible +import com.airbnb.mvrx.args +import im.vector.app.R +import im.vector.app.core.di.ActiveSessionHolder +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.platform.ButtonStateView +import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment +import im.vector.app.core.utils.toast +import im.vector.app.databinding.BottomSheetInvitedToSpaceBinding +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 org.matrix.android.sdk.api.util.toMatrixItem +import javax.inject.Inject + +class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment() { + + interface InteractionListener { + fun onAccept(spaceId: String) + fun onDecline(spaceId: String) + } + + var interactionListener: InteractionListener? = null + + @Parcelize + data class Args( + val spaceId: String + ) : Parcelable + + @Inject + lateinit var activeSessionHolder: ActiveSessionHolder + + @Inject + lateinit var avatarRenderer: AvatarRenderer + + @Inject + lateinit var errorFormatter: ErrorFormatter + + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) + } + + override val showExpanded = true + + private val inviteArgs: Args by args() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val session = activeSessionHolder.getSafeActiveSession() ?: return + + val summary = session.getRoomSummary(inviteArgs.spaceId) ?: return Unit.also { + dismiss() + } + + val inviter = summary.inviterId?.let { session.getUser(it) } + if (inviter != null) { + views.inviterAvatarImage.isVisible = true + views.inviterText.isVisible = true + views.inviterMxid.isVisible = true + avatarRenderer.render(inviter.toMatrixItem(), views.inviterAvatarImage) + views.inviterText.text = getString(R.string.user_invites_you, inviter.getBestName()) + views.inviterMxid.text = inviter.userId + } else { + views.inviterAvatarImage.isVisible = false + views.inviterText.isVisible = false + views.inviterMxid.isVisible = false + } + + views.spaceCard.matrixToCardContentVisibility.isVisible = true + avatarRenderer.renderSpace(summary.toMatrixItem(), views.spaceCard.matrixToCardAvatar) + views.spaceCard.matrixToCardNameText.text = summary.displayName + views.spaceCard.matrixToBetaTag.isVisible = true + views.spaceCard.matrixToCardAliasText.setTextOrHide(summary.canonicalAlias) + 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.callback = object : ButtonStateView.Callback { + override fun onButtonClicked() { + doJoin() + } + + override fun onRetryClicked() { + doJoin() + } + + private fun doJoin() { + views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Loading) + views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = false + GlobalScope.launch(Dispatchers.IO) { + try { + activeSessionHolder.getSafeActiveSession()?.getRoom(inviteArgs.spaceId)?.join() + withContext(Dispatchers.Main) { + if (!isAdded) return@withContext + views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Loaded) + views.spaceCard.matrixToCardSecondaryButton.isEnabled = true + interactionListener?.onAccept(inviteArgs.spaceId) + 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) + views.spaceCard.matrixToCardSecondaryButton.button.text = getString(R.string.reject) + views.spaceCard.matrixToCardSecondaryButton.callback = object : ButtonStateView.Callback { + override fun onButtonClicked() { + doJoin() + } + + override fun onRetryClicked() { + doJoin() + } + + private fun doJoin() { + views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Loading) + views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = false + GlobalScope.launch(Dispatchers.IO) { + try { + activeSessionHolder.getSafeActiveSession()?.getRoom(inviteArgs.spaceId)?.leave() + withContext(Dispatchers.Main) { + if (!isAdded) return@withContext + views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Loaded) + views.spaceCard.matrixToCardMainButton.button.isEnabled = true + interactionListener?.onDecline(inviteArgs.spaceId) + 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 + if (memberCount != 0) { + views.spaceCard.matrixToMemberPills.isVisible = true + views.spaceCard.spaceChildMemberCountText.text = resources.getQuantityString(R.plurals.room_title_members, memberCount, memberCount) + } else { + // hide the pill + views.spaceCard.matrixToMemberPills.isVisible = false + } + + val knownMembers = summary.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) + + val images = listOf( + views.spaceCard.knownMember1, + views.spaceCard.knownMember2, + views.spaceCard.knownMember3, + views.spaceCard.knownMember4, + views.spaceCard.knownMember5 + ).onEach { it.isGone = true } + + if (peopleYouKnow.isEmpty()) { + views.spaceCard.peopleYouMayKnowText.isVisible = false + } else { + peopleYouKnow.forEachIndexed { index, item -> + images[index].isVisible = true + avatarRenderer.render(item.toMatrixItem(), images[index]) + } + views.spaceCard.peopleYouMayKnowText.setTextOrHide( + resources.getQuantityString(R.plurals.space_people_you_know, + peopleYouKnow.count(), + peopleYouKnow.count() + ) + ) + } + } + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetInvitedToSpaceBinding { + return BottomSheetInvitedToSpaceBinding.inflate(inflater, container, false) + } + + companion object { + + fun newInstance(spaceId: String, interactionListener: InteractionListener) + : SpaceInviteBottomSheet { + return SpaceInviteBottomSheet().apply { + this.interactionListener = interactionListener + setArguments(Args(spaceId)) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt index 672fb28928..f1627cc6b6 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListFragment.kt @@ -60,6 +60,7 @@ class SpaceListFragment @Inject constructor( is SpaceListViewEvents.OpenSpace -> sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup(it.groupingMethodHasChanged)) is SpaceListViewEvents.AddSpace -> sharedActionViewModel.post(HomeActivitySharedAction.AddSpace) is SpaceListViewEvents.OpenGroup -> sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup(it.groupingMethodHasChanged)) + is SpaceListViewEvents.OpenSpaceInvite -> sharedActionViewModel.post(HomeActivitySharedAction.OpenSpaceInvite(it.id)) }.exhaustive } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewEvents.kt index b7e31d28f2..582f6cd144 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewEvents.kt @@ -24,6 +24,7 @@ import im.vector.app.core.platform.VectorViewEvents sealed class SpaceListViewEvents : VectorViewEvents { data class OpenSpace(val groupingMethodHasChanged: Boolean) : SpaceListViewEvents() data class OpenSpaceSummary(val id: String) : SpaceListViewEvents() + data class OpenSpaceInvite(val id: String) : SpaceListViewEvents() object AddSpace : SpaceListViewEvents() data class OpenGroup(val groupingMethodHasChanged: Boolean) : SpaceListViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index 90ab771342..c79f6b12a4 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -181,7 +181,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp } private fun handleSelectSpaceInvite(action: SpaceListAction.OpenSpaceInvite) { - _viewEvents.post(SpaceListViewEvents.OpenSpaceSummary(action.spaceSummary.roomId)) + _viewEvents.post(SpaceListViewEvents.OpenSpaceInvite(action.spaceSummary.roomId)) } private fun handleToggleExpand(action: SpaceListAction.ToggleExpand) = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewController.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewController.kt index eee8d1241f..8f2e7379c4 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewController.kt @@ -37,16 +37,15 @@ class SpacePreviewController @Inject constructor( var interactionListener: InteractionListener? = null override fun buildModels(data: SpacePreviewState?) { - val result = data?.childInfoList?.invoke() ?: return - - val memberCount = data.spaceInfo.invoke()?.memberCount ?: 0 + val memberCount = data?.spaceInfo?.invoke()?.memberCount ?: 0 spaceTopSummaryItem { id("info") formattedMemberCount(stringProvider.getQuantityString(R.plurals.room_title_members, memberCount, memberCount)) - topic(data.spaceInfo.invoke()?.topic ?: data.topic ?: "") + topic(data?.spaceInfo?.invoke()?.topic ?: data?.topic ?: "") } + val result = data?.childInfoList?.invoke() ?: return if (result.isNotEmpty()) { genericItemHeader { id("header_rooms") diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewFragment.kt index 563b4f39e0..b6f1fb6a4e 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewFragment.kt @@ -119,14 +119,17 @@ class SpacePreviewFragment @Inject constructor( } } updateToolbar(it) + + when (it.inviteTermination) { + is Loading -> sharedActionViewModel.post(SpacePreviewSharedAction.ShowModalLoading) + else -> sharedActionViewModel.post(SpacePreviewSharedAction.HideModalLoading) + } } private fun handleViewEvents(viewEvents: SpacePreviewViewEvents) { when (viewEvents) { SpacePreviewViewEvents.Dismiss -> { - } - SpacePreviewViewEvents.StartJoining -> { - sharedActionViewModel.post(SpacePreviewSharedAction.ShowModalLoading) + sharedActionViewModel.post(SpacePreviewSharedAction.DismissAction) } SpacePreviewViewEvents.JoinSuccess -> { sharedActionViewModel.post(SpacePreviewSharedAction.HideModalLoading) diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewState.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewState.kt index cf64672046..d31d05cf96 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewState.kt @@ -26,7 +26,8 @@ data class SpacePreviewState( val topic: String? = null, val avatarUrl: String? = null, val spaceInfo: Async = Uninitialized, - val childInfoList: Async> = Uninitialized + val childInfoList: Async> = Uninitialized, + val inviteTermination: Async = Uninitialized ) : MvRxState { constructor(args: SpacePreviewArgs) : this(idOrAlias = args.idOrAlias) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewEvents.kt index 04645e59ad..2f0eddb189 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewEvents.kt @@ -20,7 +20,6 @@ import im.vector.app.core.platform.VectorViewEvents sealed class SpacePreviewViewEvents : VectorViewEvents { object Dismiss: SpacePreviewViewEvents() - object StartJoining: SpacePreviewViewEvents() object JoinSuccess: SpacePreviewViewEvents() data class JoinFailure(val message: String?): SpacePreviewViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt index 7da6c8a053..2073a55e90 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewModel.kt @@ -28,6 +28,7 @@ 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 kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -41,6 +42,7 @@ import timber.log.Timber class SpacePreviewViewModel @AssistedInject constructor( @Assisted private val initialState: SpacePreviewState, + private val errorFormatter: ErrorFormatter, private val session: Session ) : VectorViewModel(initialState) { @@ -80,12 +82,15 @@ class SpacePreviewViewModel @AssistedInject constructor( private fun handleDismissInvite() { // Here we need to join the space himself as well as the default rooms in that space - // TODO modal loading + setState { copy(inviteTermination = Loading()) } viewModelScope.launch(Dispatchers.IO) { try { session.spaceService().rejectInvite(initialState.idOrAlias, null) + setState { copy(inviteTermination = Uninitialized) } } catch (failure: Throwable) { + setState { copy(inviteTermination = Uninitialized) } Timber.e(failure, "## Space: Failed to reject invite") + _viewEvents.post(SpacePreviewViewEvents.JoinFailure(errorFormatter.toHumanReadable(failure))) } } } @@ -98,18 +103,24 @@ class SpacePreviewViewModel @AssistedInject constructor( val spaceVia = spaceInfo?.viaServers ?: emptyList() // trigger modal loading - _viewEvents.post(SpacePreviewViewEvents.StartJoining) + setState { copy(inviteTermination = Loading()) } viewModelScope.launch(Dispatchers.IO) { - val joinResult = session.spaceService().joinSpace(initialState.idOrAlias, null, spaceVia) - when (joinResult) { - JoinSpaceResult.Success, - is JoinSpaceResult.PartialSuccess -> { - // For now we don't handle partial success, it's just success - _viewEvents.post(SpacePreviewViewEvents.JoinSuccess) - } - is JoinSpaceResult.Fail -> { - _viewEvents.post(SpacePreviewViewEvents.JoinFailure(joinResult.error.toString())) + try { + val joinResult = session.spaceService().joinSpace(initialState.idOrAlias, null, spaceVia) + setState { copy(inviteTermination = Uninitialized) } + when (joinResult) { + JoinSpaceResult.Success, + is JoinSpaceResult.PartialSuccess -> { + // For now we don't handle partial success, it's just success + _viewEvents.post(SpacePreviewViewEvents.JoinSuccess) + } + is JoinSpaceResult.Fail -> { + _viewEvents.post(SpacePreviewViewEvents.JoinFailure(joinResult.error.toString())) + } } + } catch (failure: Throwable) { + // should not throw + Timber.w(failure, "## Failed to join space") } } } @@ -221,7 +232,21 @@ class SpacePreviewViewModel @AssistedInject constructor( } } catch (failure: Throwable) { setState { - copy(spaceInfo = Fail(failure), childInfoList = Fail(failure)) + copy( + spaceInfo = session.getRoomSummary(initialState.idOrAlias)?.let { + Success( + ChildInfo( + roomId = it.roomId, + avatarUrl = it.avatarUrl, + name = it.displayName, + topic = it.topic, + memberCount = it.joinedMembersCount, + isSubSpace = false, + viaServers = null, + children = Uninitialized + ) + ) + } ?: Fail(failure), childInfoList = Fail(failure)) } } } diff --git a/vector/src/main/res/layout/bottom_sheet_invited_to_space.xml b/vector/src/main/res/layout/bottom_sheet_invited_to_space.xml new file mode 100644 index 0000000000..6730a585db --- /dev/null +++ b/vector/src/main/res/layout/bottom_sheet_invited_to_space.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_matrix_to_room_space_card.xml b/vector/src/main/res/layout/fragment_matrix_to_room_space_card.xml index c87a0d9e4b..85ec04ba50 100644 --- a/vector/src/main/res/layout/fragment_matrix_to_room_space_card.xml +++ b/vector/src/main/res/layout/fragment_matrix_to_room_space_card.xml @@ -1,224 +1,257 @@ - + android:layout_height="wrap_content"> - - - - - - - + tools:visibility="visible"> + android:id="@+id/matrixToCardAvatar" + android:layout_width="60dp" + android:layout_height="60dp" + android:layout_marginTop="20dp" + android:contentDescription="@string/avatar" + android:elevation="4dp" + android:transitionName="profile" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:src="@tools:sample/avatars" /> - + + - + android:textSize="15sp" + android:textStyle="bold" + app:layout_constraintTop_toBottomOf="@+id/matrixToCardAvatar" + tools:text="@sample/matrix.json/data/roomName" /> - + - + - + - + + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + diff --git a/vector/src/main/res/layout/view_button_state.xml b/vector/src/main/res/layout/view_button_state.xml index 28a3f066e5..1e22d79b5e 100644 --- a/vector/src/main/res/layout/view_button_state.xml +++ b/vector/src/main/res/layout/view_button_state.xml @@ -1,14 +1,14 @@ Experimental Space - Restricted Room. Warning requires server support and experimental room version + %s invites you diff --git a/vector/src/main/res/values/styles_riot.xml b/vector/src/main/res/values/styles_riot.xml index 89d5ac12e2..a265727522 100644 --- a/vector/src/main/res/values/styles_riot.xml +++ b/vector/src/main/res/values/styles_riot.xml @@ -152,7 +152,7 @@ wrap_content wrap_content @null - + ?colorAccent ?colorAccent @@ -162,7 +162,7 @@ wrap_content wrap_content @null - + ?colorAccent ?colorAccent @color/button_background_tint_selector 1dp