From 99d44717993acf990ae7aaae5d56b110465964d7 Mon Sep 17 00:00:00 2001 From: Valere Date: Wed, 5 May 2021 10:05:47 +0200 Subject: [PATCH 1/7] space beta people screen --- vector/src/main/AndroidManifest.xml | 1 + .../im/vector/app/core/di/FragmentModule.kt | 6 + .../im/vector/app/core/di/ViewModelModule.kt | 6 + .../ProfileMatrixItemWithPowerLevel.kt | 41 +++++ .../features/navigation/DefaultNavigator.kt | 18 +- .../members/RoomMemberListFragment.kt | 7 +- .../members/RoomMemberListViewModel.kt | 12 +- .../members/RoomMemberSummaryFilter.kt | 11 +- .../spaces/people/SpacePeopleActivity.kt | 104 +++++++++++ .../spaces/people/SpacePeopleFragment.kt | 152 ++++++++++++++++ .../people/SpacePeopleListController.kt | 167 ++++++++++++++++++ .../SpacePeopleSharedActionViewModel.kt | 31 ++++ .../spaces/people/SpacePeopleViewModel.kt | 130 ++++++++++++++ .../res/layout/activity_simple_loading.xml | 17 ++ .../fragment_recyclerview_with_search.xml | 96 ++++++++++ .../res/layout/item_profile_matrix_item.xml | 22 ++- vector/src/main/res/values/strings.xml | 2 + 17 files changed, 808 insertions(+), 15 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/epoxy/profiles/ProfileMatrixItemWithPowerLevel.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedActionViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt create mode 100644 vector/src/main/res/layout/activity_simple_loading.xml create mode 100644 vector/src/main/res/layout/fragment_recyclerview_with_search.xml diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 1e2bf1ab0f..ea4ea9fd12 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -295,6 +295,7 @@ + () { + + @EpoxyAttribute var powerLevelLabel: CharSequence? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.editableView.isVisible = false + holder.powerLabel.setTextOrHide(powerLevelLabel) + } + + class Holder : ProfileMatrixItem.Holder() { + val powerLabel by bind(R.id.matrixItemPowerLevelLabel) + } +} diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 735a29afa4..c38dccd63b 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -75,6 +75,7 @@ import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet import im.vector.app.features.spaces.SpaceExploreActivity import im.vector.app.features.spaces.SpacePreviewActivity import im.vector.app.features.spaces.manage.SpaceManageActivity +import im.vector.app.features.spaces.people.SpacePeopleActivity import im.vector.app.features.terms.ReviewTermsActivity import im.vector.app.features.widgets.WidgetActivity import im.vector.app.features.widgets.WidgetArgsBuilder @@ -283,8 +284,21 @@ class DefaultNavigator @Inject constructor( } override fun openCreateDirectRoom(context: Context) { - val intent = CreateDirectRoomActivity.getIntent(context) - context.startActivity(intent) + when (val currentGroupingMethod = appStateHandler.getCurrentRoomGroupingMethod()) { + is RoomGroupingMethod.ByLegacyGroup -> { + val intent = CreateDirectRoomActivity.getIntent(context) + context.startActivity(intent) + } + is RoomGroupingMethod.BySpace -> { + if (currentGroupingMethod.spaceSummary != null) { + val intent = SpacePeopleActivity.newIntent(context, currentGroupingMethod.spaceSummary.roomId) + context.startActivity(intent) + } else { + val intent = CreateDirectRoomActivity.getIntent(context) + context.startActivity(intent) + } + } + } } override fun openInviteUsersToRoom(context: Context, roomId: String) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt index 2ff89d6e54..591e3b84e0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt @@ -47,11 +47,16 @@ class RoomMemberListFragment @Inject constructor( private val roomMemberListController: RoomMemberListController, private val avatarRenderer: AvatarRenderer ) : VectorBaseFragment(), - RoomMemberListController.Callback { + RoomMemberListController.Callback, + RoomMemberListViewModel.Factory { private val viewModel: RoomMemberListViewModel by fragmentViewModel() private val roomProfileArgs: RoomProfileArgs by args() + override fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel { + return viewModelFactory.create(initialState) + } + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomMemberListBinding { return FragmentRoomMemberListBinding.inflate(inflater, container, false) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index 270b3cb408..5c6ff48403 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -17,12 +17,13 @@ package im.vector.app.features.roomprofile.members import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -62,8 +63,11 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState @JvmStatic override fun create(viewModelContext: ViewModelContext, state: RoomMemberListViewState): RoomMemberListViewModel? { - val fragment: RoomMemberListFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.viewModelFactory.create(state) + 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") } } @@ -188,7 +192,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState override fun handle(action: RoomMemberListAction) { when (action) { is RoomMemberListAction.RevokeThreePidInvite -> handleRevokeThreePidInvite(action) - is RoomMemberListAction.FilterMemberList -> handleFilterMemberList(action) + is RoomMemberListAction.FilterMemberList -> handleFilterMemberList(action) }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt index e2cc3f7b99..a9e55f91c3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt @@ -29,9 +29,12 @@ class RoomMemberSummaryFilter @Inject constructor() : Predicate + acc + && (roomMemberSummary.displayName?.contains(s, ignoreCase = true).orFalse() + // We should maybe exclude the domain from the userId + || roomMemberSummary.userId.contains(s, ignoreCase = true)) + } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt new file mode 100644 index 0000000000..1cc2203042 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt @@ -0,0 +1,104 @@ +/* + * 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.people + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.core.view.isGone +import androidx.core.view.isVisible +import com.airbnb.mvrx.MvRx +import im.vector.app.R +import im.vector.app.core.extensions.commitTransaction +import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.databinding.ActivitySimpleLoadingBinding +import im.vector.app.features.roomprofile.RoomProfileArgs +import im.vector.app.features.spaces.ShareSpaceBottomSheet + +class SpacePeopleActivity : VectorBaseActivity() { + + override fun getBinding() = ActivitySimpleLoadingBinding.inflate(layoutInflater) + + private lateinit var sharedActionViewModel: SpacePeopleSharedActionViewModel + + override fun initUiAndData() { + super.initUiAndData() + waitingView = views.waitingView.waitingView + } + + override fun showWaitingView(text: String?) { + hideKeyboard() + views.waitingView.waitingStatusText.isGone = views.waitingView.waitingStatusText.text.isNullOrBlank() + super.showWaitingView(text) + } + + override fun hideWaitingView() { + views.waitingView.waitingStatusText.text = null + views.waitingView.waitingStatusText.isGone = true + views.waitingView.waitingHorizontalProgress.progress = 0 + views.waitingView.waitingHorizontalProgress.isVisible = false + super.hideWaitingView() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val args = intent?.getParcelableExtra(MvRx.KEY_ARG) + if (isFirstCreation()) { + val simpleName = SpacePeopleFragment::class.java.simpleName + if (supportFragmentManager.findFragmentByTag(simpleName) == null) { + supportFragmentManager.commitTransaction { + replace(R.id.simpleFragmentContainer, + SpacePeopleFragment::class.java, + Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) }, + simpleName + ) + } + } + } + + sharedActionViewModel = viewModelProvider.get(SpacePeopleSharedActionViewModel::class.java) + sharedActionViewModel + .observe() + .subscribe { sharedAction -> + when (sharedAction) { + SpacePeopleSharedAction.Dismiss -> finish() + is SpacePeopleSharedAction.NavigateToRoom -> navigateToRooms(sharedAction) + SpacePeopleSharedAction.HideModalLoading -> hideWaitingView() + SpacePeopleSharedAction.ShowModalLoading -> { + showWaitingView() + } + is SpacePeopleSharedAction.NavigateToInvite -> { + ShareSpaceBottomSheet.show(supportFragmentManager, sharedAction.spaceId) + } + } + }.disposeOnDestroy() + } + + private fun navigateToRooms(action: SpacePeopleSharedAction.NavigateToRoom) { + navigator.openRoom(this, action.roomId) + finish() + } + + companion object { + fun newIntent(context: Context, spaceId: String): Intent { + return Intent(context, SpacePeopleActivity::class.java).apply { + putExtra(MvRx.KEY_ARG, RoomProfileArgs(spaceId)) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt new file mode 100644 index 0000000000..7f5369a6ec --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt @@ -0,0 +1,152 @@ +/* + * 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.people + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState +import com.jakewharton.rxbinding3.appcompat.queryTextChanges +import im.vector.app.R +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.OnBackPressed +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentRecyclerviewWithSearchBinding +import im.vector.app.features.roomprofile.members.RoomMemberListAction +import im.vector.app.features.roomprofile.members.RoomMemberListViewModel +import im.vector.app.features.roomprofile.members.RoomMemberListViewState +import io.reactivex.rxkotlin.subscribeBy +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +class SpacePeopleFragment @Inject constructor( + private val viewModelFactory: SpacePeopleViewModel.Factory, + private val roomMemberModelFactory: RoomMemberListViewModel.Factory, + private val epoxyController: SpacePeopleListController +) : VectorBaseFragment(), + SpacePeopleViewModel.Factory, + RoomMemberListViewModel.Factory, + OnBackPressed, SpacePeopleListController.InteractionListener { + + private val viewModel by fragmentViewModel(SpacePeopleViewModel::class) + private val membersViewModel by fragmentViewModel(RoomMemberListViewModel::class) + private lateinit var sharedActionViewModel: SpacePeopleSharedActionViewModel + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = + FragmentRecyclerviewWithSearchBinding.inflate(inflater, container, false) + + override fun onBackPressed(toolbarButton: Boolean): Boolean { + sharedActionViewModel.post(SpacePeopleSharedAction.Dismiss) + return true + } + + override fun create(initialState: SpacePeopleViewState): SpacePeopleViewModel { + return viewModelFactory.create(initialState) + } + + override fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel { + return roomMemberModelFactory.create(initialState) + } + + override fun invalidate() = withState(viewModel, membersViewModel) { baseState, memberListState -> + views.appBarTitle.text = getString(R.string.bottom_action_people) + val memberCount = (memberListState.roomSummary.invoke()?.otherMemberIds?.size ?: 0) + 1 + views.appBarSpaceInfo.text = resources.getQuantityString(R.plurals.room_title_members, memberCount, memberCount) +// views.listBuildingProgress.isVisible = true + epoxyController.setData(memberListState) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + sharedActionViewModel = activityViewModelProvider.get(SpacePeopleSharedActionViewModel::class.java) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupRecyclerView() + setupSearchView() + + views.addRoomToSpaceToolbar.navigationIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_close_24dp) + views.addRoomToSpaceToolbar.setNavigationOnClickListener { + sharedActionViewModel.post(SpacePeopleSharedAction.Dismiss) + } + + viewModel.observeViewEvents { + handleViewEvents(it) + } + + viewModel.subscribe(this) { + when (it.createAndInviteState) { + is Loading -> sharedActionViewModel.post(SpacePeopleSharedAction.ShowModalLoading) + Uninitialized, + is Fail -> sharedActionViewModel.post(SpacePeopleSharedAction.HideModalLoading) + is Success -> { + // don't hide on success, it will navigate out. If not the loading goes out before navigation + } + } + } + } + + override fun onDestroyView() { + epoxyController.listener = null + views.roomList.cleanup() + super.onDestroyView() + } + + private fun setupRecyclerView() { + views.roomList.configureWith(epoxyController, hasFixedSize = false, disableItemAnimation = false) + epoxyController.listener = this + } + + private fun setupSearchView() { + views.memberNameFilter.queryHint = getString(R.string.search_members_hint) + views.memberNameFilter.queryTextChanges() + .debounce(100, TimeUnit.MILLISECONDS) + .subscribeBy { + membersViewModel.handle(RoomMemberListAction.FilterMemberList(it.toString())) + } + .disposeOnDestroyView() + } + + private fun handleViewEvents(events: SpacePeopleViewEvents) { + when (events) { + is SpacePeopleViewEvents.OpenRoom -> { + sharedActionViewModel.post(SpacePeopleSharedAction.NavigateToRoom(events.roomId)) + } + is SpacePeopleViewEvents.InviteToSpace -> { + sharedActionViewModel.post(SpacePeopleSharedAction.NavigateToInvite(events.spaceId)) + } + } + } + + override fun onSpaceMemberClicked(roomMemberSummary: RoomMemberSummary) { + viewModel.handle(SpacePeopleViewAction.ChatWith(roomMemberSummary)) + } + + override fun onInviteToSpaceSelected() { + viewModel.handle(SpacePeopleViewAction.InviteToSpace) + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt new file mode 100644 index 0000000000..fc982862a7 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt @@ -0,0 +1,167 @@ +/* + * 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.people + +import com.airbnb.epoxy.TypedEpoxyController +import im.vector.app.R +import im.vector.app.core.epoxy.dividerItem +import im.vector.app.core.epoxy.loadingItem +import im.vector.app.core.epoxy.profiles.profileMatrixItemWithPowerLevel +import im.vector.app.core.extensions.join +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.list.GenericItem +import im.vector.app.core.ui.list.genericItem +import im.vector.app.core.utils.DimensionConverter +import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.roomprofile.members.RoomMemberListCategories +import im.vector.app.features.roomprofile.members.RoomMemberListViewState +import im.vector.app.features.roomprofile.members.RoomMemberSummaryFilter +import me.gujun.android.span.span +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.util.toMatrixItem +import javax.inject.Inject + +class SpacePeopleListController @Inject constructor( + private val avatarRenderer: AvatarRenderer, + private val colorProvider: ColorProvider, + private val stringProvider: StringProvider, + private val dimensionConverter: DimensionConverter, + private val roomMemberSummaryFilter: RoomMemberSummaryFilter +) : TypedEpoxyController() { + + private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color) + + interface InteractionListener { + fun onSpaceMemberClicked(roomMemberSummary: RoomMemberSummary) + fun onInviteToSpaceSelected() + } + + var listener: InteractionListener? = null + + init { + setData(null) + } + + override fun buildModels(data: RoomMemberListViewState?) { + val memberSummaries = data?.roomMemberSummaries?.invoke() + if (memberSummaries == null) { + loadingItem { id("loading") } + return + } + roomMemberSummaryFilter.filter = data.filter + var foundCount = 0 + memberSummaries.forEach { memberEntry -> + + val filtered = memberEntry.second + .filter { roomMemberSummaryFilter.test(it) } + if (filtered.isNotEmpty()) { + dividerItem { + id("divider_type_${memberEntry.first.titleRes}") + color(dividerColor) + } + } + foundCount += filtered.size + filtered + .join( + each = { _, roomMember -> + profileMatrixItemWithPowerLevel { + id(roomMember.userId) + matrixItem(roomMember.toMatrixItem()) + avatarRenderer(avatarRenderer) + userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId)) + .apply { + val pl = memberEntry.first.toPowerLevelLabel() + if (memberEntry.first == RoomMemberListCategories.INVITE) { + powerLevelLabel( + span { + span(stringProvider.getString(R.string.invited)) { + textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) + textStyle = "bold" + // fontFamily = "monospace" + } + } + ) + } else if (pl != null) { + powerLevelLabel( + span { + span(" $pl ") { + backgroundColor = colorProvider.getColor(R.color.notification_accent_color) + paddingTop = dimensionConverter.dpToPx(2) + paddingBottom = dimensionConverter.dpToPx(2) + textColor = colorProvider.getColor(R.color.white) + textStyle = "bold" + // fontFamily = "monospace" + } + } + ) + } else { + powerLevelLabel(null) + } + } + + clickListener { _ -> + listener?.onSpaceMemberClicked(roomMember) + } + } + }, + between = { _, roomMemberBefore -> + dividerItem { + id("divider_${roomMemberBefore.userId}") + color(dividerColor) + } + } + ) + } + + if (foundCount == 0 && data.filter.isNotEmpty()) { + // add the footer thing + genericItem { + id("not_found") + title( + span { + +"\n" + +stringProvider.getString(R.string.no_result_placeholder) + } + ) + description( + span { + +stringProvider.getString(R.string.looking_for_someone_not_in_space, data.roomSummary.invoke()?.displayName ?: "") + +"\n" + span("Invite them") { + textColor = colorProvider.getColorFromAttribute(R.attr.colorAccent) + textStyle = "bold" + } + } + ) + itemClickAction(GenericItem.Action("invite").apply { + perform = Runnable { + listener?.onInviteToSpaceSelected() + } + }) + } + } + } + + private fun RoomMemberListCategories.toPowerLevelLabel(): String? { + return when (this) { + RoomMemberListCategories.ADMIN -> stringProvider.getString(R.string.power_level_admin) + RoomMemberListCategories.MODERATOR -> stringProvider.getString(R.string.power_level_moderator) + else -> null + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedActionViewModel.kt new file mode 100644 index 0000000000..649f241bf9 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedActionViewModel.kt @@ -0,0 +1,31 @@ +/* + * 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.people + +import im.vector.app.core.platform.VectorSharedAction +import im.vector.app.core.platform.VectorSharedActionViewModel +import javax.inject.Inject + +sealed class SpacePeopleSharedAction : VectorSharedAction { + object Dismiss : SpacePeopleSharedAction() + object ShowModalLoading : SpacePeopleSharedAction() + object HideModalLoading : SpacePeopleSharedAction() + data class NavigateToRoom(val roomId: String) : SpacePeopleSharedAction() + data class NavigateToInvite(val spaceId: String) : SpacePeopleSharedAction() +} + +class SpacePeopleSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel() diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt new file mode 100644 index 0000000000..71c3bcdda7 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt @@ -0,0 +1,130 @@ +/* + * 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.people + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRxState +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.extensions.exhaustive +import im.vector.app.core.platform.VectorViewEvents +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.platform.VectorViewModelAction +import im.vector.app.features.raw.wellknown.getElementWellknown +import im.vector.app.features.raw.wellknown.isE2EByDefault +import im.vector.app.features.roomprofile.RoomProfileArgs +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.raw.RawService +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams + +data class SpacePeopleViewState( + val spaceId: String, + val createAndInviteState: Async = Uninitialized +) : MvRxState { + constructor(args: RoomProfileArgs) : this( + spaceId = args.roomId + ) +} + +sealed class SpacePeopleViewAction : VectorViewModelAction { + data class ChatWith(val member: RoomMemberSummary) : SpacePeopleViewAction() + object InviteToSpace : SpacePeopleViewAction() +} + +sealed class SpacePeopleViewEvents : VectorViewEvents { + data class OpenRoom(val roomId: String) : SpacePeopleViewEvents() + data class InviteToSpace(val spaceId: String) : SpacePeopleViewEvents() +} + +class SpacePeopleViewModel @AssistedInject constructor( + @Assisted val initialState: SpacePeopleViewState, + private val rawService: RawService, + private val session: Session +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory { + fun create(initialState: SpacePeopleViewState): SpacePeopleViewModel + } + + companion object : MvRxViewModelFactory { + override fun create(viewModelContext: ViewModelContext, state: SpacePeopleViewState): SpacePeopleViewModel? { + 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: SpacePeopleViewAction) { + when (action) { + is SpacePeopleViewAction.ChatWith -> handleChatWith(action) + SpacePeopleViewAction.InviteToSpace -> handleInviteToSpace() + }.exhaustive + } + + private fun handleInviteToSpace() { + _viewEvents.post(SpacePeopleViewEvents.InviteToSpace(initialState.spaceId)) + } + + private fun handleChatWith(action: SpacePeopleViewAction.ChatWith) { + val otherUserId = action.member.userId + if (otherUserId == session.myUserId) return + val existingRoomId = session.getExistingDirectRoomWithUser(otherUserId) + if (existingRoomId != null) { + // just open it + _viewEvents.post(SpacePeopleViewEvents.OpenRoom(existingRoomId)) + return + } + setState { copy(createAndInviteState = Loading()) } + + viewModelScope.launch(Dispatchers.IO) { + val adminE2EByDefault = rawService.getElementWellknown(session.myUserId) + ?.isE2EByDefault() + ?: true + + val roomParams = CreateRoomParams() + .apply { + invitedUserIds.add(otherUserId) + setDirectMessage() + enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault + } + + try { + val roomId = session.createRoom(roomParams) + _viewEvents.post(SpacePeopleViewEvents.OpenRoom(roomId)) + setState { copy(createAndInviteState = Success(roomId)) } + } catch (failure: Throwable) { + setState { copy(createAndInviteState = Fail(failure)) } + } + } + } +} diff --git a/vector/src/main/res/layout/activity_simple_loading.xml b/vector/src/main/res/layout/activity_simple_loading.xml new file mode 100644 index 0000000000..b523d97c92 --- /dev/null +++ b/vector/src/main/res/layout/activity_simple_loading.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/fragment_recyclerview_with_search.xml b/vector/src/main/res/layout/fragment_recyclerview_with_search.xml new file mode 100644 index 0000000000..a9c26cff6f --- /dev/null +++ b/vector/src/main/res/layout/fragment_recyclerview_with_search.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/layout/item_profile_matrix_item.xml b/vector/src/main/res/layout/item_profile_matrix_item.xml index d2ea7c01f5..29f537c315 100644 --- a/vector/src/main/res/layout/item_profile_matrix_item.xml +++ b/vector/src/main/res/layout/item_profile_matrix_item.xml @@ -40,7 +40,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" + android:layout_marginEnd="8dp" android:drawablePadding="16dp" android:ellipsize="end" android:maxLines="1" @@ -48,7 +48,7 @@ android:textSize="16sp" app:layout_constrainedWidth="true" app:layout_constraintBottom_toTopOf="@+id/matrixItemSubtitle" - app:layout_constraintEnd_toStartOf="@+id/matrixItemEditable" + app:layout_constraintEnd_toStartOf="@+id/matrixItemPowerLevelLabel" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toEndOf="@id/matrixItemAvatar" app:layout_constraintTop_toTopOf="parent" @@ -60,7 +60,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" + android:layout_marginEnd="8dp" android:drawablePadding="16dp" android:ellipsize="end" android:maxLines="1" @@ -68,13 +68,27 @@ android:textSize="12sp" app:layout_constrainedWidth="true" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/matrixItemEditable" + app:layout_constraintEnd_toStartOf="@+id/matrixItemPowerLevelLabel" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toEndOf="@id/matrixItemAvatar" app:layout_constraintTop_toBottomOf="@id/matrixItemTitle" app:layout_goneMarginStart="0dp" tools:text="@sample/matrix.json/data/mxid" /> + + + Experimental Space - Restricted Room. Warning requires server support and experimental room version %s invites you + + Looking for someone not in %s? From 2acfb294161bdfea6823734be773f445f9bde388 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 May 2021 17:46:58 +0200 Subject: [PATCH 2/7] Some cleanup --- .../epoxy/profiles/BaseProfileMatrixItem.kt | 2 ++ .../features/navigation/DefaultNavigator.kt | 15 +++++++------- .../members/RoomMemberSummaryFilter.kt | 7 +++---- .../res/layout/item_profile_matrix_item.xml | 14 ++++--------- .../item_profile_matrix_item_progress.xml | 20 +++++++------------ 5 files changed, 23 insertions(+), 35 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt b/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt index ccb3bea25a..9f865df372 100644 --- a/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt +++ b/vector/src/main/java/im/vector/app/core/epoxy/profiles/BaseProfileMatrixItem.kt @@ -17,6 +17,7 @@ package im.vector.app.core.epoxy.profiles import android.view.View +import androidx.annotation.CallSuper import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import im.vector.app.core.epoxy.VectorEpoxyModel @@ -34,6 +35,7 @@ abstract class BaseProfileMatrixItem : VectorEpoxy var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null @EpoxyAttribute var clickListener: View.OnClickListener? = null + @CallSuper override fun bind(holder: T) { super.bind(holder) val bestName = matrixItem.getBestName() diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index c38dccd63b..27de04210a 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -284,21 +284,20 @@ class DefaultNavigator @Inject constructor( } override fun openCreateDirectRoom(context: Context) { - when (val currentGroupingMethod = appStateHandler.getCurrentRoomGroupingMethod()) { + val intent = when (val currentGroupingMethod = appStateHandler.getCurrentRoomGroupingMethod()) { is RoomGroupingMethod.ByLegacyGroup -> { - val intent = CreateDirectRoomActivity.getIntent(context) - context.startActivity(intent) + CreateDirectRoomActivity.getIntent(context) } is RoomGroupingMethod.BySpace -> { if (currentGroupingMethod.spaceSummary != null) { - val intent = SpacePeopleActivity.newIntent(context, currentGroupingMethod.spaceSummary.roomId) - context.startActivity(intent) + SpacePeopleActivity.newIntent(context, currentGroupingMethod.spaceSummary.roomId) } else { - val intent = CreateDirectRoomActivity.getIntent(context) - context.startActivity(intent) + CreateDirectRoomActivity.getIntent(context) } } - } + else -> null + } ?: return + context.startActivity(intent) } override fun openInviteUsersToRoom(context: Context, roomId: String) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt index a9e55f91c3..bd9fb7b941 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt @@ -30,11 +30,10 @@ class RoomMemberSummaryFilter @Inject constructor() : Predicate - acc - && (roomMemberSummary.displayName?.contains(s, ignoreCase = true).orFalse() + return filter.split(" ").all { + roomMemberSummary.displayName?.contains(it, ignoreCase = true).orFalse() // We should maybe exclude the domain from the userId - || roomMemberSummary.userId.contains(s, ignoreCase = true)) + || roomMemberSummary.userId.contains(it, ignoreCase = true) } } } diff --git a/vector/src/main/res/layout/item_profile_matrix_item.xml b/vector/src/main/res/layout/item_profile_matrix_item.xml index 29f537c315..3d9aa0e876 100644 --- a/vector/src/main/res/layout/item_profile_matrix_item.xml +++ b/vector/src/main/res/layout/item_profile_matrix_item.xml @@ -37,11 +37,10 @@ - - diff --git a/vector/src/main/res/layout/item_profile_matrix_item_progress.xml b/vector/src/main/res/layout/item_profile_matrix_item_progress.xml index 3376d58598..55e8cecade 100644 --- a/vector/src/main/res/layout/item_profile_matrix_item_progress.xml +++ b/vector/src/main/res/layout/item_profile_matrix_item_progress.xml @@ -37,11 +37,10 @@ + tools:ignore="MissingPrefix" /> From 961f3bcd19caaeea811c94370c5ba16f9750cb69 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 May 2021 18:09:29 +0200 Subject: [PATCH 3/7] More cleanup and split into several files --- .../spaces/people/SpacePeopleFragment.kt | 4 +-- .../people/SpacePeopleListController.kt | 2 +- .../spaces/people/SpacePeopleSharedAction.kt | 27 ++++++++++++++++ .../SpacePeopleSharedActionViewModel.kt | 9 ------ .../spaces/people/SpacePeopleViewAction.kt | 25 +++++++++++++++ .../spaces/people/SpacePeopleViewEvents.kt | 24 ++++++++++++++ .../spaces/people/SpacePeopleViewModel.kt | 28 +---------------- .../spaces/people/SpacePeopleViewState.kt | 31 +++++++++++++++++++ 8 files changed, 111 insertions(+), 39 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewEvents.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt index 7f5369a6ec..e6a84a37cf 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt @@ -71,7 +71,7 @@ class SpacePeopleFragment @Inject constructor( return roomMemberModelFactory.create(initialState) } - override fun invalidate() = withState(viewModel, membersViewModel) { baseState, memberListState -> + override fun invalidate() = withState(membersViewModel) { memberListState -> views.appBarTitle.text = getString(R.string.bottom_action_people) val memberCount = (memberListState.roomSummary.invoke()?.otherMemberIds?.size ?: 0) + 1 views.appBarSpaceInfo.text = resources.getQuantityString(R.plurals.room_title_members, memberCount, memberCount) @@ -133,7 +133,7 @@ class SpacePeopleFragment @Inject constructor( private fun handleViewEvents(events: SpacePeopleViewEvents) { when (events) { - is SpacePeopleViewEvents.OpenRoom -> { + is SpacePeopleViewEvents.OpenRoom -> { sharedActionViewModel.post(SpacePeopleSharedAction.NavigateToRoom(events.roomId)) } is SpacePeopleViewEvents.InviteToSpace -> { diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt index fc982862a7..71be3690a1 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleListController.kt @@ -159,7 +159,7 @@ class SpacePeopleListController @Inject constructor( private fun RoomMemberListCategories.toPowerLevelLabel(): String? { return when (this) { - RoomMemberListCategories.ADMIN -> stringProvider.getString(R.string.power_level_admin) + RoomMemberListCategories.ADMIN -> stringProvider.getString(R.string.power_level_admin) RoomMemberListCategories.MODERATOR -> stringProvider.getString(R.string.power_level_moderator) else -> null } diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedAction.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedAction.kt new file mode 100644 index 0000000000..201196c865 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedAction.kt @@ -0,0 +1,27 @@ +/* + * 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.people + +import im.vector.app.core.platform.VectorSharedAction + +sealed class SpacePeopleSharedAction : VectorSharedAction { + object Dismiss : SpacePeopleSharedAction() + object ShowModalLoading : SpacePeopleSharedAction() + object HideModalLoading : SpacePeopleSharedAction() + data class NavigateToRoom(val roomId: String) : SpacePeopleSharedAction() + data class NavigateToInvite(val spaceId: String) : SpacePeopleSharedAction() +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedActionViewModel.kt index 649f241bf9..ace942cd63 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedActionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleSharedActionViewModel.kt @@ -16,16 +16,7 @@ package im.vector.app.features.spaces.people -import im.vector.app.core.platform.VectorSharedAction import im.vector.app.core.platform.VectorSharedActionViewModel import javax.inject.Inject -sealed class SpacePeopleSharedAction : VectorSharedAction { - object Dismiss : SpacePeopleSharedAction() - object ShowModalLoading : SpacePeopleSharedAction() - object HideModalLoading : SpacePeopleSharedAction() - data class NavigateToRoom(val roomId: String) : SpacePeopleSharedAction() - data class NavigateToInvite(val spaceId: String) : SpacePeopleSharedAction() -} - class SpacePeopleSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel() diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewAction.kt new file mode 100644 index 0000000000..39d9dddc3a --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewAction.kt @@ -0,0 +1,25 @@ +/* + * 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.people + +import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary + +sealed class SpacePeopleViewAction : VectorViewModelAction { + data class ChatWith(val member: RoomMemberSummary) : SpacePeopleViewAction() + object InviteToSpace : SpacePeopleViewAction() +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewEvents.kt new file mode 100644 index 0000000000..e2d93668f2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewEvents.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.people + +import im.vector.app.core.platform.VectorViewEvents + +sealed class SpacePeopleViewEvents : VectorViewEvents { + data class OpenRoom(val roomId: String) : SpacePeopleViewEvents() + data class InviteToSpace(val spaceId: String) : SpacePeopleViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt index 71c3bcdda7..13944adfc8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewModel.kt @@ -18,51 +18,25 @@ package im.vector.app.features.spaces.people import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.MvRxState 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.extensions.exhaustive -import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.raw.wellknown.isE2EByDefault -import im.vector.app.features.roomprofile.RoomProfileArgs import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams -data class SpacePeopleViewState( - val spaceId: String, - val createAndInviteState: Async = Uninitialized -) : MvRxState { - constructor(args: RoomProfileArgs) : this( - spaceId = args.roomId - ) -} - -sealed class SpacePeopleViewAction : VectorViewModelAction { - data class ChatWith(val member: RoomMemberSummary) : SpacePeopleViewAction() - object InviteToSpace : SpacePeopleViewAction() -} - -sealed class SpacePeopleViewEvents : VectorViewEvents { - data class OpenRoom(val roomId: String) : SpacePeopleViewEvents() - data class InviteToSpace(val spaceId: String) : SpacePeopleViewEvents() -} - class SpacePeopleViewModel @AssistedInject constructor( @Assisted val initialState: SpacePeopleViewState, private val rawService: RawService, @@ -86,7 +60,7 @@ class SpacePeopleViewModel @AssistedInject constructor( override fun handle(action: SpacePeopleViewAction) { when (action) { - is SpacePeopleViewAction.ChatWith -> handleChatWith(action) + is SpacePeopleViewAction.ChatWith -> handleChatWith(action) SpacePeopleViewAction.InviteToSpace -> handleInviteToSpace() }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt new file mode 100644 index 0000000000..094145f901 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt @@ -0,0 +1,31 @@ +/* + * 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.people + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.roomprofile.RoomProfileArgs + +data class SpacePeopleViewState( + val spaceId: String, + val createAndInviteState: Async = Uninitialized +) : MvRxState { + constructor(args: RoomProfileArgs) : this( + spaceId = args.roomId + ) +} From ea3abee63a1c2d789353ed605fd9417668b31735 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 May 2021 18:12:45 +0200 Subject: [PATCH 4/7] Format --- .../fragment_recyclerview_with_search.xml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/vector/src/main/res/layout/fragment_recyclerview_with_search.xml b/vector/src/main/res/layout/fragment_recyclerview_with_search.xml index a9c26cff6f..a76c786026 100644 --- a/vector/src/main/res/layout/fragment_recyclerview_with_search.xml +++ b/vector/src/main/res/layout/fragment_recyclerview_with_search.xml @@ -4,8 +4,8 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/coordinatorLayout" android:layout_width="match_parent" - android:background="?riotx_background" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:background="?riotx_background"> + android:textStyle="bold" + tools:text="@tools:sample/lorem/random" /> + tools:text="@tools:sample/lorem/random" /> @@ -83,13 +83,13 @@ app:layout_constraintTop_toBottomOf="@+id/addRoomToSpaceToolbar" app:queryHint="@string/search_hint_room_name" /> - - - - - - - + + + + + + + From cdb3d7c3080caa1ffec13c542909cf1627517cec Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 May 2021 18:22:16 +0200 Subject: [PATCH 5/7] Create a GenericIdArgs instead of using unrelated GenericIdArgs --- .../vector/app/core/platform/GenericIdArgs.kt | 28 +++++++++++++++++++ .../spaces/people/SpacePeopleActivity.kt | 6 ++-- .../spaces/people/SpacePeopleViewState.kt | 6 ++-- 3 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/platform/GenericIdArgs.kt diff --git a/vector/src/main/java/im/vector/app/core/platform/GenericIdArgs.kt b/vector/src/main/java/im/vector/app/core/platform/GenericIdArgs.kt new file mode 100644 index 0000000000..0f22ae9136 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/platform/GenericIdArgs.kt @@ -0,0 +1,28 @@ +/* + * 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.core.platform + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Generic argument with one String. Can be an id (ex: roomId, spaceId, callId, etc.), or anything else + */ +@Parcelize +data class GenericIdArgs( + val id: String +) : Parcelable diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt index 1cc2203042..87f5356f6d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleActivity.kt @@ -25,9 +25,9 @@ import com.airbnb.mvrx.MvRx import im.vector.app.R import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.platform.GenericIdArgs import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleLoadingBinding -import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.spaces.ShareSpaceBottomSheet class SpacePeopleActivity : VectorBaseActivity() { @@ -57,7 +57,7 @@ class SpacePeopleActivity : VectorBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val args = intent?.getParcelableExtra(MvRx.KEY_ARG) + val args = intent?.getParcelableExtra(MvRx.KEY_ARG) if (isFirstCreation()) { val simpleName = SpacePeopleFragment::class.java.simpleName if (supportFragmentManager.findFragmentByTag(simpleName) == null) { @@ -97,7 +97,7 @@ class SpacePeopleActivity : VectorBaseActivity() { companion object { fun newIntent(context: Context, spaceId: String): Intent { return Intent(context, SpacePeopleActivity::class.java).apply { - putExtra(MvRx.KEY_ARG, RoomProfileArgs(spaceId)) + putExtra(MvRx.KEY_ARG, GenericIdArgs(spaceId)) } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt index 094145f901..ea322e3fbd 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleViewState.kt @@ -19,13 +19,13 @@ package im.vector.app.features.spaces.people import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized -import im.vector.app.features.roomprofile.RoomProfileArgs +import im.vector.app.core.platform.GenericIdArgs data class SpacePeopleViewState( val spaceId: String, val createAndInviteState: Async = Uninitialized ) : MvRxState { - constructor(args: RoomProfileArgs) : this( - spaceId = args.roomId + constructor(args: GenericIdArgs) : this( + spaceId = args.id ) } From 908e7be2bac2ca3b8da2f81ec45e4297e61333c9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 May 2021 18:36:33 +0200 Subject: [PATCH 6/7] Create a GenericIdArgs instead of using unrelated GenericIdArgs - fix crash --- .../features/roomprofile/members/RoomMemberListViewState.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt index 6fe8df3d36..63d07cc4dd 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt @@ -21,6 +21,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.app.R +import im.vector.app.core.platform.GenericIdArgs import im.vector.app.features.roomprofile.RoomProfileArgs import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.events.model.Event @@ -38,6 +39,8 @@ data class RoomMemberListViewState( ) : MvRxState { constructor(args: RoomProfileArgs) : this(roomId = args.roomId) + + constructor(args: GenericIdArgs) : this(roomId = args.id) } data class ActionPermissions( From ea93261b8315016cce2c47bfe5e8d10631db39ac Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 5 May 2021 18:51:43 +0200 Subject: [PATCH 7/7] Fix close icon color in dark theme --- .../app/features/spaces/people/SpacePeopleFragment.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt index e6a84a37cf..bd7a941249 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/people/SpacePeopleFragment.kt @@ -20,7 +20,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.content.ContextCompat import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success @@ -33,6 +32,8 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.DrawableProvider import im.vector.app.databinding.FragmentRecyclerviewWithSearchBinding import im.vector.app.features.roomprofile.members.RoomMemberListAction import im.vector.app.features.roomprofile.members.RoomMemberListViewModel @@ -45,6 +46,8 @@ import javax.inject.Inject class SpacePeopleFragment @Inject constructor( private val viewModelFactory: SpacePeopleViewModel.Factory, private val roomMemberModelFactory: RoomMemberListViewModel.Factory, + private val drawableProvider: DrawableProvider, + private val colorProvider: ColorProvider, private val epoxyController: SpacePeopleListController ) : VectorBaseFragment(), SpacePeopleViewModel.Factory, @@ -89,7 +92,10 @@ class SpacePeopleFragment @Inject constructor( setupRecyclerView() setupSearchView() - views.addRoomToSpaceToolbar.navigationIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_close_24dp) + views.addRoomToSpaceToolbar.navigationIcon = drawableProvider.getDrawable( + R.drawable.ic_close_24dp, + colorProvider.getColorFromAttribute(R.attr.riot_primary_text_color) + ) views.addRoomToSpaceToolbar.setNavigationOnClickListener { sharedActionViewModel.post(SpacePeopleSharedAction.Dismiss) }