mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 20:06:51 +03:00
Login screens: add MSISDN
This commit is contained in:
parent
248a584e1a
commit
127916a8d9
13 changed files with 179 additions and 32 deletions
|
@ -33,9 +33,9 @@ interface RegistrationWizard {
|
|||
|
||||
fun addThreePid(threePid: RegisterThreePid, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
|
||||
fun confirmMsisdn(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
|
||||
fun validateEmail(callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
fun checkIfEmailHasBeenValidated(callback: MatrixCallback<RegistrationResult>): Cancelable
|
||||
|
||||
val currentThreePid: String?
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ sealed class Failure(cause: Throwable? = null) : Throwable(cause = cause) {
|
|||
data class Cancelled(val throwable: Throwable? = null) : Failure(throwable)
|
||||
data class NetworkConnection(val ioException: IOException? = null) : Failure(ioException)
|
||||
data class ServerError(val error: MatrixError, val httpCode: Int) : Failure(RuntimeException(error.toString()))
|
||||
object SuccessError : Failure(RuntimeException(RuntimeException("SuccessResult is false")))
|
||||
// When server send an error, but it cannot be interpreted as a MatrixError
|
||||
data class OtherServerError(val errorBody: String, val httpCode: Int) : Failure(RuntimeException(errorBody))
|
||||
|
||||
|
|
|
@ -19,9 +19,7 @@ package im.vector.matrix.android.internal.auth
|
|||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
||||
import im.vector.matrix.android.internal.auth.data.PasswordLoginParams
|
||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationParams
|
||||
import im.vector.matrix.android.internal.auth.registration.AddThreePidRegistrationResponse
|
||||
import im.vector.matrix.android.internal.auth.registration.RegistrationParams
|
||||
import im.vector.matrix.android.internal.auth.registration.*
|
||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.*
|
||||
|
@ -46,6 +44,12 @@ internal interface AuthAPI {
|
|||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register/{threePid}/requestToken")
|
||||
fun add3Pid(@Path("threePid") threePid: String, @Body params: AddThreePidRegistrationParams): Call<AddThreePidRegistrationResponse>
|
||||
|
||||
/**
|
||||
* Validate 3pid
|
||||
*/
|
||||
@POST
|
||||
fun validate3Pid(@Url url: String, @Body params: ValidationCodeBody): Call<SuccessResult>
|
||||
|
||||
/**
|
||||
* Get the supported login flow
|
||||
* Ref: https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-login
|
||||
|
|
|
@ -61,6 +61,10 @@ internal data class AuthParams(
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that there is a bug in Synapse (I have to investigate where), but if we pass LoginFlowTypes.MSISDN,
|
||||
* the homeserver answer with the login flow with MatrixError fields and not with a simple MatrixError 401.
|
||||
*/
|
||||
fun createForMsisdnIdentity(session: String, threePidCredentials: ThreePidCredentials): AuthParams {
|
||||
return AuthParams(
|
||||
type = LoginFlowTypes.MSISDN,
|
||||
|
|
|
@ -63,6 +63,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
|||
private val authAPI = buildAuthAPI()
|
||||
private val registerTask = DefaultRegisterTask(authAPI)
|
||||
private val registerAddThreePidTask = DefaultRegisterAddThreePidTask(authAPI)
|
||||
private val validateCodeTask = DefaultValidateCodeTask(authAPI)
|
||||
|
||||
private var currentThreePidData: ThreePidData? = null
|
||||
|
||||
|
@ -168,7 +169,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
|||
return CancelableCoroutine(job)
|
||||
}
|
||||
|
||||
override fun validateEmail(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
override fun checkIfEmailHasBeenValidated(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeParam = currentThreePidData?.registrationParams ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, no pending three pid"))
|
||||
return NoOpCancellable
|
||||
|
@ -178,18 +179,49 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
|
|||
return performRegistrationRequest(safeParam, callback, 10_000)
|
||||
}
|
||||
|
||||
override fun confirmMsisdn(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeSession = currentSession ?: run {
|
||||
override fun handleValidateThreePid(code: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
val safeParam = currentThreePidData?.registrationParams ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, no pending three pid"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
|
||||
val safeCurrentData = currentThreePidData ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
|
||||
// TODO
|
||||
return performRegistrationRequest(
|
||||
RegistrationParams(
|
||||
// TODO
|
||||
auth = AuthParams.createForEmailIdentity(safeSession, ThreePidCredentials(code))
|
||||
), callback)
|
||||
val url = safeCurrentData.addThreePidRegistrationResponse.submitUrl ?: run {
|
||||
callback.onFailure(IllegalStateException("Missing url the send the code"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
|
||||
val params = ValidationCodeBody(
|
||||
clientSecret = clientSecret,
|
||||
sid = safeCurrentData.addThreePidRegistrationResponse.sid,
|
||||
code = code
|
||||
)
|
||||
|
||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||
runCatching {
|
||||
validateCodeTask.execute(ValidateCodeTask.Params(url, params))
|
||||
}
|
||||
.fold(
|
||||
{
|
||||
if (it.success == true) {
|
||||
// The entered code is correct
|
||||
// Same that validate email
|
||||
performRegistrationRequest(safeParam, callback, 3_000)
|
||||
} else {
|
||||
// The code is not correct
|
||||
callback.onFailure(Failure.SuccessError)
|
||||
}
|
||||
},
|
||||
{
|
||||
callback.onFailure(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
return CancelableCoroutine(job)
|
||||
}
|
||||
|
||||
override fun dummy(callback: MatrixCallback<RegistrationResult>): Cancelable {
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.matrix.android.internal.auth.registration
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SuccessResult(
|
||||
@Json(name = "success")
|
||||
val success: Boolean?
|
||||
)
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.matrix.android.internal.auth.registration
|
||||
|
||||
import im.vector.matrix.android.internal.auth.AuthAPI
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
|
||||
internal interface ValidateCodeTask : Task<ValidateCodeTask.Params, SuccessResult> {
|
||||
data class Params(
|
||||
val url: String,
|
||||
val body: ValidationCodeBody
|
||||
)
|
||||
}
|
||||
|
||||
internal class DefaultValidateCodeTask(private val authAPI: AuthAPI)
|
||||
: ValidateCodeTask {
|
||||
|
||||
override suspend fun execute(params: ValidateCodeTask.Params): SuccessResult {
|
||||
return executeRequest {
|
||||
apiCall = authAPI.validate3Pid(params.url, params.body)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.matrix.android.internal.auth.registration
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
/**
|
||||
* This object is used to send a code received by SMS to validate Msisdn ownership
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ValidationCodeBody(
|
||||
@Json(name = "client_secret")
|
||||
val clientSecret: String,
|
||||
|
||||
@Json(name = "sid")
|
||||
val sid: String,
|
||||
|
||||
@Json(name = "token")
|
||||
val code: String
|
||||
)
|
|
@ -35,8 +35,9 @@ sealed class LoginAction : VectorViewModelAction {
|
|||
data class RegisterWith(val username: String, val password: String, val initialDeviceName: String) : RegisterAction()
|
||||
data class AddThreePid(val threePid: RegisterThreePid) : RegisterAction()
|
||||
// TODO Confirm Email (from link in the email, open in the phone, intercepted by RiotX)
|
||||
data class ConfirmMsisdn(val code: String) : RegisterAction()
|
||||
object ValidateEmail : RegisterAction()
|
||||
data class ValidateThreePid(val code: String) : RegisterAction()
|
||||
|
||||
object CheckIfEmailHasBeenValidated : RegisterAction()
|
||||
|
||||
data class CaptchaDone(val captchaResponse: String) : RegisterAction()
|
||||
object AcceptTerms : RegisterAction()
|
||||
|
|
|
@ -121,10 +121,10 @@ class LoginGenericTextInputFormFragment @Inject constructor(private val errorFor
|
|||
}
|
||||
TextInputFormFragmentMode.SetMsisdn -> {
|
||||
// TODO Country code
|
||||
loginViewModel.handle(LoginAction.AddThreePid(RegisterThreePid.Msisdn(text, "TODO")))
|
||||
loginViewModel.handle(LoginAction.AddThreePid(RegisterThreePid.Msisdn(text, "FR")))
|
||||
}
|
||||
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
||||
loginViewModel.handle(LoginAction.ConfirmMsisdn(text))
|
||||
loginViewModel.handle(LoginAction.ValidateThreePid(text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +163,12 @@ class LoginGenericTextInputFormFragment @Inject constructor(private val errorFor
|
|||
}
|
||||
}
|
||||
TextInputFormFragmentMode.ConfirmMsisdn -> {
|
||||
// TODO
|
||||
if (throwable is Failure.SuccessError) {
|
||||
// The entered code is not correct
|
||||
loginGenericTextInputFormTil.error = getString(R.string.login_validation_code_is_not_correct)
|
||||
} else {
|
||||
loginGenericTextInputFormTil.error = errorFormatter.toHumanReadable(throwable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,25 +105,25 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
|
||||
private fun handleRegisterAction(action: LoginAction.RegisterAction) {
|
||||
when (action) {
|
||||
is LoginAction.RegisterWith -> handleRegisterWith(action)
|
||||
is LoginAction.CaptchaDone -> handleCaptchaDone(action)
|
||||
is LoginAction.AcceptTerms -> handleAcceptTerms()
|
||||
is LoginAction.RegisterDummy -> handleRegisterDummy()
|
||||
is LoginAction.AddThreePid -> handleAddThreePid(action)
|
||||
is LoginAction.ConfirmMsisdn -> handleConfirmMsisdn(action)
|
||||
is LoginAction.ValidateEmail -> handleValidateEmail()
|
||||
is LoginAction.RegisterWith -> handleRegisterWith(action)
|
||||
is LoginAction.CaptchaDone -> handleCaptchaDone(action)
|
||||
is LoginAction.AcceptTerms -> handleAcceptTerms()
|
||||
is LoginAction.RegisterDummy -> handleRegisterDummy()
|
||||
is LoginAction.AddThreePid -> handleAddThreePid(action)
|
||||
is LoginAction.ValidateThreePid -> handleValidateThreePid(action)
|
||||
is LoginAction.CheckIfEmailHasBeenValidated -> handleCheckIfEmailHasBeenValidated()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleValidateEmail() {
|
||||
private fun handleCheckIfEmailHasBeenValidated() {
|
||||
// We do not want the common progress bar to be displayed, so we do not change asyncRegistration value in the state
|
||||
currentTask?.cancel()
|
||||
currentTask = registrationWizard?.validateEmail(registrationCallback)
|
||||
currentTask = registrationWizard?.checkIfEmailHasBeenValidated(registrationCallback)
|
||||
}
|
||||
|
||||
private fun handleConfirmMsisdn(action: LoginAction.ConfirmMsisdn) {
|
||||
private fun handleValidateThreePid(action: LoginAction.ValidateThreePid) {
|
||||
setState { copy(asyncRegistration = Loading()) }
|
||||
currentTask = registrationWizard?.confirmMsisdn(action.code, registrationCallback)
|
||||
currentTask = registrationWizard?.handleValidateThreePid(action.code, registrationCallback)
|
||||
}
|
||||
|
||||
private val registrationCallback = object : MatrixCallback<RegistrationResult> {
|
||||
|
|
|
@ -49,7 +49,7 @@ class LoginWaitForEmailFragment @Inject constructor(private val errorFormatter:
|
|||
|
||||
setupUi()
|
||||
|
||||
loginViewModel.handle(LoginAction.ValidateEmail)
|
||||
loginViewModel.handle(LoginAction.CheckIfEmailHasBeenValidated)
|
||||
}
|
||||
|
||||
private fun setupUi() {
|
||||
|
@ -59,7 +59,7 @@ class LoginWaitForEmailFragment @Inject constructor(private val errorFormatter:
|
|||
override fun onRegistrationError(throwable: Throwable) {
|
||||
if (throwable.is401()) {
|
||||
// Try again, with a delay
|
||||
loginViewModel.handle(LoginAction.ValidateEmail)
|
||||
loginViewModel.handle(LoginAction.CheckIfEmailHasBeenValidated)
|
||||
} else {
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.dialog_title_error)
|
||||
|
|
|
@ -110,5 +110,6 @@
|
|||
|
||||
<string name="login_wait_for_email_title">Please check your email</string>
|
||||
<string name="login_wait_for_email_notice">We just sent an email to %1$s.\nPlease click on the link it contains to continue the account creation.</string>
|
||||
<string name="login_validation_code_is_not_correct">The entered code is not correct. Please check.</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue