diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt index 7f14a5b378..a65eba54f7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationService.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.api.session.crypto.sas import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.events.model.LocalEcho import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest /** @@ -51,7 +52,7 @@ interface VerificationService { /** * Request a key verification from another user using toDevice events. */ - fun requestKeyVerificationInDMs(methods: List, userId: String, roomId: String): PendingVerificationRequest + fun requestKeyVerificationInDMs(methods: List, userId: String, roomId: String, localId: String? = LocalEcho.createLocalEchoId()): PendingVerificationRequest fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt index a25f158cb3..4c487b5ed2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt @@ -715,7 +715,7 @@ internal class DefaultVerificationService @Inject constructor( } } - override fun requestKeyVerificationInDMs(methods: List, userId: String, roomId: String) + override fun requestKeyVerificationInDMs(methods: List, userId: String, roomId: String, localId: String?) : PendingVerificationRequest { Timber.i("## SAS Requesting verification to user: $userId in room $roomId") @@ -737,7 +737,7 @@ internal class DefaultVerificationService @Inject constructor( } } - val localID = LocalEcho.createLocalEchoId() + val localID = localId ?: LocalEcho.createLocalEchoId() val verificationRequest = PendingVerificationRequest( ageLocalTs = System.currentTimeMillis(), diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index dc93327ca4..576ef9a464 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -22,6 +22,7 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.RoomSummaryQueryParams +import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.VersioningState import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams @@ -36,6 +37,7 @@ import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.query.process import im.vector.matrix.android.internal.session.room.alias.GetRoomIdByAliasTask import im.vector.matrix.android.internal.session.room.create.CreateRoomTask +import im.vector.matrix.android.internal.session.room.membership.RoomMemberHelper import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask import im.vector.matrix.android.internal.session.room.read.MarkAllRoomsReadTask import im.vector.matrix.android.internal.session.user.accountdata.UpdateBreadcrumbsTask @@ -76,15 +78,21 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona override fun getExistingDirectRoomWithUser(otherUserId: String): Room? { Realm.getInstance(monarchy.realmConfiguration).use { realm -> - val roomId = RoomSummaryEntity.where(realm) + val candidates = RoomSummaryEntity.where(realm) .equalTo(RoomSummaryEntityFields.IS_DIRECT, true) - .findAll()?.let { dms -> - dms.firstOrNull { - it.otherMemberIds.contains(otherUserId) - } + .findAll()?.filter { dm -> + dm.otherMemberIds.contains(otherUserId) + && dm.membership == Membership.JOIN + }?.map { + it.roomId } - ?.roomId ?: return null - return RoomEntity.where(realm, roomId).findFirst()?.let { roomFactory.create(roomId) } + ?: return null + candidates.forEach { roomId -> + if (RoomMemberHelper(realm, roomId).getActiveRoomMemberIds().any { it == otherUserId }) { + return RoomEntity.where(realm, roomId).findFirst()?.let { roomFactory.create(roomId) } + } + } + return null } } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt index 8b0989fbd8..4e91a93da3 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt @@ -40,7 +40,6 @@ import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMet import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment import im.vector.riotx.features.crypto.verification.emoji.VerificationEmojiCodeFragment import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment -import im.vector.riotx.features.crypto.verification.request.VerificationRequestViewModel import im.vector.riotx.features.home.AvatarRenderer import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.bottom_sheet_verification.* @@ -57,8 +56,6 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { val roomId: String? = null ) : Parcelable - @Inject - lateinit var verificationRequestViewModelFactory: VerificationRequestViewModel.Factory @Inject lateinit var verificationViewModelFactory: VerificationBottomSheetViewModel.Factory @Inject @@ -133,7 +130,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { it.otherUserMxItem?.id ?: "", // If it was outgoing it.transaction id would be null, but the pending request // would be updated (from localID to txId) - it.pendingRequest?.transactionId ?: it.transactionId)) + it.pendingRequest.invoke()?.transactionId ?: it.transactionId)) }) } VerificationTxState.Verified, @@ -153,36 +150,36 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { // At this point there is no transaction for this request // Transaction has not yet started - if (it.pendingRequest?.cancelConclusion != null) { + if (it.pendingRequest.invoke()?.cancelConclusion != null) { // The request has been declined, we should dismiss dismiss() } // If it's an outgoing - if (it.pendingRequest == null || !it.pendingRequest.isIncoming) { + if (it.pendingRequest.invoke() == null || it.pendingRequest.invoke()?.isIncoming == false) { Timber.v("## SAS show bottom sheet for outgoing request") - if (it.pendingRequest?.isReady == true) { + if (it.pendingRequest.invoke()?.isReady == true) { Timber.v("## SAS show bottom sheet for outgoing and ready request") // Show choose method fragment with waiting showFragment(VerificationChooseMethodFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationArgs(it.otherUserMxItem?.id - ?: "", it.pendingRequest.transactionId)) + ?: "", it.pendingRequest.invoke()?.transactionId)) }) } else { // Stay on the start fragment showFragment(VerificationRequestFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationArgs( it.otherUserMxItem?.id ?: "", - it.pendingRequest?.transactionId, + it.pendingRequest.invoke()?.transactionId, it.roomId)) }) } - } else if (it.pendingRequest.isIncoming) { + } else if (it.pendingRequest.invoke()?.isIncoming == true) { Timber.v("## SAS show bottom sheet for Incoming request") // For incoming we can switch to choose method because ready is being sent or already sent showFragment(VerificationChooseMethodFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationArgs(it.otherUserMxItem?.id - ?: "", it.pendingRequest.transactionId)) + ?: "", it.pendingRequest.invoke()?.transactionId)) }) } super.invalidate() @@ -209,7 +206,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { fun withArgs(roomId: String?, otherUserId: String, transactionId: String? = null): VerificationBottomSheet { return VerificationBottomSheet().apply { arguments = Bundle().apply { - putParcelable(MvRx.KEY_ARG, VerificationBottomSheet.VerificationArgs( + putParcelable(MvRx.KEY_ARG, VerificationArgs( otherUserId = otherUserId, roomId = roomId, verificationId = transactionId diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt index cd64ec93f0..1c5a8946de 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -18,21 +18,27 @@ package im.vector.riotx.features.crypto.verification import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData 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 com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.crypto.sas.CancelCode import im.vector.matrix.android.api.session.crypto.sas.QRVerificationTransaction -import im.vector.matrix.android.api.session.crypto.sas.VerificationService import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod +import im.vector.matrix.android.api.session.crypto.sas.VerificationService import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction import im.vector.matrix.android.api.session.crypto.sas.VerificationTxState +import im.vector.matrix.android.api.session.events.model.LocalEcho +import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.toMatrixItem import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest @@ -43,7 +49,8 @@ import im.vector.riotx.core.utils.LiveEvent data class VerificationBottomSheetViewState( val otherUserMxItem: MatrixItem? = null, val roomId: String? = null, - val pendingRequest: PendingVerificationRequest? = null, + val pendingRequest: Async = Uninitialized, + val pendingLocalId: String? = null, val transactionState: VerificationTxState? = null, val transactionId: String? = null, val cancelCode: CancelCode? = null @@ -93,7 +100,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini otherUserMxItem = userItem?.toMatrixItem(), transactionState = sasTx?.state, transactionId = args.verificationId, - pendingRequest = pr, + pendingRequest = if (pr != null) Success(pr) else Uninitialized, roomId = args.roomId) ) } @@ -106,9 +113,40 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini when (action) { is VerificationAction.RequestVerificationByDM -> { - if (roomId == null) return@withState - setState { - copy(pendingRequest = session.getSasVerificationService().requestKeyVerificationInDMs(supportedVerificationMethods, otherUserId, roomId)) + if (roomId == null) { + val localID = LocalEcho.createLocalEchoId() + setState { + copy( + pendingLocalId = localID, + pendingRequest = Loading() + ) + } + val roomParams = CreateRoomParams().apply { + invitedUserIds = listOf(otherUserId).toMutableList() + setDirectMessage() + } + session.createRoom(roomParams, object : MatrixCallback { + override fun onSuccess(data: String) { + setState { + copy( + roomId = data, + pendingRequest = Success( + session.getSasVerificationService().requestKeyVerificationInDMs(supportedVerificationMethods, otherUserId, data, pendingLocalId) + ) + ) + } + } + + override fun onFailure(failure: Throwable) { + setState { + copy(pendingRequest = Fail(failure)) + } + } + }) + } else { + setState { + copy(pendingRequest = Success(session.getSasVerificationService().requestKeyVerificationInDMs(supportedVerificationMethods, otherUserId, roomId))) + } } } is VerificationAction.StartSASVerification -> { @@ -154,7 +192,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini } override fun transactionUpdated(tx: VerificationTransaction) = withState { state -> - if (tx.transactionId == (state.pendingRequest?.transactionId ?: state.transactionId)) { + if (tx.transactionId == (state.pendingRequest.invoke()?.transactionId ?: state.transactionId)) { // A SAS tx has been started following this request setState { copy( @@ -171,9 +209,11 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state -> - if (pr.localID == state.pendingRequest?.localID || state.pendingRequest?.transactionId == pr.transactionId) { + if (pr.localID == state.pendingLocalId + || pr.localID == state.pendingRequest.invoke()?.localID + || state.pendingRequest.invoke()?.transactionId == pr.transactionId) { setState { - copy(pendingRequest = pr) + copy(pendingRequest = Success(pr)) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodFragment.kt index 110047b49c..b9f543808c 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodFragment.kt @@ -72,7 +72,7 @@ class VerificationChooseMethodFragment @Inject constructor( override fun doVerifyBySas() = withState(sharedViewModel) { sharedViewModel.handle(VerificationAction.StartSASVerification( it.otherUserMxItem?.id ?: "", - it.pendingRequest?.transactionId ?: "")) + it.pendingRequest.invoke()?.transactionId ?: "")) } override fun openCamera() { @@ -115,7 +115,7 @@ class VerificationChooseMethodFragment @Inject constructor( private fun onRemoteQrCodeScanned(remoteQrCode: String) = withState(sharedViewModel) { sharedViewModel.handle(VerificationAction.RemoteQrCodeScanned( it.otherUserMxItem?.id ?: "", - it.pendingRequest?.transactionId ?: "", + it.pendingRequest.invoke()?.transactionId ?: "", remoteQrCode )) } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestController.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestController.kt index e14b6573f4..3bc960e676 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestController.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestController.kt @@ -19,11 +19,14 @@ package im.vector.riotx.features.crypto.verification.request import androidx.core.text.toSpannable import com.airbnb.epoxy.EpoxyController import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import im.vector.riotx.R import im.vector.riotx.core.epoxy.dividerItem import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.utils.colorizeMatchingText +import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewState import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem @@ -36,17 +39,18 @@ class VerificationRequestController @Inject constructor( var listener: Listener? = null - private var viewState: VerificationRequestViewState? = null + private var viewState: VerificationBottomSheetViewState? = null - fun update(viewState: VerificationRequestViewState) { + fun update(viewState: VerificationBottomSheetViewState) { this.viewState = viewState requestModelBuild() } override fun buildModels() { val state = viewState ?: return + val matrixItem = viewState?.otherUserMxItem ?: return - val styledText = state.matrixItem.let { + val styledText = matrixItem.let { stringProvider.getString(R.string.verification_request_notice, it.id) .toSpannable() .colorizeMatchingText(it.id, colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color)) @@ -61,14 +65,8 @@ class VerificationRequestController @Inject constructor( id("sep") } - when (state.started) { - is Loading -> { - bottomSheetVerificationWaitingItem { - id("waiting") - title(stringProvider.getString(R.string.verification_request_waiting_for, state.matrixItem.getBestName())) - } - } - else -> { + when (val pr = state.pendingRequest) { + is Uninitialized -> { bottomSheetVerificationActionItem { id("start") title(stringProvider.getString(R.string.start_verification)) @@ -79,6 +77,20 @@ class VerificationRequestController @Inject constructor( listener { listener?.onClickOnVerificationStart() } } } + is Loading -> { + bottomSheetVerificationWaitingItem { + id("waiting") + title(stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName())) + } + } + is Success -> { + if (!pr.invoke().isReady) { + bottomSheetVerificationWaitingItem { + id("waiting") + title(stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName())) + } + } + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestFragment.kt index 8250bd74b8..338d1e2864 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestFragment.kt @@ -30,22 +30,19 @@ import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.* import javax.inject.Inject class VerificationRequestFragment @Inject constructor( - val verificationRequestViewModelFactory: VerificationRequestViewModel.Factory, val controller: VerificationRequestController ) : VectorBaseFragment(), VerificationRequestController.Listener { - private val viewModel by fragmentViewModel(VerificationRequestViewModel::class) - - private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) + private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setupRecyclerView() } + override fun onDestroyView() { bottomSheetVerificationRecyclerView.cleanup() controller.listener = null @@ -61,7 +58,9 @@ class VerificationRequestFragment @Inject constructor( controller.update(state) } - override fun onClickOnVerificationStart() = withState(viewModel) { state -> - sharedViewModel.handle(VerificationAction.RequestVerificationByDM(state.matrixItem.id, state.roomId)) + override fun onClickOnVerificationStart(): Unit = withState(viewModel) { state -> + state.otherUserMxItem?.id?.let { otherUserId -> + viewModel.handle(VerificationAction.RequestVerificationByDM(otherUserId, state.roomId)) + } } } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestViewModel.kt deleted file mode 100644 index 14388b4403..0000000000 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/request/VerificationRequestViewModel.kt +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2019 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.riotx.features.crypto.verification.request - -import com.airbnb.mvrx.* -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject -import im.vector.matrix.android.api.session.Session -import im.vector.matrix.android.api.session.crypto.sas.VerificationService -import im.vector.matrix.android.api.session.crypto.sas.VerificationTransaction -import im.vector.matrix.android.api.util.MatrixItem -import im.vector.matrix.android.api.util.toMatrixItem -import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest -import im.vector.riotx.core.di.HasScreenInjector -import im.vector.riotx.core.platform.EmptyAction -import im.vector.riotx.core.platform.VectorViewModel -import im.vector.riotx.features.crypto.verification.VerificationBottomSheet - -data class VerificationRequestViewState( - val roomId: String? = null, - val matrixItem: MatrixItem, - val started: Async = Success(false) -) : MvRxState - -class VerificationRequestViewModel @AssistedInject constructor( - @Assisted initialState: VerificationRequestViewState, - private val session: Session -) : VectorViewModel(initialState), VerificationService.VerificationListener { - - @AssistedInject.Factory - interface Factory { - fun create(initialState: VerificationRequestViewState): VerificationRequestViewModel - } - - init { - session.getSasVerificationService().addListener(this) - } - - override fun onCleared() { - session.getSasVerificationService().removeListener(this) - super.onCleared() - } - - companion object : MvRxViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: VerificationRequestViewState): VerificationRequestViewModel? { - val fragment: VerificationRequestFragment = (viewModelContext as FragmentViewModelContext).fragment() - return fragment.verificationRequestViewModelFactory.create(state) - } - - override fun initialState(viewModelContext: ViewModelContext): VerificationRequestViewState? { - val args = viewModelContext.args() - val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() - - val pr = session.getSasVerificationService() - .getExistingVerificationRequest(args.otherUserId, args.verificationId) - return session.getUser(args.otherUserId)?.let { - VerificationRequestViewState( - started = Success(false).takeIf { pr == null } - ?: Success(true).takeIf { pr?.isReady == true } - ?: Loading(), - matrixItem = it.toMatrixItem() - ) - } - } - } - - override fun handle(action: EmptyAction) {} - - override fun transactionCreated(tx: VerificationTransaction) {} - - override fun transactionUpdated(tx: VerificationTransaction) {} - - override fun verificationRequestCreated(pr: PendingVerificationRequest) { - verificationRequestUpdated(pr) - } - - override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state -> - if (pr.otherUserId == state.matrixItem.id) { - if (pr.isReady) { - setState { - copy(started = Success(true)) - } - } else { - setState { - copy(started = Loading()) - } - } - } - } -} diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index 236dd19030..74c30d1029 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -1027,6 +1027,7 @@ class RoomDetailFragment @Inject constructor( } override fun onAvatarClicked(informationData: MessageInformationData) { + //roomDetailViewModel.handle(RoomDetailAction.RequestVerification(informationData.senderId)) openRoomMemberProfile(informationData.senderId) } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt index 8ff209b443..9122b180e8 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileAction.kt @@ -23,4 +23,5 @@ sealed class RoomMemberProfileAction : VectorViewModelAction { object RetryFetchingInfo: RoomMemberProfileAction() object IgnoreUser: RoomMemberProfileAction() + data class VerifyUser(val userId: String? = null, val roomId: String? = null): RoomMemberProfileAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt index 87cf1d730a..e98fdcf0cf 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileController.kt @@ -37,7 +37,9 @@ class RoomMemberProfileController @Inject constructor( interface Callback { fun onIgnoreClicked() - fun onLearnMoreClicked() + fun onTapVerify() + fun onShowDeviceList() + fun onShowDeviceListNoCrossSigning() fun onJumpToReadReceiptClicked() fun onMentionClicked() } @@ -90,7 +92,7 @@ class RoomMemberProfileController @Inject constructor( editable = true, icon = icon, divider = false, - action = { callback?.onLearnMoreClicked() } + action = { callback?.onShowDeviceList() } ) } else { //Not trusted, propose to verify @@ -102,7 +104,7 @@ class RoomMemberProfileController @Inject constructor( editable = true, icon = R.drawable.ic_shield_black, divider = false, - action = { callback?.onLearnMoreClicked() } + action = { callback?.onTapVerify() } ) } @@ -120,7 +122,7 @@ class RoomMemberProfileController @Inject constructor( editable = false, divider = false, subtitle = stringProvider.getString(R.string.room_profile_encrypted_subtitle), - action = { callback?.onLearnMoreClicked() } + action = { callback?.onShowDeviceListNoCrossSigning() } ) } } else { diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index 23db6b53a8..e932a54a18 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -20,16 +20,23 @@ package im.vector.riotx.features.roommemberprofile import android.os.Bundle import android.os.Parcelable import android.view.View -import com.airbnb.mvrx.* +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Incomplete +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.args +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState import im.vector.matrix.android.api.util.MatrixItem import im.vector.riotx.R import im.vector.riotx.core.animations.AppBarStateChangeListener import im.vector.riotx.core.animations.MatrixItemAppBarStateChangeListener import im.vector.riotx.core.extensions.cleanup import im.vector.riotx.core.extensions.configureWith +import im.vector.riotx.core.extensions.observeEvent import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.StateView import im.vector.riotx.core.platform.VectorBaseFragment +import im.vector.riotx.features.crypto.verification.VerificationBottomSheet import im.vector.riotx.features.home.AvatarRenderer import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_matrix_profile.* @@ -83,6 +90,21 @@ class RoomMemberProfileFragment @Inject constructor( } } .disposeOnDestroyView() + + viewModel.actionResultLiveData.observeEvent(this) { async -> + when (async) { + is Success -> { + when (val action = async.invoke()) { + is RoomMemberProfileAction.VerifyUser -> { + VerificationBottomSheet + .withArgs(roomId = null, otherUserId = action.userId!!) + .show(parentFragmentManager, "VERIF") + } + } + } + } + + } } override fun onDestroyView() { @@ -126,8 +148,39 @@ class RoomMemberProfileFragment @Inject constructor( viewModel.handle(RoomMemberProfileAction.IgnoreUser) } - override fun onLearnMoreClicked() { - vectorBaseActivity.notImplemented("Learn more") + override fun onTapVerify() { + viewModel.handle(RoomMemberProfileAction.VerifyUser()) +// if (state.isRoomEncrypted) { +// if( !state.isMine && state.userMXCrossSigningInfo?.isTrusted == false) { +// // we want to verify +// // TODO do not use current room, find or create DM +// VerificationBottomSheet.withArgs( +// state.roomId, +// state.userId +// ).show(parentFragmentManager, "REQ") +// } +// } + } + +// override fun onTapVerify() = withState(viewModel) { state -> +// if (state.isRoomEncrypted) { +// if( !state.isMine && state.userMXCrossSigningInfo?.isTrusted == false) { +// // we want to verify +// // TODO do not use current room, find or create DM +// VerificationBottomSheet.withArgs( +// state.roomId, +// state.userId +// ).show(parentFragmentManager, "REQ") +// } +// } +// } + + override fun onShowDeviceList() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun onShowDeviceListNoCrossSigning() { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun onJumpToReadReceiptClicked() { diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index 29814491a3..745b91bbe5 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -17,9 +17,13 @@ package im.vector.riotx.features.roommemberprofile +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject @@ -46,6 +50,7 @@ import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.utils.DataSource +import im.vector.riotx.core.utils.LiveEvent import im.vector.riotx.core.utils.PublishDataSource import io.reactivex.Observable import io.reactivex.functions.BiFunction @@ -86,6 +91,10 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v private val _viewEvents = PublishDataSource() val viewEvents: DataSource = _viewEvents + private val _actionResultLiveData = MutableLiveData>>() + val actionResultLiveData: LiveData>> + get() = _actionResultLiveData + private val room = if (initialState.roomId != null) { session.getRoom(initialState.roomId) } else { @@ -112,7 +121,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v session.rx().liveUserCryptoDevices(initialState.userId) .map { - it.fold(true, { prev, dev -> prev && dev.isVerified}) + it.fold(true, { prev, dev -> prev && dev.isVerified }) } .execute { copy(allDevicesAreTrusted = it) @@ -134,11 +143,25 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v override fun handle(action: RoomMemberProfileAction) { when (action) { - RoomMemberProfileAction.RetryFetchingInfo -> fetchProfileInfo() - is RoomMemberProfileAction.IgnoreUser -> handleIgnoreAction() + is RoomMemberProfileAction.RetryFetchingInfo -> fetchProfileInfo() + is RoomMemberProfileAction.IgnoreUser -> handleIgnoreAction() + is RoomMemberProfileAction.VerifyUser -> prepareVerification(action) } } + private fun prepareVerification(action: RoomMemberProfileAction.VerifyUser) = withState { state -> + // Sanity + if (state.isRoomEncrypted) { + if (!state.isMine && state.userMXCrossSigningInfo?.isTrusted == false) { + // ok, let's find or create the DM room + _actionResultLiveData.postValue( + LiveEvent(Success(action.copy(userId = state.userId))) + ) + } + } + + } + private fun observeRoomMemberSummary(room: Room) { val queryParams = roomMemberQueryParams { this.userId = QueryStringValue.Equals(initialState.userId, QueryStringValue.Case.SENSITIVE) @@ -163,7 +186,6 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v .execute { copy(userMatrixItem = it) } - } private fun observeRoomSummaryAndPowerLevels(room: Room) {