Remove BootstrapStep.SetupPassphrase and BootstrapStep.ConfirmPassphrase

This commit is contained in:
Benoit Marty 2020-06-09 22:48:01 +02:00 committed by Valere
parent aa2b62976e
commit 8df7797f6d
9 changed files with 47 additions and 426 deletions

View File

@ -28,8 +28,6 @@ import im.vector.riotx.features.crypto.quads.SharedSecuredStorageKeyFragment
import im.vector.riotx.features.crypto.quads.SharedSecuredStoragePassphraseFragment import im.vector.riotx.features.crypto.quads.SharedSecuredStoragePassphraseFragment
import im.vector.riotx.features.crypto.recover.BootstrapAccountPasswordFragment import im.vector.riotx.features.crypto.recover.BootstrapAccountPasswordFragment
import im.vector.riotx.features.crypto.recover.BootstrapConclusionFragment import im.vector.riotx.features.crypto.recover.BootstrapConclusionFragment
import im.vector.riotx.features.crypto.recover.BootstrapConfirmPassphraseFragment
import im.vector.riotx.features.crypto.recover.BootstrapEnterPassphraseFragment
import im.vector.riotx.features.crypto.recover.BootstrapMigrateBackupFragment import im.vector.riotx.features.crypto.recover.BootstrapMigrateBackupFragment
import im.vector.riotx.features.crypto.recover.BootstrapSaveRecoveryKeyFragment import im.vector.riotx.features.crypto.recover.BootstrapSaveRecoveryKeyFragment
import im.vector.riotx.features.crypto.recover.BootstrapWaitingFragment import im.vector.riotx.features.crypto.recover.BootstrapWaitingFragment
@ -453,16 +451,6 @@ interface FragmentModule {
@FragmentKey(GossipingEventsPaperTrailFragment::class) @FragmentKey(GossipingEventsPaperTrailFragment::class)
fun bindGossipingEventsPaperTrailFragment(fragment: GossipingEventsPaperTrailFragment): Fragment fun bindGossipingEventsPaperTrailFragment(fragment: GossipingEventsPaperTrailFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapEnterPassphraseFragment::class)
fun bindBootstrapEnterPassphraseFragment(fragment: BootstrapEnterPassphraseFragment): Fragment
@Binds
@IntoMap
@FragmentKey(BootstrapConfirmPassphraseFragment::class)
fun bindBootstrapConfirmPassphraseFragment(fragment: BootstrapConfirmPassphraseFragment): Fragment
@Binds @Binds
@IntoMap @IntoMap
@FragmentKey(BootstrapWaitingFragment::class) @FragmentKey(BootstrapWaitingFragment::class)

View File

@ -24,15 +24,11 @@ sealed class BootstrapActions : VectorViewModelAction {
// Navigation // Navigation
object GoBack : BootstrapActions() object GoBack : BootstrapActions()
data class GoToConfirmPassphrase(val passphrase: String) : BootstrapActions()
object GoToCompleted : BootstrapActions() object GoToCompleted : BootstrapActions()
object GoToEnterAccountPassword : BootstrapActions() object GoToEnterAccountPassword : BootstrapActions()
data class DoInitialize(val passphrase: String) : BootstrapActions()
object DoInitializeGeneratedKey : BootstrapActions() object DoInitializeGeneratedKey : BootstrapActions()
object TogglePasswordVisibility : BootstrapActions() object TogglePasswordVisibility : BootstrapActions()
data class UpdateCandidatePassphrase(val pass: String) : BootstrapActions()
data class UpdateConfirmCandidatePassphrase(val pass: String) : BootstrapActions()
data class ReAuth(val pass: String) : BootstrapActions() data class ReAuth(val pass: String) : BootstrapActions()
object RecoveryKeySaved : BootstrapActions() object RecoveryKeySaved : BootstrapActions()
object Completed : BootstrapActions() object Completed : BootstrapActions()

View File

@ -127,16 +127,6 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
bootstrapTitleText.text = getString(R.string.upgrade_security) bootstrapTitleText.text = getString(R.string.upgrade_security)
showFragment(BootstrapWaitingFragment::class, Bundle()) showFragment(BootstrapWaitingFragment::class, Bundle())
} }
is BootstrapStep.SetupPassphrase -> {
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_password))
bootstrapTitleText.text = getString(R.string.set_recovery_passphrase, getString(R.string.recovery_passphrase))
showFragment(BootstrapEnterPassphraseFragment::class, Bundle())
}
is BootstrapStep.ConfirmPassphrase -> {
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_message_password))
bootstrapTitleText.text = getString(R.string.confirm_recovery_passphrase, getString(R.string.recovery_passphrase))
showFragment(BootstrapConfirmPassphraseFragment::class, Bundle())
}
is BootstrapStep.AccountPassword -> { is BootstrapStep.AccountPassword -> {
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_user)) bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_user))
bootstrapTitleText.text = getString(R.string.account_password) bootstrapTitleText.text = getString(R.string.account_password)

