Verify from RoomMember Profile

This commit is contained in:
Valere 2020-01-24 19:15:23 +01:00
parent a758efc018
commit d60351bcb7
14 changed files with 198 additions and 165 deletions

View file

@ -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<VerificationMethod>, userId: String, roomId: String): PendingVerificationRequest
fun requestKeyVerificationInDMs(methods: List<VerificationMethod>, userId: String, roomId: String, localId: String? = LocalEcho.createLocalEchoId()): PendingVerificationRequest
fun declineVerificationRequestInDMs(otherUserId: String, otherDeviceId: String, transactionId: String, roomId: String)

View file

@ -715,7 +715,7 @@ internal class DefaultVerificationService @Inject constructor(
}
}
override fun requestKeyVerificationInDMs(methods: List<VerificationMethod>, userId: String, roomId: String)
override fun requestKeyVerificationInDMs(methods: List<VerificationMethod>, 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(),

View file

@ -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
}
}

View file

@ -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

View file

@ -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<PendingVerificationRequest> = 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<String> {
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))
}
}
}

View file

@ -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
))
}

View file

@ -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()))
}
}
}
}
}

View file

@ -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))
}
}
}

View file

@ -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<Boolean> = Success(false)
) : MvRxState
class VerificationRequestViewModel @AssistedInject constructor(
@Assisted initialState: VerificationRequestViewState,
private val session: Session
) : VectorViewModel<VerificationRequestViewState, EmptyAction>(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<VerificationRequestViewModel, VerificationRequestViewState> {
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<VerificationBottomSheet.VerificationArgs>()
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())
}
}
}
}
}

View file

@ -1027,6 +1027,7 @@ class RoomDetailFragment @Inject constructor(
}
override fun onAvatarClicked(informationData: MessageInformationData) {
//roomDetailViewModel.handle(RoomDetailAction.RequestVerification(informationData.senderId))
openRoomMemberProfile(informationData.senderId)
}

View file

@ -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()
}

View file

@ -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 {

View file

@ -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() {

View file

@ -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<RoomMemberProfileViewEvents>()
val viewEvents: DataSource<RoomMemberProfileViewEvents> = _viewEvents
private val _actionResultLiveData = MutableLiveData<LiveEvent<Async<RoomMemberProfileAction>>>()
val actionResultLiveData: LiveData<LiveEvent<Async<RoomMemberProfileAction>>>
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) {