Convert to ViewEvents -> CrossSigningSettingsViewModel

This commit is contained in:
Benoit Marty 2020-02-07 18:47:54 +01:00
parent a930313bf3
commit 70973c3302
3 changed files with 74 additions and 54 deletions

View file

@ -18,16 +18,13 @@ package im.vector.riotx.features.settings.crosssigning
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.riotx.R
import im.vector.riotx.core.dialogs.PromptPasswordDialog
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.exhaustive
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
@ -44,23 +41,20 @@ class CrossSigningSettingsFragment @Inject constructor(
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel.requestLiveData.observeEvent(this) {
viewModel.observeViewEvents {
when (it) {
is Fail -> {
is CrossSigningSettingsViewEvents.Failure -> {
AlertDialog.Builder(requireContext())
.setTitle(R.string.dialog_title_error)
.setMessage(it.error.message)
.setMessage(errorFormatter.toHumanReadable(it.throwable))
.setPositiveButton(R.string.ok, null)
.show()
Unit
}
is Success -> {
when (val action = it.invoke()) {
is CrossSigningAction.RequestPasswordAuth -> {
requestPassword(action.sessionId)
}
}
is CrossSigningSettingsViewEvents.RequestPassword -> {
requestPassword()
}
}
}.exhaustive
}
}
@ -89,18 +83,14 @@ class CrossSigningSettingsFragment @Inject constructor(
super.onDestroyView()
}
private fun requestPassword(sessionId: String) {
private fun requestPassword() {
PromptPasswordDialog().show(requireActivity()) { password ->
// TODO sessionId should never get out the ViewModel
viewModel.handle(CrossSigningAction.InitializeCrossSigning(UserPasswordAuth(
session = sessionId,
password = password
)))
viewModel.handle(CrossSigningAction.PasswordEntered(password))
}
}
override fun onInitializeCrossSigningKeys() {
viewModel.handle(CrossSigningAction.InitializeCrossSigning())
viewModel.handle(CrossSigningAction.InitializeCrossSigning)
}
override fun onResetCrossSigningKeys() {
@ -108,7 +98,7 @@ class CrossSigningSettingsFragment @Inject constructor(
.setTitle(R.string.dialog_title_confirmation)
.setMessage(R.string.are_you_sure)
.setPositiveButton(R.string.ok) { _, _ ->
viewModel.handle(CrossSigningAction.InitializeCrossSigning())
viewModel.handle(CrossSigningAction.InitializeCrossSigning)
}
.show()
}

View file

@ -0,0 +1,29 @@
/*
* 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.riotx.features.settings.crosssigning
import im.vector.riotx.core.platform.VectorViewEvents
/**
* Transient events for cross signing settings screen
*/
sealed class CrossSigningSettingsViewEvents : VectorViewEvents {
// data class Loading(val message: CharSequence? = null) : RoomDirectoryViewEvents()
data class Failure(val throwable: Throwable) : CrossSigningSettingsViewEvents()
object RequestPassword : CrossSigningSettingsViewEvents()
}

View file

@ -15,14 +15,9 @@
*/
package im.vector.riotx.features.settings.crosssigning
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.MvRxState
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
@ -36,11 +31,9 @@ import im.vector.matrix.android.internal.crypto.crosssigning.isVerified
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.platform.VectorViewModelAction
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.utils.LiveEvent
data class CrossSigningSettingsViewState(
val crossSigningInfo: MXCrossSigningInfo? = null,
@ -51,19 +44,13 @@ data class CrossSigningSettingsViewState(
) : MvRxState
sealed class CrossSigningAction : VectorViewModelAction {
data class InitializeCrossSigning(val auth: UserPasswordAuth? = null) : CrossSigningAction()
data class RequestPasswordAuth(val sessionId: String) : CrossSigningAction()
object InitializeCrossSigning : CrossSigningAction()
data class PasswordEntered(val password: String) : CrossSigningAction()
}
class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted private val initialState: CrossSigningSettingsViewState,
private val stringProvider: StringProvider,
private val session: Session)
: VectorViewModel<CrossSigningSettingsViewState, CrossSigningAction, EmptyViewEvents>(initialState) {
// Can be used for several actions, for a one shot result
private val _requestLiveData = MutableLiveData<LiveEvent<Async<CrossSigningAction>>>()
val requestLiveData: LiveData<LiveEvent<Async<CrossSigningAction>>>
get() = _requestLiveData
: VectorViewModel<CrossSigningSettingsViewState, CrossSigningAction, CrossSigningSettingsViewEvents>(initialState) {
init {
session.rx().liveCrossSigningInfo(session.myUserId)
@ -81,6 +68,9 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
}
}
// Storage when password is required
private var _pendingSession: String? = null
@AssistedInject.Factory
interface Factory {
fun create(initialState: CrossSigningSettingsViewState): CrossSigningSettingsViewModel
@ -89,26 +79,37 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
override fun handle(action: CrossSigningAction) {
when (action) {
is CrossSigningAction.InitializeCrossSigning -> {
initializeCrossSigning(action.auth?.copy(user = session.myUserId))
initializeCrossSigning(null)
}
}
is CrossSigningAction.PasswordEntered -> {
initializeCrossSigning(UserPasswordAuth(
session = _pendingSession,
user = session.myUserId,
password = action.password
))
}
}.exhaustive
}
private fun initializeCrossSigning(auth: UserPasswordAuth?) {
_pendingSession = null
setState {
copy(isUploadingKeys = true)
}
session.getCrossSigningService().initializeCrossSigning(auth, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
_pendingSession = null
setState {
copy(isUploadingKeys = false)
}
}
override fun onFailure(failure: Throwable) {
if (failure is Failure.OtherServerError
&& failure.httpCode == 401
) {
_pendingSession = null
if (failure is Failure.OtherServerError && failure.httpCode == 401) {
try {
MoshiProvider.providesMoshi()
.adapter(RegistrationFlowResponse::class.java)
@ -118,23 +119,23 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
}?.let { flowResponse ->
// Retry with authentication
if (flowResponse.flows?.any { it.stages?.contains(LoginFlowTypes.PASSWORD) == true } == true) {
_requestLiveData.postValue(LiveEvent(Success(CrossSigningAction.RequestPasswordAuth(flowResponse.session ?: ""))))
_pendingSession = flowResponse.session ?: ""
_viewEvents.post(CrossSigningSettingsViewEvents.RequestPassword)
return
} else {
_requestLiveData.postValue(LiveEvent(Fail(Throwable("You cannot do that from mobile"))))
// can't do this from here
_viewEvents.post(CrossSigningSettingsViewEvents.Failure(Throwable("You cannot do that from mobile")))
setState {
copy(isUploadingKeys = false)
}
return
}
}
}
when (failure) {
is Failure.ServerError -> {
_requestLiveData.postValue(LiveEvent(Fail(Throwable(failure.error.message))))
}
else -> {
_requestLiveData.postValue(LiveEvent(Fail(failure)))
}
}
_viewEvents.post(CrossSigningSettingsViewEvents.Failure(failure))
setState {
copy(isUploadingKeys = false)
}