From 94675b9f854ddaa2f3b6220866203ebb956f5f0f Mon Sep 17 00:00:00 2001 From: Yoan Pintas Date: Mon, 6 Mar 2023 23:05:43 +0100 Subject: [PATCH] create encrypted DM for user invite by email (#8172) Co-authored-by: jonnyandrew --- changelog.d/6912.misc | 1 + .../src/main/res/values/strings.xml | 3 + .../room/create/CreateRoomBodyBuilder.kt | 4 +- .../membership/RoomDisplayNameResolver.kt | 7 +- .../app/core/di/MavericksViewModelModule.kt | 6 -- .../createdirect/CreateDirectRoomActivity.kt | 1 + .../createdirect/CreateDirectRoomViewModel.kt | 2 +- .../home/room/detail/TimelineFragment.kt | 4 + .../timeline/factory/EncryptionItemFactory.kt | 43 ++++++---- .../timeline/item/MergedRoomCreationItem.kt | 33 +++++--- .../timeline/item/StatusTileTimelineItem.kt | 2 + .../HomeServerCapabilitiesViewModel.kt | 83 ------------------- .../HomeServerCapabilitiesViewState.kt | 25 ------ .../userdirectory/UserListController.kt | 8 ++ .../userdirectory/UserListFragment.kt | 5 +- .../userdirectory/UserListFragmentArgs.kt | 1 + .../userdirectory/UserListViewModel.kt | 31 ++++++- .../userdirectory/UserListViewState.kt | 3 + 18 files changed, 113 insertions(+), 149 deletions(-) create mode 100644 changelog.d/6912.misc delete mode 100644 vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt delete mode 100644 vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewState.kt diff --git a/changelog.d/6912.misc b/changelog.d/6912.misc new file mode 100644 index 0000000000..c43830e902 --- /dev/null +++ b/changelog.d/6912.misc @@ -0,0 +1 @@ +Direct Message: Manage encrypted DM in case of invite by email diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 2ee623cf88..62f061f009 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -1817,6 +1817,7 @@ Add by QR code QR code "Creating room…" + You can only invite one email at a time Known Users Suggestions @@ -2561,6 +2562,8 @@ Messages in this room are end-to-end encrypted. Learn more & verify users in their profile. Messages in this chat are end-to-end encrypted. Messages in this chat will be end-to-end encrypted. + Waiting for users to join ${app_name} + Once invited users have joined ${app_name}, you will be able to chat and the room will be end-to-end encrypted Encryption not enabled Encryption is misconfigured The encryption used by this room is not supported diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt index 4105c77cc8..92081885af 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt @@ -190,10 +190,8 @@ internal class CreateRoomBodyBuilder @Inject constructor( private suspend fun canEnableEncryption(params: CreateRoomParams): Boolean { return params.enableEncryptionIfInvitedUsersSupportIt && // Parity with web, enable if users have encryption ready devices - // for now remove checks on cross signing and 3pid invites + // for now remove checks on cross signing // && crossSigningService.isCrossSigningVerified() - params.invite3pids.isEmpty() && - params.invitedUserIds.isNotEmpty() && params.invitedUserIds.let { userIds -> val keys = deviceListManager.downloadKeys(userIds, forceDownload = false) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index 4645bb05ab..7497ecf21b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -115,7 +115,12 @@ internal class RoomDisplayNameResolver @Inject constructor( val leftMembersNames = roomMembers.queryLeftRoomMembersEvent() .findAll() .map { displayNameResolver.getBestName(it.toMatrixItem()) } - roomDisplayNameFallbackProvider.getNameForEmptyRoom(roomSummary?.isDirect.orFalse(), leftMembersNames) + val directUserId = roomSummary?.directUserId + if (!directUserId.isNullOrBlank() && leftMembersNames.isEmpty()) { + directUserId + } else { + roomDisplayNameFallbackProvider.getNameForEmptyRoom(roomSummary?.isDirect.orFalse(), leftMembersNames) + } } 1 -> { roomDisplayNameFallbackProvider.getNameFor1member( diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index 27981c3d36..8b435c9e6c 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -57,7 +57,6 @@ import im.vector.app.features.home.room.list.RoomListViewModel import im.vector.app.features.home.room.list.home.HomeRoomListViewModel import im.vector.app.features.home.room.list.home.invites.InvitesViewModel import im.vector.app.features.home.room.list.home.release.ReleaseNotesViewModel -import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel import im.vector.app.features.invite.InviteUsersToRoomViewModel import im.vector.app.features.location.LocationSharingViewModel import im.vector.app.features.location.live.map.LiveLocationMapViewModel @@ -500,11 +499,6 @@ interface MavericksViewModelModule { @MavericksViewModelKey(StartAppViewModel::class) fun startAppViewModelFactory(factory: StartAppViewModel.Factory): MavericksAssistedViewModelFactory<*, *> - @Binds - @IntoMap - @MavericksViewModelKey(HomeServerCapabilitiesViewModel::class) - fun homeServerCapabilitiesViewModelFactory(factory: HomeServerCapabilitiesViewModel.Factory): MavericksAssistedViewModelFactory<*, *> - @Binds @IntoMap @MavericksViewModelKey(InviteUsersToRoomViewModel::class) diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt index acaf24dca7..fbddf815c6 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt @@ -93,6 +93,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() { title = getString(R.string.fab_menu_create_chat), menuResId = R.menu.vector_create_direct_room, submitMenuItemId = R.id.action_create_direct_room, + single3pidSelection = true, ) ) } diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt index 3f67708a28..dce1e63766 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt @@ -124,7 +124,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor( } val result = runCatchingToAsync { - if (vectorPreferences.isDeferredDmEnabled()) { + if (vectorPreferences.isDeferredDmEnabled() && roomParams.invite3pids.isEmpty()) { session.roomService().createLocalRoom(roomParams) } else { analyticsTracker.capture(CreatedRoom(isDM = roomParams.isDirect.orFalse())) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt index 2bb6fdb3e6..f363128ad5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt @@ -1176,6 +1176,10 @@ class TimelineFragment : views.hideComposerViews() views.notificationAreaView.render(NotificationAreaView.State.Tombstone(mainState.tombstoneEvent)) } + + if (summary.isDirect && summary.isEncrypted && summary.joinedMembersCount == 1 && summary.invitedMembersCount == 0) { + views.hideComposerViews() + } } else if (summary?.membership == Membership.INVITE && inviter != null) { views.hideComposerViews() lazyLoadedViews.inviteView(true)?.apply { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt index 69b4f6e039..9d757a5f2e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptionItemFactory.kt @@ -56,22 +56,37 @@ class EncryptionItemFactory @Inject constructor( val description: String val shield: StatusTileTimelineItem.ShieldUIState if (isSafeAlgorithm) { - val isDirect = session.getRoomSummary(event.root.roomId.orEmpty())?.isDirect.orFalse() - title = stringProvider.getString(R.string.encryption_enabled) - description = stringProvider.getString( + val roomSummary = session.getRoomSummary(event.root.roomId.orEmpty()) + val isDirect = roomSummary?.isDirect.orFalse() + val (resTitle, resDescription, resShield) = when { + isDirect -> { + val isWaitingUser = roomSummary?.isEncrypted.orFalse() && roomSummary?.joinedMembersCount == 1 && roomSummary.invitedMembersCount == 0 when { - isDirect && RoomLocalEcho.isLocalEchoId(event.root.roomId.orEmpty()) -> { - R.string.direct_room_encryption_enabled_tile_description_future - } - isDirect -> { - R.string.direct_room_encryption_enabled_tile_description - } - else -> { - R.string.encryption_enabled_tile_description - } + RoomLocalEcho.isLocalEchoId(event.root.roomId.orEmpty()) -> Triple( + R.string.encryption_enabled, + R.string.direct_room_encryption_enabled_tile_description_future, + StatusTileTimelineItem.ShieldUIState.BLACK + ) + isWaitingUser -> Triple( + R.string.direct_room_encryption_enabled_waiting_users, + R.string.direct_room_encryption_enabled_waiting_users_tile_description, + StatusTileTimelineItem.ShieldUIState.WAITING + ) + else -> Triple( + R.string.encryption_enabled, + R.string.direct_room_encryption_enabled_tile_description, + StatusTileTimelineItem.ShieldUIState.BLACK + ) } - ) - shield = StatusTileTimelineItem.ShieldUIState.BLACK + } + else -> { + Triple(R.string.encryption_enabled, R.string.encryption_enabled_tile_description, StatusTileTimelineItem.ShieldUIState.BLACK) + } + } + + title = stringProvider.getString(resTitle) + description = stringProvider.getString(resDescription) + shield = resShield } else { title = stringProvider.getString(R.string.encryption_misconfigured) description = stringProvider.getString(R.string.encryption_unknown_algorithm_tile_description) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt index a5e2b0d064..d8a9170402 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt @@ -40,6 +40,7 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.tools.linkify import im.vector.app.features.themes.ThemeUtils import me.gujun.android.span.span +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.localecho.RoomLocalEcho import org.matrix.android.sdk.api.util.toMatrixItem @@ -127,26 +128,38 @@ abstract class MergedRoomCreationItem : BasedMergedItem { - if (attributes.isLocalRoom) { - resources.getString(R.string.direct_room_encryption_enabled_tile_description_future) - } else { - resources.getString(R.string.direct_room_encryption_enabled_tile_description) + val isWaitingUser = roomSummary?.isEncrypted.orFalse() && roomSummary?.joinedMembersCount == 1 && roomSummary?.invitedMembersCount == 0 + when { + attributes.isLocalRoom -> Triple( + R.string.encryption_enabled, + R.string.direct_room_encryption_enabled_tile_description_future, + R.drawable.ic_shield_black + ) + isWaitingUser -> Triple( + R.string.direct_room_encryption_enabled_waiting_users, + R.string.direct_room_encryption_enabled_waiting_users_tile_description, + R.drawable.ic_room_profile_member_list + ) + else -> Triple( + R.string.encryption_enabled, + R.string.direct_room_encryption_enabled_tile_description, + R.drawable.ic_shield_black + ) } } else -> { - resources.getString(R.string.encryption_enabled_tile_description) + Triple(R.string.encryption_enabled, R.string.encryption_enabled_tile_description, R.drawable.ic_shield_black) } } - holder.e2eTitleTextView.text = holder.expandView.resources.getString(R.string.encryption_enabled) + holder.e2eTitleTextView.text = holder.expandView.resources.getString(title) holder.e2eTitleTextView.setCompoundDrawablesWithIntrinsicBounds( - ContextCompat.getDrawable(holder.view.context, R.drawable.ic_shield_black), + ContextCompat.getDrawable(holder.view.context, drawable), null, null, null ) - holder.e2eTitleDescriptionView.text = description + holder.e2eTitleDescriptionView.text = holder.expandView.resources.getString(description) holder.e2eTitleDescriptionView.textAlignment = View.TEXT_ALIGNMENT_CENTER } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/StatusTileTimelineItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/StatusTileTimelineItem.kt index 1e5bb0521d..5a0c024ec8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/StatusTileTimelineItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/StatusTileTimelineItem.kt @@ -57,6 +57,7 @@ abstract class StatusTileTimelineItem : AbsBaseMessageItem R.drawable.ic_shield_trusted ShieldUIState.BLACK -> R.drawable.ic_shield_black ShieldUIState.RED -> R.drawable.ic_shield_warning + ShieldUIState.WAITING -> R.drawable.ic_room_profile_member_list ShieldUIState.ERROR -> R.drawable.ic_warning_badge } @@ -101,6 +102,7 @@ abstract class StatusTileTimelineItem : AbsBaseMessageItem(initialState) { - - @AssistedFactory - interface Factory : MavericksAssistedViewModelFactory { - override fun create(initialState: HomeServerCapabilitiesViewState): HomeServerCapabilitiesViewModel - } - - companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() { - - override fun initialState(viewModelContext: ViewModelContext): HomeServerCapabilitiesViewState { - val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getSafeActiveSession() - return HomeServerCapabilitiesViewState( - capabilities = session?.homeServerCapabilitiesService()?.getHomeServerCapabilities() ?: HomeServerCapabilities() - ) - } - } - - init { - - initAdminE2eByDefault() - } - - private fun initAdminE2eByDefault() { - viewModelScope.launch(Dispatchers.IO) { - val adminE2EByDefault = tryOrNull { - rawService.getElementWellknown(session.sessionParams) - ?.isE2EByDefault() - ?: true - } ?: true - - setState { - copy( - isE2EByDefault = adminE2EByDefault - ) - } - } - } - - override fun handle(action: EmptyAction) {} -} diff --git a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewState.kt b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewState.kt deleted file mode 100644 index d7ced5e632..0000000000 --- a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewState.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2020 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.homeserver - -import com.airbnb.mvrx.MavericksState -import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities - -data class HomeServerCapabilitiesViewState( - val capabilities: HomeServerCapabilities = HomeServerCapabilities(), - val isE2EByDefault: Boolean = true -) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListController.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListController.kt index 2131eda60e..54e43038b1 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListController.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListController.kt @@ -25,6 +25,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.noResultItem +import im.vector.app.core.epoxy.profiles.notifications.textHeaderItem import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider @@ -61,6 +62,13 @@ class UserListController @Inject constructor( val currentState = state ?: return val host = this + if (currentState.isE2EByDefault && currentState.single3pidSelection && currentState.pendingSelections.isNotEmpty()) { + textHeaderItem { + id("userListNotificationHeader") + textRes(R.string.direct_room_user_list_only_invite_one_email) + } + } + // Build generic items if (currentState.searchTerm.isBlank()) { if (currentState.showInviteActions()) { diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index fbb6a8ee14..6447452e5b 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -27,7 +27,6 @@ import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.args -import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.chip.Chip import dagger.hilt.android.AndroidEntryPoint @@ -42,7 +41,6 @@ import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentUserListBinding -import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel import im.vector.app.features.settings.VectorSettingsActivity import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -63,7 +61,6 @@ class UserListFragment : private val args: UserListFragmentArgs by args() private val viewModel: UserListViewModel by activityViewModel() - private val homeServerCapabilitiesViewModel: HomeServerCapabilitiesViewModel by fragmentViewModel() private lateinit var sharedActionViewModel: UserListSharedActionViewModel override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentUserListBinding { @@ -86,7 +83,7 @@ class UserListFragment : setupRecyclerView() setupSearchView() - homeServerCapabilitiesViewModel.onEach { + viewModel.onEach { views.userListE2EbyDefaultDisabled.isVisible = !it.isE2EByDefault } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragmentArgs.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragmentArgs.kt index d6e55c29ae..0a0ae8127a 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragmentArgs.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragmentArgs.kt @@ -26,6 +26,7 @@ data class UserListFragmentArgs( val submitMenuItemId: Int, val excludedUserIds: Set? = null, val singleSelection: Boolean = false, + val single3pidSelection: Boolean = false, val showInviteActions: Boolean = true, val showContactBookAction: Boolean = true, val showToolbar: Boolean = true diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index 5a764f11fd..ae28ff020d 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -31,6 +31,9 @@ import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.discovery.fetchIdentityServerWithTerms +import im.vector.app.features.raw.wellknown.getElementWellknown +import im.vector.app.features.raw.wellknown.isE2EByDefault +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.filter @@ -41,6 +44,7 @@ import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.IdentityServiceListener @@ -57,6 +61,7 @@ data class ThreePidUser( class UserListViewModel @AssistedInject constructor( @Assisted initialState: UserListViewState, private val stringProvider: StringProvider, + private val rawService: RawService, private val session: Session ) : VectorViewModel(initialState) { @@ -84,6 +89,7 @@ class UserListViewModel @AssistedInject constructor( } init { + initAdminE2eByDefault() observeUsers() setState { copy( @@ -93,6 +99,22 @@ class UserListViewModel @AssistedInject constructor( session.identityService().addListener(identityServerListener) } + private fun initAdminE2eByDefault() { + viewModelScope.launch(Dispatchers.IO) { + val adminE2EByDefault = tryOrNull { + rawService.getElementWellknown(session.sessionParams) + ?.isE2EByDefault() + ?: true + } ?: true + + setState { + copy( + isE2EByDefault = adminE2EByDefault + ) + } + } + } + private fun cleanISURL(url: String?): String? { return url?.removePrefix("https://") } @@ -258,8 +280,13 @@ class UserListViewModel @AssistedInject constructor( } private fun handleSelectUser(action: UserListAction.AddPendingSelection) = withState { state -> - val selections = state.pendingSelections.toggle(action.pendingSelection, singleElement = state.singleSelection) - setState { copy(pendingSelections = selections) } + val canSelectUser = !state.isE2EByDefault || state.pendingSelections.isEmpty() || !state.single3pidSelection || + (action.pendingSelection is PendingSelection.UserPendingSelection && + state.pendingSelections.last() is PendingSelection.UserPendingSelection) + if (canSelectUser) { + val selections = state.pendingSelections.toggle(action.pendingSelection, singleElement = state.singleSelection) + setState { copy(pendingSelections = selections) } + } } private fun handleRemoveSelectedUser(action: UserListAction.RemovePendingSelection) = withState { state -> diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt index f91e273aeb..ec932a2a57 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt @@ -32,6 +32,8 @@ data class UserListViewState( val pendingSelections: Set = emptySet(), val searchTerm: String = "", val singleSelection: Boolean, + val single3pidSelection: Boolean, + val isE2EByDefault: Boolean = false, val configuredIdentityServer: String? = null, private val showInviteActions: Boolean, val showContactBookAction: Boolean @@ -40,6 +42,7 @@ data class UserListViewState( constructor(args: UserListFragmentArgs) : this( excludedUserIds = args.excludedUserIds, singleSelection = args.singleSelection, + single3pidSelection = args.single3pidSelection, showInviteActions = args.showInviteActions, showContactBookAction = args.showContactBookAction )