View File

@ -1,118 +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.riotx.features.crypto.recover
import android.os.Bundle
import android.view.View
import android.view.inputmethod.EditorInfo
import androidx.core.text.toSpannable
import androidx.core.view.isGone
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.riotx.R
import im.vector.riotx.core.extensions.hideKeyboard
import im.vector.riotx.core.extensions.showPassword
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.utils.colorizeMatchingText
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class BootstrapConfirmPassphraseFragment @Inject constructor(
private val colorProvider: ColorProvider
) : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_enter_passphrase
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ssss_passphrase_security_progress.isGone = true
val recPassPhrase = getString(R.string.recovery_passphrase)
bootstrapDescriptionText.text = getString(R.string.bootstrap_info_confirm_text, recPassPhrase)
.toSpannable()
.colorizeMatchingText(recPassPhrase, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
ssss_passphrase_enter_edittext.hint = getString(R.string.passphrase_confirm_passphrase)
withState(sharedViewModel) {
// set initial value (useful when coming back)
ssss_passphrase_enter_edittext.setText(it.passphraseRepeat ?: "")
ssss_passphrase_enter_edittext.requestFocus()
}
ssss_passphrase_enter_edittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
ssss_passphrase_enter_edittext.textChanges()
.subscribe {
ssss_passphrase_enter_til.error = null
sharedViewModel.handle(BootstrapActions.UpdateConfirmCandidatePassphrase(it?.toString() ?: ""))
}
.disposeOnDestroyView()
sharedViewModel.observeViewEvents {
// when (it) {
// is SharedSecureStorageViewEvent.InlineError -> {
// ssss_passphrase_enter_til.error = it.message
// }
// }
}
ssss_view_show_password.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) }
bootstrapSubmit.debouncedClicks { submit() }
}
private fun submit() = withState(sharedViewModel) { state ->
if (state.step !is BootstrapStep.ConfirmPassphrase) {
return@withState
}
val passphrase = ssss_passphrase_enter_edittext.text?.toString()
when {
passphrase.isNullOrBlank() ->
ssss_passphrase_enter_til.error = getString(R.string.passphrase_empty_error_message)
passphrase != state.passphrase ->
ssss_passphrase_enter_til.error = getString(R.string.passphrase_passphrase_does_not_match)
else -> {
view?.hideKeyboard()
sharedViewModel.handle(BootstrapActions.DoInitialize(passphrase))
}
}
}
override fun invalidate() = withState(sharedViewModel) { state ->
if (state.step is BootstrapStep.ConfirmPassphrase) {
val isPasswordVisible = state.step.isPasswordVisible
ssss_passphrase_enter_edittext.showPassword(isPasswordVisible, updateCursor = false)
ssss_view_show_password.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
}
}
}

View File

@ -68,7 +68,6 @@ interface BootstrapProgressListener {
data class Params( data class Params(
val userPasswordAuth: UserPasswordAuth? = null, val userPasswordAuth: UserPasswordAuth? = null,
val progressListener: BootstrapProgressListener? = null, val progressListener: BootstrapProgressListener? = null,
val passphrase: String?,
val keySpec: SsssKeySpec? = null val keySpec: SsssKeySpec? = null
) )
@ -105,24 +104,13 @@ class BootstrapCrossSigningTask @Inject constructor(
) )
try { try {
keyInfo = awaitCallback { keyInfo = awaitCallback {
params.passphrase?.let { passphrase -> ssssService.generateKey(
ssssService.generateKeyWithPassphrase( UUID.randomUUID().toString(),
UUID.randomUUID().toString(), params.keySpec,
"ssss_key", "ssss_key",
passphrase, EmptyKeySigner(),
EmptyKeySigner(), it
null, )
it
)
} ?: kotlin.run {
ssssService.generateKey(
UUID.randomUUID().toString(),
params.keySpec,
"ssss_key",
EmptyKeySigner(),
it
)
}
} }
} catch (failure: Failure) { } catch (failure: Failure) {
return BootstrapResult.FailedToCreateSSSSKey(failure) return BootstrapResult.FailedToCreateSSSSKey(failure)

View File

@ -1,126 +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.riotx.features.crypto.recover
import android.os.Bundle
import android.view.View
import android.view.inputmethod.EditorInfo
import androidx.core.text.toSpannable
import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState
import com.jakewharton.rxbinding3.widget.editorActionEvents
import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.riotx.R
import im.vector.riotx.core.extensions.showPassword
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.utils.colorizeMatchingText
import im.vector.riotx.features.settings.VectorLocale
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class BootstrapEnterPassphraseFragment @Inject constructor(
private val colorProvider: ColorProvider
) : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_enter_passphrase
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val recPassPhrase = getString(R.string.recovery_passphrase)
bootstrapDescriptionText.text = getString(R.string.bootstrap_info_text, recPassPhrase)
.toSpannable()
.colorizeMatchingText(recPassPhrase, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
ssss_passphrase_enter_edittext.hint = getString(R.string.passphrase_enter_passphrase)
withState(sharedViewModel) {
// set initial value (useful when coming back)
ssss_passphrase_enter_edittext.setText(it.passphrase ?: "")
}
ssss_passphrase_enter_edittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
if (it.actionId == EditorInfo.IME_ACTION_DONE) {
submit()
}
}
.disposeOnDestroyView()
ssss_passphrase_enter_edittext.textChanges()
.subscribe {
// ssss_passphrase_enter_til.error = null
sharedViewModel.handle(BootstrapActions.UpdateCandidatePassphrase(it?.toString() ?: ""))
// ssss_passphrase_submit.isEnabled = it.isNotBlank()
}
.disposeOnDestroyView()
sharedViewModel.observeViewEvents {
// when (it) {
// is SharedSecureStorageViewEvent.InlineError -> {
// ssss_passphrase_enter_til.error = it.message
// }
// }
}
ssss_view_show_password.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) }
bootstrapSubmit.debouncedClicks { submit() }
}
private fun submit() = withState(sharedViewModel) { state ->
if (state.step !is BootstrapStep.SetupPassphrase) {
return@withState
}
val score = state.passphraseStrength.invoke()?.score
val passphrase = ssss_passphrase_enter_edittext.text?.toString()
if (passphrase.isNullOrBlank()) {
ssss_passphrase_enter_til.error = getString(R.string.passphrase_empty_error_message)
} else if (score != 4) {
ssss_passphrase_enter_til.error = getString(R.string.passphrase_passphrase_too_weak)
} else {
sharedViewModel.handle(BootstrapActions.GoToConfirmPassphrase(passphrase))
}
}
override fun invalidate() = withState(sharedViewModel) { state ->
if (state.step is BootstrapStep.SetupPassphrase) {
val isPasswordVisible = state.step.isPasswordVisible
ssss_passphrase_enter_edittext.showPassword(isPasswordVisible, updateCursor = false)
ssss_view_show_password.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
state.passphraseStrength.invoke()?.let { strength ->
val score = strength.score
ssss_passphrase_security_progress.strength = score
if (score in 1..3) {
val hint =
strength.feedback?.getWarning(VectorLocale.applicationLocale)?.takeIf { it.isNotBlank() }
?: strength.feedback?.getSuggestions(VectorLocale.applicationLocale)?.firstOrNull()
if (hint != null && hint != ssss_passphrase_enter_til.error.toString()) {
ssss_passphrase_enter_til.error = hint
}
} else {
ssss_passphrase_enter_til.error = null
}
}
}
}
}

