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..c16c602530 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.invite.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..1de1ff1c3e 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 @@ -62,6 +62,7 @@ import im.vector.app.features.spaces.ShareSpaceBottomSheet import im.vector.app.features.spaces.SpaceCreationActivity import im.vector.app.features.spaces.SpacePreviewActivity 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.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewState @@ -88,7 +89,8 @@ class HomeActivity : UnknownDeviceDetectorSharedViewModel.Factory, ServerBackupStatusViewModel.Factory, UnreadMessagesSharedViewModel.Factory, - NavigationInterceptor { + NavigationInterceptor, + SpaceInviteBottomSheet.InteractionListener { private lateinit var sharedActionViewModel: HomeSharedActionViewModel @@ -210,6 +212,10 @@ class HomeActivity : }) .show(supportFragmentManager, "SPACE_SETTINGS") } + is HomeActivitySharedAction.OpenSpaceInvite -> { + SpaceInviteBottomSheet.newInstance(sharedAction.spaceId) + .show(supportFragmentManager, "SPACE_INVITE") + } }.exhaustive } .disposeOnDestroy() @@ -514,6 +520,14 @@ class HomeActivity : return true } + override fun spaceInviteBottomSheetOnAccept(spaceId: String) { + navigator.switchToSpace(this, spaceId, Navigator.PostSwitchSpaceAction.None) + } + + override fun spaceInviteBottomSheetOnDecline(spaceId: String) { + // nop + } + companion object { fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent { val args = HomeActivityArgs( 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/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/invite/SpaceInviteBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt new file mode 100644 index 0000000000..8e536459a3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt @@ -0,0 +1,230 @@ +/* + * 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 android.content.Context +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.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.fragmentViewModel +import com.airbnb.mvrx.withState +import im.vector.app.R +import im.vector.app.core.di.ScreenComponent +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.parcelize.Parcelize +import org.matrix.android.sdk.api.util.toMatrixItem +import javax.inject.Inject + +class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetInvitedToSpaceBinding>(), SpaceInviteBottomSheetViewModel.Factory { + + interface InteractionListener { + fun spaceInviteBottomSheetOnAccept(spaceId: String) + fun spaceInviteBottomSheetOnDecline(spaceId: String) + } + + var interactionListener: InteractionListener? = null + + @Parcelize + data class Args( + val spaceId: String + ) : Parcelable + + @Inject + lateinit var avatarRenderer: AvatarRenderer + + private val viewModel: SpaceInviteBottomSheetViewModel by fragmentViewModel(SpaceInviteBottomSheetViewModel::class) + + @Inject lateinit var viewModelFactory: SpaceInviteBottomSheetViewModel.Factory + + override fun create(initialState: SpaceInviteBottomSheetState) = viewModelFactory.create(initialState) + + 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) + + views.spaceCard.matrixToCardMainButton.callback = object : ButtonStateView.Callback { + override fun onButtonClicked() { + // quick local echo + views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Loading) + views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = false + viewModel.handle(SpaceInviteBottomSheetAction.DoJoin) + } + + override fun onRetryClicked() = onButtonClicked() + } + views.spaceCard.matrixToCardSecondaryButton.callback = object : ButtonStateView.Callback { + override fun onButtonClicked() { + views.spaceCard.matrixToCardMainButton.button.isEnabled = false + views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Loading) + viewModel.handle(SpaceInviteBottomSheetAction.DoReject) + } + + override fun onRetryClicked() = onButtonClicked() + } + + 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) { + views.inviterAvatarImage.isVisible = true + views.inviterText.isVisible = true + views.inviterMxid.isVisible = true + avatarRenderer.render(inviter, views.inviterAvatarImage) + views.inviterText.text = getString(R.string.user_invites_you, inviter.getBestName()) + views.inviterMxid.text = inviter.id + } else { + views.inviterAvatarImage.isVisible = false + views.inviterText.isVisible = false + views.inviterMxid.isVisible = false + } + + views.spaceCard.matrixToCardContentVisibility.isVisible = true + summary?.toMatrixItem()?.let { avatarRenderer.renderSpace(it, 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.button.text = getString(R.string.accept) + views.spaceCard.matrixToCardSecondaryButton.button.text = getString(R.string.decline) + + when (state.joinActionState) { + Uninitialized -> { + views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Button) + } + is Loading -> { + views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Loading) + views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = false + } + is Success -> { + interactionListener?.spaceInviteBottomSheetOnAccept(inviteArgs.spaceId) + dismiss() + } + is Fail -> { + views.spaceCard.matrixToCardMainButton.render(ButtonStateView.State.Error) + views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = true + } + } + + when (state.rejectActionState) { + Uninitialized -> { + views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Button) + } + is Loading -> { + views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Loading) + views.spaceCard.matrixToCardMainButton.button.isEnabled = false + } + is Success -> { + interactionListener?.spaceInviteBottomSheetOnDecline(inviteArgs.spaceId) + dismiss() + } + is Fail -> { + views.spaceCard.matrixToCardSecondaryButton.render(ButtonStateView.State.Error) + views.spaceCard.matrixToCardSecondaryButton.button.isEnabled = true + } + } + + val memberCount = summary?.otherMemberIds?.size ?: 0 + 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 peopleYouKnow = state.peopleYouKnow.invoke().orEmpty() + + 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) + : SpaceInviteBottomSheet { + return SpaceInviteBottomSheet().apply { + setArguments(Args(spaceId)) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetAction.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetAction.kt new file mode 100644 index 0000000000..b8e3283aff --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetAction.kt @@ -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() +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetEvents.kt new file mode 100644 index 0000000000..b097d19d9e --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetEvents.kt @@ -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() +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetState.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetState.kt new file mode 100644 index 0000000000..d712cf9e8a --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetState.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.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 + ) +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt new file mode 100644 index 0000000000..4524b57004 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheetViewModel.kt @@ -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))) + } + } + } + } + } +} 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<ChildInfo> = Uninitialized, - val childInfoList: Async<List<ChildInfo>> = Uninitialized + val childInfoList: Async<List<ChildInfo>> = Uninitialized, + val inviteTermination: Async<Unit> = 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..61328d2a1c 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<SpacePreviewState, SpacePreviewViewAction, SpacePreviewViewEvents>(initialState) { @@ -80,13 +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) } catch (failure: Throwable) { Timber.e(failure, "## Space: Failed to reject invite") + _viewEvents.post(SpacePreviewViewEvents.JoinFailure(errorFormatter.toHumanReadable(failure))) } + setState { copy(inviteTermination = Uninitialized) } } } @@ -98,18 +102,25 @@ 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(errorFormatter.toHumanReadable(joinResult.error))) + } } + } catch (failure: Throwable) { + // should not throw + Timber.w(failure, "## Failed to join space") + _viewEvents.post(SpacePreviewViewEvents.JoinFailure(errorFormatter.toHumanReadable(failure))) } } } @@ -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 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/bottomSheetScrollView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:animateLayoutChanges="true" + android:background="?riotx_bottom_sheet_background" + android:fadeScrollbars="false" + android:scrollbars="vertical"> + + <!-- Inviter info--> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:padding="16dp" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/inviterAvatarImage" + android:layout_width="40dp" + android:layout_height="40dp" + android:contentDescription="@string/avatar" + 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" /> + + + <TextView + android:id="@+id/inviterText" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:textColor="?riotx_text_primary" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/inviterAvatarImage" + app:layout_constraintTop_toTopOf="@id/inviterAvatarImage" + tools:text="@string/user_invites_you" /> + + <TextView + android:id="@+id/inviterMxid" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:ellipsize="end" + android:maxLines="1" + android:textColor="?riotx_text_secondary" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/inviterAvatarImage" + app:layout_constraintTop_toBottomOf="@id/inviterText" + tools:text="@sample/matrix.json/data/mxid" /> + + + </androidx.constraintlayout.widget.ConstraintLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/vctr_list_divider_color" /> + + <include + android:id="@+id/spaceCard" + layout="@layout/fragment_matrix_to_room_space_card" /> + + </LinearLayout> + +</androidx.core.widget.NestedScrollView> \ 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 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="200dp" - android:padding="16dp"> + android:layout_height="wrap_content"> - <ImageView - android:id="@+id/matrixToCardAvatar" - android:layout_width="60dp" - android:layout_height="60dp" - android:layout_marginTop="@dimen/layout_vertical_margin_big" - 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" /> - - <TextView - android:id="@+id/matrixToCardNameText" + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/matrixToCardContentVisibility" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/layout_vertical_margin" - android:maxLines="1" - android:singleLine="true" - android:textAlignment="textStart" - android:textColor="?riotx_text_primary" - android:textSize="15sp" - android:textStyle="bold" - app:layout_constraintTop_toBottomOf="@+id/matrixToCardAvatar" - tools:text="@sample/matrix.json/data/roomName" /> - - <TextView - android:id="@+id/matrixToCardAliasText" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="4dp" - android:maxLines="1" - android:singleLine="true" - android:textAlignment="textStart" - android:textColor="?riotx_text_secondary" - android:textSize="15sp" + android:minHeight="200dp" + android:padding="16dp" android:visibility="gone" - app:layout_constraintTop_toBottomOf="@id/matrixToCardNameText" - app:layout_goneMarginTop="0dp" - tools:text="@sample/matrix.json/data/roomAlias" /> - - <LinearLayout - android:id="@+id/matrixToMemberPills" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:background="@drawable/pill_receipt" - android:gravity="center" - android:orientation="horizontal" - android:paddingStart="12dp" - android:paddingTop="8dp" - android:paddingEnd="12dp" - android:paddingBottom="8dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/matrixToCardAliasText"> + tools:visibility="visible"> <ImageView - android:id="@+id/spaceChildMemberCountIcon" - android:layout_width="18dp" - android:layout_height="18dp" - android:layout_gravity="center_vertical" - android:layout_marginEnd="4dp" - android:importantForAccessibility="no" - android:src="@drawable/ic_room_profile_member_list" - app:tint="?riotx_text_primary" - tools:ignore="MissingPrefix" /> + 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" /> - <TextView - android:id="@+id/spaceChildMemberCountText" + <ImageView + android:id="@+id/matrixToBetaTag" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" + android:layout_marginTop="@dimen/layout_vertical_margin_big" + android:importantForAccessibility="no" + android:src="@drawable/ic_beta_pill" + android:visibility="gone" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:visibility="visible" /> + + <TextView + android:id="@+id/matrixToCardNameText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/layout_vertical_margin" android:maxLines="1" + android:singleLine="true" + android:textAlignment="textStart" android:textColor="?riotx_text_primary" - tools:text="123 members" /> - </LinearLayout> + android:textSize="15sp" + android:textStyle="bold" + app:layout_constraintTop_toBottomOf="@+id/matrixToCardAvatar" + tools:text="@sample/matrix.json/data/roomName" /> - <TextView - android:id="@+id/matrixToCardDescText" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:maxLines="4" - android:textAlignment="textStart" - android:textColor="?riotx_text_secondary" - android:textSize="15sp" - app:layout_constraintTop_toBottomOf="@id/matrixToMemberPills" - tools:text="@sample/matrix.json/data/roomTopic" /> + <TextView + android:id="@+id/matrixToCardAliasText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:maxLines="1" + android:singleLine="true" + android:textAlignment="textStart" + android:textColor="?riotx_text_secondary" + android:textSize="15sp" + android:visibility="gone" + app:layout_constraintTop_toBottomOf="@id/matrixToCardNameText" + app:layout_goneMarginTop="0dp" + tools:text="@sample/matrix.json/data/roomAlias" /> - <ImageView - android:id="@+id/knownMember5" - android:layout_width="30dp" - android:layout_height="30dp" - android:contentDescription="@string/avatar" - app:layout_constraintCircle="@id/knownMember4" - app:layout_constraintCircleAngle="90" - app:layout_constraintCircleRadius="20dp" - tools:ignore="MissingConstraints" - tools:src="@tools:sample/avatars" /> + <LinearLayout + android:id="@+id/matrixToMemberPills" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:background="@drawable/pill_receipt" + android:gravity="center" + android:orientation="horizontal" + android:paddingStart="12dp" + android:paddingTop="8dp" + android:paddingEnd="12dp" + android:paddingBottom="8dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/matrixToCardAliasText"> - <ImageView - android:id="@+id/knownMember4" - android:layout_width="30dp" - android:layout_height="30dp" - android:contentDescription="@string/avatar" - app:layout_constraintCircle="@id/knownMember3" - app:layout_constraintCircleAngle="90" - app:layout_constraintCircleRadius="20dp" - tools:ignore="MissingConstraints" - tools:src="@tools:sample/avatars" /> + <ImageView + android:id="@+id/spaceChildMemberCountIcon" + android:layout_width="18dp" + android:layout_height="18dp" + android:layout_gravity="center_vertical" + android:layout_marginEnd="4dp" + android:importantForAccessibility="no" + android:src="@drawable/ic_room_profile_member_list" + app:tint="?riotx_text_primary" + tools:ignore="MissingPrefix" /> - <ImageView - android:id="@+id/knownMember3" - android:layout_width="30dp" - android:layout_height="30dp" - android:contentDescription="@string/avatar" - app:layout_constraintCircle="@id/knownMember2" - app:layout_constraintCircleAngle="90" - app:layout_constraintCircleRadius="20dp" - tools:ignore="MissingConstraints" - tools:src="@tools:sample/avatars" /> + <TextView + android:id="@+id/spaceChildMemberCountText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:maxLines="1" + android:textColor="?riotx_text_primary" + tools:text="123 members" /> + </LinearLayout> - <ImageView - android:id="@+id/knownMember2" - android:layout_width="30dp" - android:layout_height="30dp" - android:contentDescription="@string/avatar" - app:layout_constraintCircle="@id/knownMember1" - app:layout_constraintCircleAngle="90" - app:layout_constraintCircleRadius="20dp" - tools:ignore="MissingConstraints" - tools:src="@tools:sample/avatars" /> + <TextView + android:id="@+id/matrixToCardDescText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:maxLines="4" + android:textAlignment="textStart" + android:textColor="?riotx_text_secondary" + android:textSize="15sp" + app:layout_constraintTop_toBottomOf="@id/matrixToMemberPills" + tools:text="@sample/matrix.json/data/roomTopic" /> - <ImageView - android:id="@+id/knownMember1" - android:layout_width="30dp" - android:layout_height="30dp" - android:layout_marginTop="8dp" - android:contentDescription="@string/avatar" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/matrixToCardDescText" - tools:src="@tools:sample/avatars" /> + <ImageView + android:id="@+id/knownMember5" + android:layout_width="30dp" + android:layout_height="30dp" + android:contentDescription="@string/avatar" + app:layout_constraintCircle="@id/knownMember4" + app:layout_constraintCircleAngle="90" + app:layout_constraintCircleRadius="20dp" + tools:ignore="MissingConstraints" + tools:src="@tools:sample/avatars" + tools:visibility="gone" /> - <TextView - android:id="@+id/peopleYouMayKnowText" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="20dp" - android:textColor="?riotx_text_secondary" - android:textSize="15sp" - app:layout_constraintBottom_toBottomOf="@id/knownMember1" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toEndOf="@id/knownMember5" - app:layout_constraintTop_toTopOf="@id/knownMember1" - tools:text="7 people you may know" /> + <ImageView + android:id="@+id/knownMember4" + android:layout_width="30dp" + android:layout_height="30dp" + android:contentDescription="@string/avatar" + app:layout_constraintCircle="@id/knownMember3" + app:layout_constraintCircleAngle="90" + app:layout_constraintCircleRadius="20dp" + tools:ignore="MissingConstraints" + tools:src="@tools:sample/avatars" + tools:visibility="gone" /> - <com.google.android.material.button.MaterialButton - android:id="@+id/matrixToCardMainButton" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:layout_marginBottom="@dimen/layout_vertical_margin_big" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/knownMember1" - app:layout_constraintWidth_max="400dp" - tools:text="@string/join" /> + <ImageView + android:id="@+id/knownMember3" + android:layout_width="30dp" + android:layout_height="30dp" + android:contentDescription="@string/avatar" + app:layout_constraintCircle="@id/knownMember2" + app:layout_constraintCircleAngle="90" + app:layout_constraintCircleRadius="20dp" + tools:ignore="MissingConstraints" + tools:src="@tools:sample/avatars" /> - <com.google.android.material.button.MaterialButton - android:id="@+id/matrixToCardSecondaryButton" - style="@style/VectorButtonStyleOutlined" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_marginBottom="@dimen/layout_vertical_margin_big" - android:textAllCaps="true" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/matrixToCardMainButton" - app:layout_constraintWidth_max="400dp" - tools:text="@string/dismiss" /> + <ImageView + android:id="@+id/knownMember2" + android:layout_width="30dp" + android:layout_height="30dp" + android:contentDescription="@string/avatar" + app:layout_constraintCircle="@id/knownMember1" + app:layout_constraintCircleAngle="90" + app:layout_constraintCircleRadius="20dp" + tools:ignore="MissingConstraints" + tools:src="@tools:sample/avatars" /> + + <ImageView + android:id="@+id/knownMember1" + android:layout_width="30dp" + android:layout_height="30dp" + android:layout_marginTop="8dp" + android:contentDescription="@string/avatar" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/matrixToCardDescText" + tools:src="@tools:sample/avatars" /> + + <TextView + android:id="@+id/peopleYouMayKnowText" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="20dp" + android:textColor="?riotx_text_secondary" + android:textSize="15sp" + app:layout_constraintBottom_toBottomOf="@id/knownMember1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/knownMember5" + app:layout_constraintTop_toTopOf="@id/knownMember1" + tools:text="7 people you may know" /> + + <im.vector.app.core.platform.ButtonStateView + android:id="@+id/matrixToCardMainButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:layout_marginBottom="@dimen/layout_vertical_margin_big" + app:bsv_button_text="@string/accept" + app:bsv_loaded_image_src="@drawable/ic_tick" + app:bsv_use_flat_button="false" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/knownMember1" + app:layout_constraintWidth_max="400dp" /> + + <!-- <com.google.android.material.button.MaterialButton--> + <!-- android:id="@+id/matrixToCardMainButton"--> + <!-- android:layout_width="0dp"--> + <!-- android:layout_height="wrap_content"--> + <!-- android:layout_marginTop="16dp"--> + <!-- android:layout_marginBottom="@dimen/layout_vertical_margin_big"--> + <!-- app:layout_constraintEnd_toEndOf="parent"--> + <!-- app:layout_constraintStart_toStartOf="parent"--> + <!-- app:layout_constraintTop_toBottomOf="@id/knownMember1"--> + <!-- app:layout_constraintWidth_max="400dp"--> + <!-- tools:text="@string/join" />--> + + <!-- <com.google.android.material.button.MaterialButton--> + <!-- android:id="@+id/matrixToCardSecondaryButton"--> + <!-- style="@style/VectorButtonStyleOutlined"--> + <!-- android:layout_width="0dp"--> + <!-- android:layout_height="wrap_content"--> + <!-- android:layout_marginTop="8dp"--> + <!-- android:layout_marginBottom="@dimen/layout_vertical_margin_big"--> + <!-- android:textAllCaps="true"--> + <!-- app:layout_constraintBottom_toBottomOf="parent"--> + <!-- app:layout_constraintEnd_toEndOf="parent"--> + <!-- app:layout_constraintStart_toStartOf="parent"--> + <!-- app:layout_constraintTop_toBottomOf="@id/matrixToCardMainButton"--> + <!-- app:layout_constraintWidth_max="400dp"--> + <!-- tools:text="@string/dismiss" />--> + + <im.vector.app.core.platform.ButtonStateView + android:id="@+id/matrixToCardSecondaryButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_marginBottom="@dimen/layout_vertical_margin_big" + app:bsv_button_text="@string/dismiss" + app:bsv_loaded_image_src="@drawable/ic_tick" + app:bsv_use_flat_button="true" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/matrixToCardMainButton" + app:layout_constraintWidth_max="400dp" /> + + </androidx.constraintlayout.widget.ConstraintLayout> <ProgressBar android:id="@+id/matrixToCardButtonLoading" style="?android:attr/progressBarStyleSmall" android:layout_width="20dp" android:layout_height="20dp" + android:layout_centerInParent="true" android:visibility="gone" - app:layout_constraintBottom_toBottomOf="@id/matrixToCardMainButton" - app:layout_constraintEnd_toEndOf="@id/matrixToCardMainButton" - app:layout_constraintStart_toStartOf="@id/matrixToCardMainButton" - app:layout_constraintTop_toTopOf="@id/matrixToCardMainButton" tools:visibility="visible" /> - <androidx.constraintlayout.widget.Group - android:id="@+id/matrixToCardPeopleYouKnowVisibility" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:constraint_referenced_ids="knownMember1,knownMember2,knownMember3,knownMember4,knownMember5,peopleYouMayKnowText" - tools:visibility="visible" /> - - <androidx.constraintlayout.widget.Group - android:id="@+id/matrixToCardContentVisibility" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="gone" - app:constraint_referenced_ids="matrixToCardNameText,matrixToCardAvatar,matrixToCardNameText,matrixToCardDescText, matrixToCardMainButton, matrixToCardAliasText, matrixToCardSecondaryButton, matrixToMemberPills,matrixToCardPeopleYouKnowVisibility" - tools:visibility="visible" /> - -</androidx.constraintlayout.widget.ConstraintLayout> +</RelativeLayout> 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 @@ <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" tools:parentTag="android.widget.FrameLayout"> <com.google.android.material.button.MaterialButton android:id="@+id/buttonStateButtonFlat" style="@style/VectorButtonStyleText" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" tools:layout_gravity="center_horizontal" @@ -16,7 +16,7 @@ <com.google.android.material.button.MaterialButton android:id="@+id/buttonStateButtonBig" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:minWidth="120dp" diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index bfd91a6707..f8b04ef3a6 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3352,4 +3352,5 @@ <string name="labs_use_restricted_join_rule">Experimental Space - Restricted Room.</string> <string name="labs_use_restricted_join_rule_desc">Warning requires server support and experimental room version</string> + <string name="user_invites_you">%s invites you</string> </resources> 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 @@ <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:background">@null</item> - <!--item name="android:textColor">?colorAccent</item--> + <item name="android:textColor">?colorAccent</item> <item name="colorControlHighlight">?colorAccent</item> </style> @@ -162,7 +162,7 @@ <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:background">@null</item> - <!--item name="android:textColor">?colorAccent</item--> + <item name="android:textColor">?colorAccent</item> <item name="colorControlHighlight">?colorAccent</item> <item name="strokeColor">@color/button_background_tint_selector</item> <item name="strokeWidth">1dp</item>