Add some guard, and allow to cancel adding 3pid

This commit is contained in:
Benoit Marty 2020-08-28 17:55:38 +02:00
parent f6f9373aeb
commit 1af45ede62
9 changed files with 126 additions and 71 deletions

View file

@ -107,6 +107,12 @@ interface ProfileService {
accountPassword: String?, accountPassword: String?,
matrixCallback: MatrixCallback<Unit>): Cancelable matrixCallback: MatrixCallback<Unit>): Cancelable
/**
* Cancel adding a threepid. It will remove data store locally about this ThreePid
*/
fun cancelAddingThreePid(threePid: ThreePid,
matrixCallback: MatrixCallback<Unit>): Cancelable
/** /**
* Delete a 3Pids. * Delete a 3Pids.
*/ */

View file

@ -163,7 +163,25 @@ internal class DefaultProfileService @Inject constructor(private val taskExecuto
accountPassword: String?, accountPassword: String?,
matrixCallback: MatrixCallback<Unit>): Cancelable { matrixCallback: MatrixCallback<Unit>): Cancelable {
return finalizeAddingThreePidTask return finalizeAddingThreePidTask
.configureWith(FinalizeAddingThreePidTask.Params(threePid, uiaSession, accountPassword)) { .configureWith(FinalizeAddingThreePidTask.Params(
threePid = threePid,
session = uiaSession,
accountPassword = accountPassword,
userWantsToCancel = false
)) {
callback = alsoRefresh(matrixCallback)
}
.executeBy(taskExecutor)
}
override fun cancelAddingThreePid(threePid: ThreePid, matrixCallback: MatrixCallback<Unit>): Cancelable {
return finalizeAddingThreePidTask
.configureWith(FinalizeAddingThreePidTask.Params(
threePid = threePid,
session = null,
accountPassword = null,
userWantsToCancel = true
)) {
callback = alsoRefresh(matrixCallback) callback = alsoRefresh(matrixCallback)
} }
.executeBy(taskExecutor) .executeBy(taskExecutor)

View file

@ -36,7 +36,8 @@ internal abstract class FinalizeAddingThreePidTask : Task<FinalizeAddingThreePid
data class Params( data class Params(
val threePid: ThreePid, val threePid: ThreePid,
val session: String?, val session: String?,
val accountPassword: String? val accountPassword: String?,
val userWantsToCancel: Boolean
) )
} }
@ -48,35 +49,41 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor(
private val eventBus: EventBus) : FinalizeAddingThreePidTask() { private val eventBus: EventBus) : FinalizeAddingThreePidTask() {
override suspend fun execute(params: Params) { override suspend fun execute(params: Params) {
// Get the required pending data if (params.userWantsToCancel.not()) {
val pendingThreePids = monarchy.fetchAllMappedSync( // Get the required pending data
{ it.where(PendingThreePidEntity::class.java) }, val pendingThreePids = monarchy.fetchAllMappedSync(
{ pendingThreePidMapper.map(it) } { it.where(PendingThreePidEntity::class.java) },
) { pendingThreePidMapper.map(it) }
.firstOrNull { it.threePid == params.threePid } )
?: throw IllegalArgumentException("unknown threepid") .firstOrNull { it.threePid == params.threePid }
?: throw IllegalArgumentException("unknown threepid")
try { try {
executeRequest<Unit>(eventBus) { executeRequest<Unit>(eventBus) {
val body = FinalizeAddThreePidBody( val body = FinalizeAddThreePidBody(
clientSecret = pendingThreePids.clientSecret, clientSecret = pendingThreePids.clientSecret,
sid = pendingThreePids.sid, sid = pendingThreePids.sid,
auth = if (params.session != null && params.accountPassword != null) { auth = if (params.session != null && params.accountPassword != null) {
UserPasswordAuth( UserPasswordAuth(
session = params.session, session = params.session,
user = userId, user = userId,
password = params.accountPassword password = params.accountPassword
) )
} else null } else null
) )
apiCall = profileAPI.finalizeAddThreePid(body) apiCall = profileAPI.finalizeAddThreePid(body)
}
} catch (throwable: Throwable) {
throw throwable.toRegistrationFlowResponse()
?.let { Failure.RegistrationFlowError(it) }
?: throwable
} }
} catch (throwable: Throwable) {
throw throwable.toRegistrationFlowResponse()
?.let { Failure.RegistrationFlowError(it) }
?: throwable
} }
cleanupDatabase(params)
}
private suspend fun cleanupDatabase(params: Params) {
// Delete the pending three pid // Delete the pending three pid
monarchy.awaitTransaction { realm -> monarchy.awaitTransaction { realm ->
realm.where(PendingThreePidEntity::class.java) realm.where(PendingThreePidEntity::class.java)

View file

@ -341,25 +341,22 @@ class DiscoverySettingsController @Inject constructor(
private fun buildContinueCancel(threePid: ThreePid) { private fun buildContinueCancel(threePid: ThreePid) {
settingsContinueCancelItem { settingsContinueCancelItem {
id("bottom${threePid.value}") id("bottom${threePid.value}")
interactionListener(object : SettingsContinueCancelItem.Listener { continueOnClick {
override fun onContinue() { when (threePid) {
when (threePid) { is ThreePid.Email -> {
is ThreePid.Email -> { listener?.checkEmailVerification(threePid)
listener?.checkEmailVerification(threePid) }
} is ThreePid.Msisdn -> {
is ThreePid.Msisdn -> { val code = codes[threePid]
val code = codes[threePid] if (code != null) {
if (code != null) { listener?.sendMsisdnVerificationCode(threePid, code)
listener?.sendMsisdnVerificationCode(threePid, code)
}
} }
} }
} }
}
override fun onCancel() { cancelOnClick {
listener?.cancelBinding(threePid) listener?.cancelBinding(threePid)
} }
})
} }
} }

View file

@ -20,33 +20,28 @@ import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import com.airbnb.epoxy.EpoxyModelWithHolder import com.airbnb.epoxy.EpoxyModelWithHolder
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
import im.vector.app.core.epoxy.onClick
@EpoxyModelClass(layout = R.layout.item_settings_continue_cancel) @EpoxyModelClass(layout = R.layout.item_settings_continue_cancel)
abstract class SettingsContinueCancelItem : EpoxyModelWithHolder<SettingsContinueCancelItem.Holder>() { abstract class SettingsContinueCancelItem : EpoxyModelWithHolder<SettingsContinueCancelItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var interactionListener: Listener? = null var continueOnClick: ClickListener? = null
@EpoxyAttribute
var cancelOnClick: ClickListener? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.cancelButton.setOnClickListener { holder.cancelButton.onClick(cancelOnClick)
interactionListener?.onCancel() holder.continueButton.onClick(continueOnClick)
}
holder.continueButton.setOnClickListener {
interactionListener?.onContinue()
}
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {
val cancelButton by bind<Button>(R.id.settings_item_cancel_button) val cancelButton by bind<Button>(R.id.settings_item_cancel_button)
val continueButton by bind<Button>(R.id.settings_item_continue_button) val continueButton by bind<Button>(R.id.settings_item_continue_button)
} }
interface Listener {
fun onContinue()
fun onCancel()
}
} }

View file

@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.identity.ThreePid
sealed class ThreePidsSettingsAction : VectorViewModelAction { sealed class ThreePidsSettingsAction : VectorViewModelAction {
data class AddThreePid(val threePid: ThreePid) : ThreePidsSettingsAction() data class AddThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
data class ContinueThreePid(val threePid: ThreePid) : ThreePidsSettingsAction() data class ContinueThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
data class CancelThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
data class AccountPassword(val password: String) : ThreePidsSettingsAction() data class AccountPassword(val password: String) : ThreePidsSettingsAction()
data class DeleteThreePid(val threePid: ThreePid) : ThreePidsSettingsAction() data class DeleteThreePid(val threePid: ThreePid) : ThreePidsSettingsAction()
} }

View file

@ -30,6 +30,8 @@ import im.vector.app.core.ui.list.GenericItem
import im.vector.app.core.ui.list.genericButtonItem import im.vector.app.core.ui.list.genericButtonItem
import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericFooterItem
import im.vector.app.core.ui.list.genericItem import im.vector.app.core.ui.list.genericItem
import im.vector.app.features.discovery.settingsContinueCancelItem
import im.vector.app.features.discovery.settingsInformationItem
import im.vector.app.features.discovery.settingsSectionTitleItem import im.vector.app.features.discovery.settingsSectionTitleItem
import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.identity.ThreePid
import javax.inject.Inject import javax.inject.Inject
@ -43,6 +45,7 @@ class ThreePidsSettingsController @Inject constructor(
fun addEmail() fun addEmail()
fun addMsisdn() fun addMsisdn()
fun continueThreePid(threePid: ThreePid) fun continueThreePid(threePid: ThreePid)
fun cancelThreePid(threePid: ThreePid)
fun deleteThreePid(threePid: ThreePid) fun deleteThreePid(threePid: ThreePid)
} }
@ -80,12 +83,12 @@ class ThreePidsSettingsController @Inject constructor(
title(stringProvider.getString(R.string.settings_emails)) title(stringProvider.getString(R.string.settings_emails))
} }
emails.forEach { buildThreePid("email_", it) } emails.forEach { buildThreePid("email ", it) }
// Pending threePids // Pending threePids
pendingThreePids.invoke() pendingThreePids.invoke()
?.filterIsInstance(ThreePid.Email::class.java) ?.filterIsInstance(ThreePid.Email::class.java)
?.forEach { buildPendingThreePid("email_", it) } ?.forEach { buildPendingThreePid("email ", it) }
genericButtonItem { genericButtonItem {
id("addEmail") id("addEmail")
@ -99,12 +102,12 @@ class ThreePidsSettingsController @Inject constructor(
title(stringProvider.getString(R.string.settings_phone_numbers)) title(stringProvider.getString(R.string.settings_phone_numbers))
} }
msisdn.forEach { buildThreePid("msisdn_", it) } msisdn.forEach { buildThreePid("msisdn ", it) }
// Pending threePids // Pending threePids
pendingThreePids.invoke() pendingThreePids.invoke()
?.filterIsInstance(ThreePid.Msisdn::class.java) ?.filterIsInstance(ThreePid.Msisdn::class.java)
?.forEach { buildPendingThreePid("msisdn_", it) } ?.forEach { buildPendingThreePid("msisdn ", it) }
genericButtonItem { genericButtonItem {
id("addMsisdn") id("addMsisdn")
@ -131,15 +134,20 @@ class ThreePidsSettingsController @Inject constructor(
genericItem { genericItem {
id(idPrefix + threePid.value) id(idPrefix + threePid.value)
title(threePid.value) title(threePid.value)
if (threePid is ThreePid.Email) { }
description(stringProvider.getString(R.string.account_email_validation_message))
if (threePid is ThreePid.Email) {
settingsInformationItem {
id("info" + idPrefix + threePid.value)
message(stringProvider.getString(R.string.account_email_validation_message))
colorProvider(colorProvider)
} }
buttonAction( }
GenericItem.Action(stringProvider.getString(R.string._continue))
.apply { settingsContinueCancelItem {
perform = Runnable { interactionListener?.continueThreePid(threePid) } id("cont" + idPrefix + threePid.value)
} continueOnClick { interactionListener?.continueThreePid(threePid) }
) cancelOnClick { interactionListener?.cancelThreePid(threePid) }
} }
} }
} }

View file

@ -125,6 +125,10 @@ class ThreePidsSettingsFragment @Inject constructor(
viewModel.handle(ThreePidsSettingsAction.ContinueThreePid(threePid)) viewModel.handle(ThreePidsSettingsAction.ContinueThreePid(threePid))
} }
override fun cancelThreePid(threePid: ThreePid) {
viewModel.handle(ThreePidsSettingsAction.CancelThreePid(threePid))
}
override fun deleteThreePid(threePid: ThreePid) { override fun deleteThreePid(threePid: ThreePid) {
AlertDialog.Builder(requireActivity()) AlertDialog.Builder(requireActivity())
.setMessage(getString(R.string.settings_remove_three_pid_confirmation_content, threePid.value)) .setMessage(getString(R.string.settings_remove_three_pid_confirmation_content, threePid.value))

View file

@ -23,8 +23,10 @@ import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
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.app.R
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
@ -35,7 +37,8 @@ import org.matrix.android.sdk.rx.rx
class ThreePidsSettingsViewModel @AssistedInject constructor( class ThreePidsSettingsViewModel @AssistedInject constructor(
@Assisted initialState: ThreePidsSettingsViewState, @Assisted initialState: ThreePidsSettingsViewState,
private val session: Session private val session: Session,
private val stringProvider: StringProvider
) : VectorViewModel<ThreePidsSettingsViewState, ThreePidsSettingsAction, ThreePidsSettingsViewEvents>(initialState) { ) : VectorViewModel<ThreePidsSettingsViewState, ThreePidsSettingsAction, ThreePidsSettingsViewEvents>(initialState) {
// UIA session // UIA session
@ -128,6 +131,7 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
when (action) { when (action) {
is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action) is ThreePidsSettingsAction.AddThreePid -> handleAddThreePid(action)
is ThreePidsSettingsAction.ContinueThreePid -> handleContinueThreePid(action) is ThreePidsSettingsAction.ContinueThreePid -> handleContinueThreePid(action)
is ThreePidsSettingsAction.CancelThreePid -> handleCancelThreePid(action)
is ThreePidsSettingsAction.AccountPassword -> handleAccountPassword(action) is ThreePidsSettingsAction.AccountPassword -> handleAccountPassword(action)
is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action) is ThreePidsSettingsAction.DeleteThreePid -> handleDeleteThreePid(action)
}.exhaustive }.exhaustive
@ -135,8 +139,16 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
private fun handleAddThreePid(action: ThreePidsSettingsAction.AddThreePid) { private fun handleAddThreePid(action: ThreePidsSettingsAction.AddThreePid) {
isLoading(true) isLoading(true)
viewModelScope.launch {
session.addThreePid(action.threePid, loadingCallback) withState { state ->
val allThreePids = state.threePids.invoke().orEmpty() + state.pendingThreePids.invoke().orEmpty()
if (allThreePids.any { it.value == action.threePid.value }) {
_viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalArgumentException(stringProvider.getString(R.string.auth_email_already_defined))))
} else {
viewModelScope.launch {
session.addThreePid(action.threePid, loadingCallback)
}
}
} }
} }
@ -148,6 +160,13 @@ class ThreePidsSettingsViewModel @AssistedInject constructor(
} }
} }
private fun handleCancelThreePid(action: ThreePidsSettingsAction.CancelThreePid) {
isLoading(true)
viewModelScope.launch {
session.cancelAddingThreePid(action.threePid, loadingCallback)
}
}
private fun handleAccountPassword(action: ThreePidsSettingsAction.AccountPassword) { private fun handleAccountPassword(action: ThreePidsSettingsAction.AccountPassword) {
val safeSession = pendingSession ?: return Unit val safeSession = pendingSession ?: return Unit
.also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending session"))) } .also { _viewEvents.post(ThreePidsSettingsViewEvents.Failure(IllegalStateException("No pending session"))) }