View File

@ -51,15 +51,11 @@ class BootstrapMigrateBackupFragment @Inject constructor(
override fun getLayoutResId() = R.layout.fragment_bootstrap_migrate_backup override fun getLayoutResId() = R.layout.fragment_bootstrap_migrate_backup
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel() private val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
withState(sharedViewModel) {
// set initial value (useful when coming back)
bootstrapMigrateEditText.setText(it.passphrase ?: "")
}
bootstrapMigrateEditText.editorActionEvents() bootstrapMigrateEditText.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS) .throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View File

@ -26,8 +26,6 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import com.nulabinc.zxcvbn.Strength
import com.nulabinc.zxcvbn.Zxcvbn
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
@ -49,13 +47,9 @@ import kotlinx.coroutines.launch
import java.io.OutputStream import java.io.OutputStream
data class BootstrapViewState( data class BootstrapViewState(
val step: BootstrapStep = BootstrapStep.SetupPassphrase(false), val step: BootstrapStep = BootstrapStep.AccountPassword(false),
val passphrase: String? = null,
val migrationRecoveryKey: String? = null, val migrationRecoveryKey: String? = null,
val passphraseRepeat: String? = null,
val crossSigningInitialization: Async<Unit> = Uninitialized, val crossSigningInitialization: Async<Unit> = Uninitialized,
val passphraseStrength: Async<Strength> = Uninitialized,
val passphraseConfirmMatch: Async<Unit> = Uninitialized,
val recoveryKeyCreationInfo: SsssKeyCreationInfo? = null, val recoveryKeyCreationInfo: SsssKeyCreationInfo? = null,
val initializationWaitingViewData: WaitingViewData? = null, val initializationWaitingViewData: WaitingViewData? = null,
val recoverySaveFileProcess: Async<Unit> = Uninitialized val recoverySaveFileProcess: Async<Unit> = Uninitialized
@ -71,8 +65,6 @@ class BootstrapSharedViewModel @AssistedInject constructor(
private val reAuthHelper: ReAuthHelper private val reAuthHelper: ReAuthHelper
) : VectorViewModel<BootstrapViewState, BootstrapActions, BootstrapViewEvents>(initialState) { ) : VectorViewModel<BootstrapViewState, BootstrapActions, BootstrapViewEvents>(initialState) {
private val zxcvbn = Zxcvbn()
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
fun create(initialState: BootstrapViewState, args: BootstrapBottomSheet.Args): BootstrapSharedViewModel fun create(initialState: BootstrapViewState, args: BootstrapBottomSheet.Args): BootstrapSharedViewModel
@ -84,7 +76,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
// need to check if user have an existing keybackup // need to check if user have an existing keybackup
if (args.isNewAccount) { if (args.isNewAccount) {
setState { setState {
copy(step = BootstrapStep.SetupPassphrase(false)) copy(step = BootstrapStep.AccountPassword(false))
} }
} else { } else {
setState { setState {
@ -99,7 +91,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
if (version == null) { if (version == null) {
// we just resume plain bootstrap // we just resume plain bootstrap
setState { setState {
copy(step = BootstrapStep.SetupPassphrase(false)) copy(step = BootstrapStep.AccountPassword(false))
} }
} else { } else {
// we need to get existing backup passphrase/key and convert to SSSS // we need to get existing backup passphrase/key and convert to SSSS
@ -128,19 +120,9 @@ class BootstrapSharedViewModel @AssistedInject constructor(
override fun handle(action: BootstrapActions) = withState { state -> override fun handle(action: BootstrapActions) = withState { state ->
when (action) { when (action) {
is BootstrapActions.GoBack -> queryBack() is BootstrapActions.GoBack -> queryBack()
BootstrapActions.TogglePasswordVisibility -> { BootstrapActions.TogglePasswordVisibility -> {
when (state.step) { when (state.step) {
is BootstrapStep.SetupPassphrase -> {
setState {
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
}
}
is BootstrapStep.ConfirmPassphrase -> {
setState {
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
}
}
is BootstrapStep.AccountPassword -> { is BootstrapStep.AccountPassword -> {
setState { setState {
copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible)) copy(step = state.step.copy(isPasswordVisible = !state.step.isPasswordVisible))
@ -155,119 +137,64 @@ class BootstrapSharedViewModel @AssistedInject constructor(
} }
} }
} }
is BootstrapActions.DoInitializeGeneratedKey -> {
is BootstrapActions.UpdateCandidatePassphrase -> {
val strength = zxcvbn.measure(action.pass)
setState {
copy(
passphrase = action.pass,
passphraseStrength = Success(strength)
)
}
}
is BootstrapActions.GoToConfirmPassphrase -> {
setState {
copy(
passphrase = action.passphrase,
step = BootstrapStep.ConfirmPassphrase(
isPasswordVisible = (state.step as? BootstrapStep.SetupPassphrase)?.isPasswordVisible ?: false
)
)
}
}
is BootstrapActions.UpdateConfirmCandidatePassphrase -> {
setState {
copy(
passphraseRepeat = action.pass
)
}
}
is BootstrapActions.DoInitialize -> {
if (state.passphrase == state.passphraseRepeat) {
val userPassword = reAuthHelper.data
if (userPassword == null) {
setState {
copy(
step = BootstrapStep.AccountPassword(false)
)
}
} else {
startInitializeFlow(userPassword)
}
} else {
setState {
copy(
passphraseConfirmMatch = Fail(Throwable(stringProvider.getString(R.string.passphrase_passphrase_does_not_match)))
)
}
}
}
is BootstrapActions.DoInitializeGeneratedKey -> {
val userPassword = reAuthHelper.data val userPassword = reAuthHelper.data
if (userPassword == null) { if (userPassword == null) {
setState { setState {
copy( copy(
passphrase = null,
passphraseRepeat = null,
step = BootstrapStep.AccountPassword(false) step = BootstrapStep.AccountPassword(false)
) )
} }
} else { } else {
setState {
copy(
passphrase = null,
passphraseRepeat = null
)
}
startInitializeFlow(userPassword) startInitializeFlow(userPassword)
} }
} }
BootstrapActions.RecoveryKeySaved -> { BootstrapActions.RecoveryKeySaved -> {
_viewEvents.post(BootstrapViewEvents.RecoveryKeySaved) _viewEvents.post(BootstrapViewEvents.RecoveryKeySaved)
setState { setState {
copy(step = BootstrapStep.SaveRecoveryKey(true)) copy(step = BootstrapStep.SaveRecoveryKey(true))
} }
} }
BootstrapActions.Completed -> { BootstrapActions.Completed -> {
_viewEvents.post(BootstrapViewEvents.Dismiss) _viewEvents.post(BootstrapViewEvents.Dismiss)
} }
BootstrapActions.GoToCompleted -> { BootstrapActions.GoToCompleted -> {
setState { setState {
copy(step = BootstrapStep.DoneSuccess) copy(step = BootstrapStep.DoneSuccess)
} }
} }
BootstrapActions.SaveReqQueryStarted -> { BootstrapActions.SaveReqQueryStarted -> {
setState { setState {
copy(recoverySaveFileProcess = Loading()) copy(recoverySaveFileProcess = Loading())
} }
} }
is BootstrapActions.SaveKeyToUri -> { is BootstrapActions.SaveKeyToUri -> {
saveRecoveryKeyToUri(action.os) saveRecoveryKeyToUri(action.os)
} }
BootstrapActions.SaveReqFailed -> { BootstrapActions.SaveReqFailed -> {
setState { setState {
copy(recoverySaveFileProcess = Uninitialized) copy(recoverySaveFileProcess = Uninitialized)
} }
} }
BootstrapActions.GoToEnterAccountPassword -> { BootstrapActions.GoToEnterAccountPassword -> {
setState { setState {
copy(step = BootstrapStep.AccountPassword(false)) copy(step = BootstrapStep.AccountPassword(false))
} }
} }
BootstrapActions.HandleForgotBackupPassphrase -> { BootstrapActions.HandleForgotBackupPassphrase -> {
if (state.step is BootstrapStep.GetBackupSecretPassForMigration) { if (state.step is BootstrapStep.GetBackupSecretPassForMigration) {
setState { setState {
copy(step = BootstrapStep.GetBackupSecretPassForMigration(state.step.isPasswordVisible, true)) copy(step = BootstrapStep.GetBackupSecretPassForMigration(state.step.isPasswordVisible, true))
} }
} else return@withState } else return@withState
} }
is BootstrapActions.ReAuth -> { is BootstrapActions.ReAuth -> {
startInitializeFlow(action.pass) startInitializeFlow(action.pass)
} }
is BootstrapActions.DoMigrateWithPassphrase -> { is BootstrapActions.DoMigrateWithPassphrase -> {
startMigrationFlow(state.step, action.passphrase, null) startMigrationFlow(state.step, action.passphrase, null)
} }
is BootstrapActions.DoMigrateWithRecoveryKey -> { is BootstrapActions.DoMigrateWithRecoveryKey -> {
startMigrationFlow(state.step, null, action.recoveryKey) startMigrationFlow(state.step, null, action.recoveryKey)
} }
}.exhaustive }.exhaustive
@ -316,8 +243,6 @@ class BootstrapSharedViewModel @AssistedInject constructor(
if (it is BackupToQuadSMigrationTask.Result.Success) { if (it is BackupToQuadSMigrationTask.Result.Success) {
setState { setState {
copy( copy(
passphrase = passphrase,
passphraseRepeat = passphrase,
migrationRecoveryKey = recoveryKey migrationRecoveryKey = recoveryKey
) )
} }
@ -365,6 +290,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
} }
withState { state -> withState { state ->
val previousStep = state.step
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
val userPasswordAuth = userPassword?.let { val userPasswordAuth = userPassword?.let {
UserPasswordAuth( UserPasswordAuth(
@ -379,7 +305,6 @@ class BootstrapSharedViewModel @AssistedInject constructor(
Params( Params(
userPasswordAuth = userPasswordAuth, userPasswordAuth = userPasswordAuth,
progressListener = progressListener, progressListener = progressListener,
passphrase = state.passphrase,
keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } } keySpec = state.migrationRecoveryKey?.let { extractCurveKeyFromRecoveryKey(it)?.let { RawBytesKeySpec(it) } }
) )
) { bootstrapResult -> ) { bootstrapResult ->
@ -423,7 +348,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
_viewEvents.post(BootstrapViewEvents.ModalError(bootstrapResult.error ?: stringProvider.getString(R.string.matrix_error))) _viewEvents.post(BootstrapViewEvents.ModalError(bootstrapResult.error ?: stringProvider.getString(R.string.matrix_error)))
setState { setState {
copy( copy(
step = BootstrapStep.ConfirmPassphrase(false) step = previousStep
) )
} }
} }
@ -459,25 +384,12 @@ class BootstrapSharedViewModel @AssistedInject constructor(
// do we let you cancel from here? // do we let you cancel from here?
_viewEvents.post(BootstrapViewEvents.SkipBootstrap()) _viewEvents.post(BootstrapViewEvents.SkipBootstrap())
} }
is BootstrapStep.SetupPassphrase -> {
// do we let you cancel from here?
_viewEvents.post(BootstrapViewEvents.SkipBootstrap())
}
is BootstrapStep.ConfirmPassphrase -> {
setState {
copy(
step = BootstrapStep.SetupPassphrase(
isPasswordVisible = (state.step as? BootstrapStep.ConfirmPassphrase)?.isPasswordVisible ?: false
)
)
}
}
is BootstrapStep.AccountPassword -> { is BootstrapStep.AccountPassword -> {
_viewEvents.post(BootstrapViewEvents.SkipBootstrap(state.passphrase != null)) _viewEvents.post(BootstrapViewEvents.SkipBootstrap(false))
} }
BootstrapStep.Initializing -> { BootstrapStep.Initializing -> {
// do we let you cancel from here? // do we let you cancel from here?
_viewEvents.post(BootstrapViewEvents.SkipBootstrap(state.passphrase != null)) _viewEvents.post(BootstrapViewEvents.SkipBootstrap(false))
} }
is BootstrapStep.SaveRecoveryKey, is BootstrapStep.SaveRecoveryKey,
BootstrapStep.DoneSuccess -> { BootstrapStep.DoneSuccess -> {
@ -490,7 +402,8 @@ class BootstrapSharedViewModel @AssistedInject constructor(
// Companion, view model assisted creation // Companion, view model assisted creation
// ====================================== // ======================================
companion object : MvRxViewModelFactory<BootstrapSharedViewModel, BootstrapViewState> { companion object
: MvRxViewModelFactory<BootstrapSharedViewModel, BootstrapViewState> {
override fun create(viewModelContext: ViewModelContext, state: BootstrapViewState): BootstrapSharedViewModel? { override fun create(viewModelContext: ViewModelContext, state: BootstrapViewState): BootstrapSharedViewModel? {
val fragment: BootstrapBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() val fragment: BootstrapBottomSheet = (viewModelContext as FragmentViewModelContext).fragment()

View File

@ -30,29 +30,25 @@ package im.vector.riotx.features.crypto.recover
* *
* *
* *
* Existing No * Existing No
* Keybackup KeyBackup * Keybackup KeyBackup
* *
* *
* *
* BootstrapStep.SetupPassphrase *
* BootstrapStep.GetBackupSecretForMigration * BootstrapStep.GetBackupSecretForMigration
* *
* Back *
* *
* * is password needed?
* BootstrapStep.ConfirmPassphrase *
* *
*
* is password needed?
*
*
* *
* BootstrapStep.AccountPassword * BootstrapStep.AccountPassword
* *
* *
* *
* password not needed (in * password not needed (in
* memory) * memory)
* *
* *
@ -77,8 +73,6 @@ package im.vector.riotx.features.crypto.recover
*/ */
sealed class BootstrapStep { sealed class BootstrapStep {
data class SetupPassphrase(val isPasswordVisible: Boolean) : BootstrapStep()
data class ConfirmPassphrase(val isPasswordVisible: Boolean) : BootstrapStep()
data class AccountPassword(val isPasswordVisible: Boolean, val failure: String? = null) : BootstrapStep() data class AccountPassword(val isPasswordVisible: Boolean, val failure: String? = null) : BootstrapStep()
object CheckingMigration : BootstrapStep() object CheckingMigration : BootstrapStep()