mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-16 20:10:04 +03:00
Login screens: reset password WIP
This commit is contained in:
parent
810b226f21
commit
90027cc4d5
18 changed files with 388 additions and 54 deletions
|
@ -26,6 +26,7 @@ import im.vector.matrix.android.internal.auth.data.LoginFlowResponse
|
|||
|
||||
/**
|
||||
* This interface defines methods to authenticate to a matrix server.
|
||||
* TODO Some methods has to be moved to and authenticationWizard, has it is done for registration
|
||||
*/
|
||||
interface Authenticator {
|
||||
|
||||
|
@ -78,4 +79,9 @@ interface Authenticator {
|
|||
* Reset user password
|
||||
*/
|
||||
fun resetPassword(homeServerConnectionConfig: HomeServerConnectionConfig, email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable
|
||||
|
||||
/**
|
||||
* Confirm the new password, once the user has check his email
|
||||
*/
|
||||
fun resetPasswordMailConfirmed(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<Unit>): Cancelable
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ 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.*
|
||||
import im.vector.matrix.android.internal.auth.signin.ResetPasswordMailConfirmed
|
||||
import im.vector.matrix.android.internal.network.NetworkConstants
|
||||
import retrofit2.Call
|
||||
import retrofit2.http.*
|
||||
|
@ -66,4 +67,16 @@ internal interface AuthAPI {
|
|||
@Headers("CONNECT_TIMEOUT:60000", "READ_TIMEOUT:60000", "WRITE_TIMEOUT:60000")
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "login")
|
||||
fun login(@Body loginParams: PasswordLoginParams): Call<Credentials>
|
||||
|
||||
/**
|
||||
* Ask the homeserver to reset the password associated with the provided email.
|
||||
*/
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password/email/requestToken")
|
||||
fun resetPassword(@Body params: AddThreePidRegistrationParams): Call<AddThreePidRegistrationResponse>
|
||||
|
||||
/**
|
||||
* Ask the homeserver to reset the password with the provided new password once the email is validated.
|
||||
*/
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "account/password")
|
||||
fun resetPasswordMailConfirmed(@Body params: ResetPasswordMailConfirmed): Call<Unit>
|
||||
}
|
||||
|
|
|
@ -23,12 +23,18 @@ import im.vector.matrix.android.api.auth.Authenticator
|
|||
import im.vector.matrix.android.api.auth.data.Credentials
|
||||
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.auth.registration.RegisterThreePid
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.Cancelable
|
||||
import im.vector.matrix.android.api.util.NoOpCancellable
|
||||
import im.vector.matrix.android.internal.SessionManager
|
||||
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.data.ThreePidMedium
|
||||
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.RegisterAddThreePidTask
|
||||
import im.vector.matrix.android.internal.auth.signin.ResetPasswordMailConfirmed
|
||||
import im.vector.matrix.android.internal.di.Unauthenticated
|
||||
import im.vector.matrix.android.internal.extensions.foldToCallback
|
||||
import im.vector.matrix.android.internal.network.RetrofitFactory
|
||||
|
@ -39,8 +45,15 @@ import kotlinx.coroutines.GlobalScope
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
// Container to store the data when a reset password is in the email validation step
|
||||
internal data class ResetPasswordData(
|
||||
val newPassword: String,
|
||||
val addThreePidRegistrationResponse: AddThreePidRegistrationResponse
|
||||
)
|
||||
|
||||
internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
||||
private val okHttpClient: Lazy<OkHttpClient>,
|
||||
private val retrofitFactory: RetrofitFactory,
|
||||
|
@ -48,6 +61,10 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
|||
private val sessionParamsStore: SessionParamsStore,
|
||||
private val sessionManager: SessionManager
|
||||
) : Authenticator {
|
||||
private var clientSecret = UUID.randomUUID().toString()
|
||||
private var sendAttempt = 0
|
||||
|
||||
private var resetPasswordData: ResetPasswordData? = null
|
||||
|
||||
override fun hasAuthenticatedSessions(): Boolean {
|
||||
return sessionParamsStore.getLast() != null
|
||||
|
@ -136,20 +153,58 @@ internal class DefaultAuthenticator @Inject constructor(@Unauthenticated
|
|||
override fun resetPassword(homeServerConnectionConfig: HomeServerConnectionConfig, email: String, newPassword: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||
val result = runCatching {
|
||||
resetPasswordInternal(/*homeServerConnectionConfig, email, newPassword*/)
|
||||
resetPasswordInternal(homeServerConnectionConfig, email, newPassword)
|
||||
}
|
||||
result.foldToCallback(callback)
|
||||
}
|
||||
return CancelableCoroutine(job)
|
||||
}
|
||||
|
||||
private fun resetPasswordInternal(/*homeServerConnectionConfig: HomeServerConnectionConfig, email: String, newPassword: String*/) {
|
||||
// TODO
|
||||
error("Not implemented")
|
||||
// val authAPI = buildAuthAPI(homeServerConnectionConfig)
|
||||
// executeRequest<LoginFlowResponse> {
|
||||
// apiCall = authAPI.getLoginFlows()
|
||||
// }
|
||||
private suspend fun resetPasswordInternal(homeServerConnectionConfig: HomeServerConnectionConfig, email: String, newPassword: String) {
|
||||
val authAPI = buildAuthAPI(homeServerConnectionConfig)
|
||||
|
||||
val param = RegisterAddThreePidTask.Params(
|
||||
RegisterThreePid.Email(email),
|
||||
clientSecret,
|
||||
sendAttempt++
|
||||
)
|
||||
|
||||
val result = executeRequest<AddThreePidRegistrationResponse> {
|
||||
apiCall = authAPI.resetPassword(AddThreePidRegistrationParams.from(param))
|
||||
}
|
||||
|
||||
resetPasswordData = ResetPasswordData(newPassword, result)
|
||||
}
|
||||
|
||||
override fun resetPasswordMailConfirmed(homeServerConnectionConfig: HomeServerConnectionConfig, callback: MatrixCallback<Unit>): Cancelable {
|
||||
val safeResetPasswordData = resetPasswordData ?: run {
|
||||
callback.onFailure(IllegalStateException("developer error, no reset password in progress"))
|
||||
return NoOpCancellable
|
||||
}
|
||||
val job = GlobalScope.launch(coroutineDispatchers.main) {
|
||||
val result = runCatching {
|
||||
resetPasswordMailConfirmedInternal(homeServerConnectionConfig, safeResetPasswordData)
|
||||
}
|
||||
result.foldToCallback(callback)
|
||||
}
|
||||
return CancelableCoroutine(job)
|
||||
}
|
||||
|
||||
private suspend fun resetPasswordMailConfirmedInternal(homeServerConnectionConfig: HomeServerConnectionConfig, resetPasswordData: ResetPasswordData) {
|
||||
val authAPI = buildAuthAPI(homeServerConnectionConfig)
|
||||
|
||||
val param = ResetPasswordMailConfirmed.create(
|
||||
clientSecret,
|
||||
resetPasswordData.addThreePidRegistrationResponse.sid,
|
||||
resetPasswordData.newPassword
|
||||
)
|
||||
|
||||
executeRequest<Unit> {
|
||||
apiCall = authAPI.resetPasswordMailConfirmed(param)
|
||||
}
|
||||
|
||||
// Set to null?
|
||||
// resetPasswordData = null
|
||||
}
|
||||
|
||||
private fun buildAuthAPI(homeServerConnectionConfig: HomeServerConnectionConfig): AuthAPI {
|
||||
|
|
|
@ -28,8 +28,11 @@ internal data class AuthParams(
|
|||
@Json(name = "type")
|
||||
val type: String,
|
||||
|
||||
/**
|
||||
* Note: session can be null for reset password request
|
||||
*/
|
||||
@Json(name = "session")
|
||||
val session: String,
|
||||
val session: String?,
|
||||
|
||||
/**
|
||||
* parameter for "m.login.recaptcha" type
|
||||
|
@ -72,6 +75,17 @@ internal data class AuthParams(
|
|||
threePidCredentials = threePidCredentials
|
||||
)
|
||||
}
|
||||
|
||||
fun createForResetPassword(clientSecret: String, sid: String): AuthParams {
|
||||
return AuthParams(
|
||||
type = LoginFlowTypes.EMAIL_IDENTITY,
|
||||
session = null,
|
||||
threePidCredentials = ThreePidCredentials(
|
||||
clientSecret = clientSecret,
|
||||
sid = sid
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2014 OpenMarket Ltd
|
||||
* Copyright 2017 Vector Creations Ltd
|
||||
* Copyright 2018 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.signin
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.internal.auth.registration.AuthParams
|
||||
|
||||
/**
|
||||
* Class to pass parameters to reset the password once a email has been validated.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class ResetPasswordMailConfirmed(
|
||||
// authentication parameters
|
||||
@Json(name = "auth")
|
||||
val auth: AuthParams? = null,
|
||||
|
||||
// the new password
|
||||
@Json(name = "new_password")
|
||||
val newPassword: String? = null
|
||||
) {
|
||||
companion object {
|
||||
fun create(clientSecret: String, sid: String, newPassword: String): ResetPasswordMailConfirmed {
|
||||
return ResetPasswordMailConfirmed(
|
||||
auth = AuthParams.createForResetPassword(clientSecret, sid),
|
||||
newPassword = newPassword
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -130,6 +130,11 @@ interface FragmentModule {
|
|||
@FragmentKey(LoginServerUrlFormFragment::class)
|
||||
fun bindLoginServerUrlFormFragment(fragment: LoginServerUrlFormFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(LoginResetPasswordMailConfirmationFragment::class)
|
||||
fun bindLoginResetPasswordMailConfirmationFragment(fragment: LoginResetPasswordMailConfirmationFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(LoginResetPasswordFragment::class)
|
||||
|
|
|
@ -67,6 +67,9 @@ class ErrorFormatter @Inject constructor(private val stringProvider: StringProvi
|
|||
throwable.error.code == MatrixError.LIMIT_EXCEEDED -> {
|
||||
stringProvider.getString(R.string.login_error_limit_exceeded)
|
||||
}
|
||||
throwable.error.code == MatrixError.THREEPID_NOT_FOUND -> {
|
||||
stringProvider.getString(R.string.login_reset_password_error_not_found)
|
||||
}
|
||||
else -> {
|
||||
throwable.error.message.takeIf { it.isNotEmpty() }
|
||||
?: throwable.error.code.takeIf { it.isNotEmpty() }
|
||||
|
|
|
@ -28,6 +28,7 @@ sealed class LoginAction : VectorViewModelAction {
|
|||
data class WebLoginSuccess(val credentials: Credentials) : LoginAction()
|
||||
data class InitWith(val loginConfig: LoginConfig) : LoginAction()
|
||||
data class ResetPassword(val email: String, val newPassword: String) : LoginAction()
|
||||
object ResetPasswordMailConfirmed : LoginAction()
|
||||
|
||||
// Register actions
|
||||
open class RegisterAction : LoginAction()
|
||||
|
|
|
@ -72,29 +72,35 @@ class LoginActivity : VectorBaseActivity(), ToolbarConfigurable {
|
|||
loginSharedActionViewModel = viewModelProvider.get(LoginSharedActionViewModel::class.java)
|
||||
loginSharedActionViewModel.observe()
|
||||
.subscribe {
|
||||
when (it) {
|
||||
is LoginNavigation.OpenServerSelection -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginServerSelectionFragment::class.java,
|
||||
// Assigning to dummy make sure we do not forget a case
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val dummy = when (it) {
|
||||
is LoginNavigation.OpenServerSelection -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginServerSelectionFragment::class.java,
|
||||
option = { ft ->
|
||||
val view = findViewById<View?>(R.id.loginSplashLogo)
|
||||
if (view != null) {
|
||||
ft.addSharedElement(view, ViewCompat.getTransitionName(view) ?: "")
|
||||
}
|
||||
})
|
||||
is LoginNavigation.OnServerSelectionDone -> onServerSelectionDone()
|
||||
is LoginNavigation.OnSignModeSelected -> onSignModeSelected()
|
||||
is LoginNavigation.OnLoginFlowRetrieved -> onLoginFlowRetrieved()
|
||||
is LoginNavigation.OnWebLoginError -> onWebLoginError(it)
|
||||
is LoginNavigation.OnForgetPasswordClicked -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginResetPasswordFragment::class.java)
|
||||
is LoginNavigation.OnResetPasswordSuccess -> {
|
||||
is LoginNavigation.OnServerSelectionDone -> onServerSelectionDone()
|
||||
is LoginNavigation.OnSignModeSelected -> onSignModeSelected()
|
||||
is LoginNavigation.OnLoginFlowRetrieved -> onLoginFlowRetrieved()
|
||||
is LoginNavigation.OnWebLoginError -> onWebLoginError(it)
|
||||
is LoginNavigation.OnForgetPasswordClicked -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginResetPasswordFragment::class.java)
|
||||
is LoginNavigation.OnResetPasswordSendThreePidDone -> {
|
||||
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
addFragmentToBackstack(R.id.loginFragmentContainer, LoginResetPasswordMailConfirmationFragment::class.java)
|
||||
}
|
||||
is LoginNavigation.OnResetPasswordMailConfirmationSuccess -> {
|
||||
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
addFragmentToBackstack(R.id.loginFragmentContainer, LoginResetPasswordSuccessFragment::class.java)
|
||||
}
|
||||
is LoginNavigation.OnResetPasswordSuccessDone -> supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
is LoginNavigation.OnSendEmailSuccess -> addFragmentToBackstack(R.id.loginFragmentContainer,
|
||||
is LoginNavigation.OnResetPasswordMailConfirmationSuccessDone -> supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
is LoginNavigation.OnSendEmailSuccess -> addFragmentToBackstack(R.id.loginFragmentContainer,
|
||||
LoginWaitForEmailFragment::class.java,
|
||||
LoginWaitForEmailFragmentArgument(it.email),
|
||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG)
|
||||
is LoginNavigation.OnSendMsisdnSuccess -> addFragmentToBackstack(R.id.loginFragmentContainer,
|
||||
is LoginNavigation.OnSendMsisdnSuccess -> addFragmentToBackstack(R.id.loginFragmentContainer,
|
||||
LoginGenericTextInputFormFragment::class.java,
|
||||
LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, it.msisdn),
|
||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG)
|
||||
|
|
|
@ -25,8 +25,10 @@ sealed class LoginNavigation : VectorSharedAction {
|
|||
object OnLoginFlowRetrieved : LoginNavigation()
|
||||
object OnSignModeSelected : LoginNavigation()
|
||||
object OnForgetPasswordClicked : LoginNavigation()
|
||||
object OnResetPasswordSuccess : LoginNavigation()
|
||||
object OnResetPasswordSuccessDone : LoginNavigation()
|
||||
object OnResetPasswordSendThreePidDone : LoginNavigation()
|
||||
object OnResetPasswordMailConfirmationSuccess : LoginNavigation()
|
||||
object OnResetPasswordMailConfirmationSuccessDone : LoginNavigation()
|
||||
|
||||
data class OnSendEmailSuccess(val email: String) : LoginNavigation()
|
||||
data class OnSendMsisdnSuccess(val msisdn: String) : LoginNavigation()
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ import io.reactivex.rxkotlin.subscribeBy
|
|||
import kotlinx.android.synthetic.main.fragment_login.passwordField
|
||||
import kotlinx.android.synthetic.main.fragment_login.passwordFieldTil
|
||||
import kotlinx.android.synthetic.main.fragment_login.passwordReveal
|
||||
import kotlinx.android.synthetic.main.fragment_login_generic_text_input_form.*
|
||||
import kotlinx.android.synthetic.main.fragment_login_reset_password.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -49,6 +48,9 @@ class LoginResetPasswordFragment @Inject constructor(
|
|||
|
||||
private var passwordShown = false
|
||||
|
||||
// Show warning only once
|
||||
private var showWarning = true
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_login_reset_password
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -84,6 +86,23 @@ class LoginResetPasswordFragment @Inject constructor(
|
|||
fun submit() {
|
||||
cleanupUi()
|
||||
|
||||
if (showWarning) {
|
||||
showWarning = false
|
||||
// Display a warning as Riot-Web does first
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.login_reset_password_warning_title)
|
||||
.setMessage(R.string.login_reset_password_warning_content)
|
||||
.setPositiveButton(R.string.login_reset_password_warning_submit) { _, _ ->
|
||||
doSubmit()
|
||||
}
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show()
|
||||
} else {
|
||||
doSubmit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun doSubmit() {
|
||||
val email = resetPasswordEmail.text.toString()
|
||||
val password = passwordField.text.toString()
|
||||
|
||||
|
@ -141,11 +160,10 @@ class LoginResetPasswordFragment @Inject constructor(
|
|||
renderPasswordField()
|
||||
}
|
||||
is Fail -> {
|
||||
resetPasswordEmailTil.error = " "
|
||||
passwordFieldTil.error = errorFormatter.toHumanReadable(state.asyncResetPassword.error)
|
||||
resetPasswordEmailTil.error = errorFormatter.toHumanReadable(state.asyncResetPassword.error)
|
||||
}
|
||||
is Success -> {
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordSuccess)
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordSendThreePidDone)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.login
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import butterknife.OnClick
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.error.ErrorFormatter
|
||||
import kotlinx.android.synthetic.main.fragment_login_reset_password_success.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* In this screen, the user is asked to check his email and to click on a button once it's done
|
||||
*/
|
||||
class LoginResetPasswordMailConfirmationFragment @Inject constructor(
|
||||
private val errorFormatter: ErrorFormatter
|
||||
) : AbstractLoginFragment() {
|
||||
|
||||
override fun getLayoutResId() = R.layout.fragment_login_reset_password_mail_confirmation
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupUi()
|
||||
}
|
||||
|
||||
private fun setupUi() {
|
||||
resetPasswordSuccessNotice.text = getString(R.string.login_reset_password_mail_confirmation_notice, loginViewModel.resetPasswordEmail)
|
||||
}
|
||||
|
||||
@OnClick(R.id.resetPasswordMailConfirmationSubmit)
|
||||
fun submit() {
|
||||
loginViewModel.handle(LoginAction.ResetPasswordMailConfirmed)
|
||||
}
|
||||
|
||||
override fun onRegistrationError(throwable: Throwable) {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun resetViewModel() {
|
||||
loginViewModel.handle(LoginAction.ResetResetPassword)
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(loginViewModel) { state ->
|
||||
when (state.asyncResetMailConfirmed) {
|
||||
is Fail -> {
|
||||
// Link in email not yet clicked ?
|
||||
AlertDialog.Builder(requireActivity())
|
||||
.setTitle(R.string.dialog_title_error)
|
||||
.setMessage(errorFormatter.toHumanReadable(state.asyncResetMailConfirmed.error))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
is Success -> {
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordMailConfirmationSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
Unit
|
||||
}
|
||||
}
|
|
@ -16,13 +16,10 @@
|
|||
|
||||
package im.vector.riotx.features.login
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import butterknife.OnClick
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.core.error.ErrorFormatter
|
||||
import kotlinx.android.synthetic.main.fragment_login_reset_password_success.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
@ -34,19 +31,9 @@ class LoginResetPasswordSuccessFragment @Inject constructor(
|
|||
|
||||
override fun getLayoutResId() = R.layout.fragment_login_reset_password_success
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupUi()
|
||||
}
|
||||
|
||||
private fun setupUi() {
|
||||
resetPasswordSuccessNotice.text = getString(R.string.login_reset_password_success_notice, loginViewModel.resetPasswordEmail)
|
||||
}
|
||||
|
||||
@OnClick(R.id.resetPasswordSuccessSubmit)
|
||||
fun submit() {
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordSuccessDone)
|
||||
loginSharedActionViewModel.post(LoginNavigation.OnResetPasswordMailConfirmationSuccessDone)
|
||||
}
|
||||
|
||||
override fun onRegistrationError(throwable: Throwable) {
|
||||
|
|
|
@ -92,15 +92,16 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
|
||||
override fun handle(action: LoginAction) {
|
||||
when (action) {
|
||||
is LoginAction.UpdateServerType -> handleUpdateServerType(action)
|
||||
is LoginAction.UpdateSignMode -> handleUpdateSignMode(action)
|
||||
is LoginAction.InitWith -> handleInitWith(action)
|
||||
is LoginAction.UpdateHomeServer -> handleUpdateHomeserver(action)
|
||||
is LoginAction.Login -> handleLogin(action)
|
||||
is LoginAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||
is LoginAction.ResetPassword -> handleResetPassword(action)
|
||||
is LoginAction.RegisterAction -> handleRegisterAction(action)
|
||||
is LoginAction.ResetAction -> handleResetAction(action)
|
||||
is LoginAction.UpdateServerType -> handleUpdateServerType(action)
|
||||
is LoginAction.UpdateSignMode -> handleUpdateSignMode(action)
|
||||
is LoginAction.InitWith -> handleInitWith(action)
|
||||
is LoginAction.UpdateHomeServer -> handleUpdateHomeserver(action)
|
||||
is LoginAction.Login -> handleLogin(action)
|
||||
is LoginAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||
is LoginAction.ResetPassword -> handleResetPassword(action)
|
||||
is LoginAction.ResetPasswordMailConfirmed -> handleResetPasswordMailConfirmed()
|
||||
is LoginAction.RegisterAction -> handleRegisterAction(action)
|
||||
is LoginAction.ResetAction -> handleResetAction(action)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,7 +281,8 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
resetPasswordEmail = null
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Uninitialized
|
||||
asyncResetPassword = Uninitialized,
|
||||
asyncResetMailConfirmed = Uninitialized
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -342,6 +344,43 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleResetPasswordMailConfirmed() {
|
||||
val homeServerConnectionConfigFinal = homeServerConnectionConfig
|
||||
|
||||
if (homeServerConnectionConfigFinal == null) {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetMailConfirmed = Fail(Throwable("Bad configuration"))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetMailConfirmed = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
currentTask = authenticator.resetPasswordMailConfirmed(homeServerConnectionConfigFinal, object : MatrixCallback<Unit> {
|
||||
override fun onSuccess(data: Unit) {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetMailConfirmed = Success(data)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
// TODO Handled JobCancellationException
|
||||
setState {
|
||||
copy(
|
||||
asyncResetMailConfirmed = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLogin(action: LoginAction.Login) {
|
||||
val homeServerConnectionConfigFinal = homeServerConnectionConfig
|
||||
|
||||
|
|
|
@ -22,14 +22,15 @@ data class LoginViewState(
|
|||
val asyncLoginAction: Async<Unit> = Uninitialized,
|
||||
val asyncHomeServerLoginFlowRequest: Async<LoginMode> = Uninitialized,
|
||||
val asyncResetPassword: Async<Unit> = Uninitialized,
|
||||
val asyncResetMailConfirmed: Async<Unit> = Uninitialized,
|
||||
val asyncRegistration: Async<Unit> = Uninitialized
|
||||
) : MvRxState {
|
||||
|
||||
fun isLoading(): Boolean {
|
||||
// TODO Add other async here
|
||||
return asyncLoginAction is Loading
|
||||
|| asyncHomeServerLoginFlowRequest is Loading
|
||||
|| asyncResetPassword is Loading
|
||||
|| asyncResetMailConfirmed is Loading
|
||||
|| asyncRegistration is Loading
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView style="@style/LoginLogo" />
|
||||
|
||||
<LinearLayout
|
||||
style="@style/LoginFormContainer"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login_reset_password_mail_confirmation_title"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/resetPasswordMailConfirmationNotice"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Text.Small"
|
||||
tools:text="@string/login_reset_password_mail_confirmation_notice" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:text="@string/login_reset_password_mail_confirmation_notice_2"
|
||||
android:textAppearance="@style/TextAppearance.Vector.Login.Text.Small" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/resetPasswordMailConfirmationSubmit"
|
||||
style="@style/Style.Vector.Login.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:text="@string/login_reset_password_mail_confirmation_submit" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
style="@style/Style.Vector.Login.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="@dimen/layout_vertical_margin"
|
||||
android:text="@string/login_reset_password_success_submit" />
|
||||
|
||||
|
|
|
@ -66,10 +66,22 @@
|
|||
<string name="login_reset_password_submit">Next</string>
|
||||
<string name="login_reset_password_email_hint">Email</string>
|
||||
<string name="login_reset_password_password_hint">New password</string>
|
||||
<string name="login_reset_password_success_title">Check your inbox</string>
|
||||
|
||||
<string name="login_reset_password_warning_title">Warning!</string>
|
||||
<string name="login_reset_password_warning_content">Changing your password will reset any end-to-end encryption keys on all of your devices, making encrypted chat history unreadable. Set up Key Backup or export your room keys from another device before resetting your password.</string>
|
||||
<string name="login_reset_password_warning_submit">Continue</string>
|
||||
|
||||
<string name="login_reset_password_error_not_found">This email is not linked to any account</string>
|
||||
|
||||
<string name="login_reset_password_mail_confirmation_title">Check your inbox</string>
|
||||
<!-- Replaced string is an email -->
|
||||
<string name="login_reset_password_success_notice">A verification email was sent to %1$s.</string>
|
||||
<string name="login_reset_password_success_notice_2">Tap on the link to confirm your new password.</string>
|
||||
<string name="login_reset_password_mail_confirmation_notice">A verification email was sent to %1$s.</string>
|
||||
<string name="login_reset_password_mail_confirmation_notice_2">Tap on the link to confirm your new password. Once you\'ve followed the link it contains, click below.</string>
|
||||
<string name="login_reset_password_mail_confirmation_submit">I have verified my email address</string>
|
||||
|
||||
<string name="login_reset_password_success_title">Success!</string>
|
||||
<string name="login_reset_password_success_notice">Your password has been reset.</string>
|
||||
<string name="login_reset_password_success_notice_2">You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device.</string>
|
||||
<string name="login_reset_password_success_submit">Back to Sign In</string>
|
||||
|
||||
<string name="login_set_email_title">Set email address</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue