Login screens: Registration WIP

This commit is contained in:
Benoit Marty 2019-11-18 17:39:51 +01:00
parent 08ea3d049e
commit 41ac2c6d70
22 changed files with 284 additions and 47 deletions

View File

@ -23,6 +23,7 @@ import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.registration.RegistrationService
import im.vector.matrix.android.internal.SessionManager
import im.vector.matrix.android.internal.di.DaggerMatrixComponent
import im.vector.matrix.android.internal.network.UserAgentHolder
@ -47,6 +48,7 @@ data class MatrixConfiguration(
class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
@Inject internal lateinit var authenticator: Authenticator
@Inject internal lateinit var registrationService: RegistrationService
@Inject internal lateinit var userAgentHolder: UserAgentHolder
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
@Inject internal lateinit var olmManager: OlmManager
@ -68,6 +70,10 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
return authenticator
}
fun registrationService(): RegistrationService {
return registrationService
}
companion object {
private lateinit var instance: Matrix

View File

@ -0,0 +1,31 @@
/*
* 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.api.auth.registration
import im.vector.matrix.android.api.session.Session
// Either a session or an object containing data about registration stages
sealed class RegistrationResult {
data class Success(val session: Session) : RegistrationResult()
data class FlowResponse(val flowResult: FlowResult) : RegistrationResult()
}
data class FlowResult(
val missingStages: List<Stage>,
val completedStages: List<Stage>
)

View File

@ -17,14 +17,15 @@
package im.vector.matrix.android.api.auth.registration
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.Cancelable
interface RegistrationWizard {
fun createAccount(userName: String, password: String, initialDeviceDisplayName: String?, callback: MatrixCallback<Session>): Cancelable
fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable
fun performReCaptcha(response: String, callback: MatrixCallback<Session>): Cancelable
fun createAccount(userName: String, password: String, initialDeviceDisplayName: String?, callback: MatrixCallback<RegistrationResult>): Cancelable
fun performReCaptcha(response: String, callback: MatrixCallback<RegistrationResult>): Cancelable
// TODO Add other method here
}

View File

@ -16,32 +16,29 @@
package im.vector.matrix.android.api.auth.registration
import im.vector.matrix.android.api.util.JsonDict
sealed class Stage(open val mandatory: Boolean) {
// m.login.password
data class Password(override val mandatory: Boolean, val publicKey: String) : Stage(mandatory)
// m.login.recaptcha
data class ReCaptcha(override val mandatory: Boolean, val publicKey: String) : Stage(mandatory)
// m.login.oauth2
// m.login.email.identity
data class Email(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
data class Email(override val mandatory: Boolean) : Stage(mandatory)
// m.login.msisdn
data class Msisdn(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
data class Msisdn(override val mandatory: Boolean) : Stage(mandatory)
// m.login.token
// m.login.dummy
object Dummy : Stage(false)
// Undocumented yet: m.login.terms
data class Terms(override val mandatory: Boolean, val policies: TermPolicies) : Stage(mandatory)
// TODO SSO
// For unknown stages
data class Other(override val mandatory: Boolean, val type: String, val params: JsonDict?) : Stage(mandatory)
data class Other(override val mandatory: Boolean, val type: String, val params: Map<*, *>?) : Stage(mandatory)
}
//TODO
class TermPolicies

View File

@ -36,8 +36,8 @@ internal interface AuthAPI {
* Register to the homeserver
* Ref: https://matrix.org/docs/spec/client_server/latest#account-registration-and-management
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
fun register(registrationParams: RegistrationParams): Call<Credentials>
@POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "register")
fun register(@Body registrationParams: RegistrationParams): Call<Credentials>
/**
* Get the supported login flow

View File

@ -21,8 +21,10 @@ import dagger.Binds
import dagger.Module
import dagger.Provides
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.registration.RegistrationService
import im.vector.matrix.android.internal.auth.db.AuthRealmModule
import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore
import im.vector.matrix.android.internal.auth.registration.DefaultRegistrationService
import im.vector.matrix.android.internal.database.RealmKeysUtils
import im.vector.matrix.android.internal.di.AuthDatabase
import io.realm.RealmConfiguration
@ -60,4 +62,7 @@ internal abstract class AuthModule {
@Binds
abstract fun bindAuthenticator(authenticator: DefaultAuthenticator): Authenticator
@Binds
abstract fun bindRegistrationService(service: DefaultRegistrationService): RegistrationService
}

View File

@ -16,6 +16,7 @@
package im.vector.matrix.android.internal.auth.data
// TODO Move to [InteractiveAuthenticationFlow]
object LoginFlowTypes {
const val PASSWORD = "m.login.password"
const val OAUTH2 = "m.login.oauth2"
@ -25,4 +26,5 @@ object LoginFlowTypes {
const val MSISDN = "m.login.msisdn"
const val RECAPTCHA = "m.login.recaptcha"
const val DUMMY = "m.login.dummy"
const val TERMS = "m.login.terms"
}

View File

@ -26,14 +26,14 @@ import im.vector.matrix.android.internal.di.Unauthenticated
import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import okhttp3.OkHttpClient
import javax.inject.Inject
// TODO Add @Inject
internal class DefaultRegistrationService(@Unauthenticated
private val okHttpClient: Lazy<OkHttpClient>,
private val retrofitFactory: RetrofitFactory,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val sessionParamsStore: SessionParamsStore,
private val sessionManager: SessionManager) : RegistrationService {
internal class DefaultRegistrationService @Inject constructor(@Unauthenticated
private val okHttpClient: Lazy<OkHttpClient>,
private val retrofitFactory: RetrofitFactory,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val sessionParamsStore: SessionParamsStore,
private val sessionManager: SessionManager) : RegistrationService {
override fun getOrCreateRegistrationWizard(homeServerConnectionConfig: HomeServerConnectionConfig): RegistrationWizard {
// TODO Persist the wizard?

View File

@ -20,9 +20,9 @@ import dagger.Lazy
import im.vector.matrix.android.api.MatrixCallback
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.RegistrationResult
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
import im.vector.matrix.android.api.failure.Failure
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
@ -47,10 +47,14 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
private val authAPI = buildAuthAPI()
private val registerTask = DefaultRegisterTask(authAPI)
override fun getRegistrationFlow(callback: MatrixCallback<RegistrationResult>): Cancelable {
return performRegistrationRequest(RegistrationParams(), callback)
}
override fun createAccount(userName: String,
password: String,
initialDeviceDisplayName: String?,
callback: MatrixCallback<Session>): Cancelable {
callback: MatrixCallback<RegistrationResult>): Cancelable {
return performRegistrationRequest(RegistrationParams(
username = userName,
password = password,
@ -58,7 +62,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
), callback)
}
override fun performReCaptcha(response: String, callback: MatrixCallback<Session>): Cancelable {
override fun performReCaptcha(response: String, callback: MatrixCallback<RegistrationResult>): Cancelable {
val safeSession = currentSession ?: run {
callback.onFailure(IllegalStateException("developer error, call createAccount() method first"))
return NoOpCancellable
@ -72,7 +76,7 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
), callback)
}
private fun performRegistrationRequest(registrationParams: RegistrationParams, callback: MatrixCallback<Session>): Cancelable {
private fun performRegistrationRequest(registrationParams: RegistrationParams, callback: MatrixCallback<RegistrationResult>): Cancelable {
val job = GlobalScope.launch(coroutineDispatchers.main) {
val result = runCatching {
registerTask.execute(RegisterTask.Params(registrationParams))
@ -83,13 +87,15 @@ internal class DefaultRegistrationWizard(private val homeServerConnectionConfig:
sessionParamsStore.save(sessionParams)
val session = sessionManager.getOrCreateSession(sessionParams)
callback.onSuccess(session)
callback.onSuccess(RegistrationResult.Success(session))
},
{
if (it is Failure.RegistrationFlowError) {
currentSession = it.registrationFlowResponse.session
callback.onSuccess(RegistrationResult.FlowResponse(it.registrationFlowResponse.toFlowResult()))
} else {
callback.onFailure(it)
}
callback.onFailure(it)
}
)
}

View File

@ -18,8 +18,12 @@ package im.vector.matrix.android.internal.auth.registration
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.auth.registration.FlowResult
import im.vector.matrix.android.api.auth.registration.Stage
import im.vector.matrix.android.api.auth.registration.TermPolicies
import im.vector.matrix.android.api.util.JsonDict
import im.vector.matrix.android.internal.auth.data.InteractiveAuthenticationFlow
import im.vector.matrix.android.internal.auth.data.LoginFlowTypes
@JsonClass(generateAdapter = true)
data class RegistrationFlowResponse(
@ -51,3 +55,39 @@ data class RegistrationFlowResponse(
@Json(name = "params")
var params: JsonDict? = null
)
/**
* Convert to something easier to exploit on client side
*/
fun RegistrationFlowResponse.toFlowResult(): FlowResult {
// Get all the returned stages
val allFlowTypes = mutableSetOf<String>()
val missingStage = mutableListOf<Stage>()
val completedStage = mutableListOf<Stage>()
this.flows?.forEach { it.stages?.mapTo(allFlowTypes) { type -> type } }
allFlowTypes.forEach { type ->
val isMandatory = flows?.all { type in it.stages ?: emptyList() } == true
val stage = when (type) {
LoginFlowTypes.RECAPTCHA -> Stage.ReCaptcha(isMandatory, ((params?.get(type) as? Map<*, *>)?.get("public_key") as? String)
?: "")
LoginFlowTypes.DUMMY -> Stage.Dummy
LoginFlowTypes.TERMS -> Stage.Terms(isMandatory, TermPolicies())
LoginFlowTypes.EMAIL_IDENTITY -> Stage.Email(isMandatory)
LoginFlowTypes.MSISDN -> Stage.Msisdn(isMandatory)
else -> Stage.Other(isMandatory, type, (params?.get(type) as? Map<*, *>))
}
if (type in completedStages ?: emptyList()) {
completedStage.add(stage)
} else {
missingStage.add(stage)
}
}
return FlowResult(missingStage, completedStage)
}

View File

@ -23,6 +23,7 @@ import dagger.BindsInstance
import dagger.Component
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.registration.RegistrationService
import im.vector.matrix.android.internal.SessionManager
import im.vector.matrix.android.internal.auth.AuthModule
import im.vector.matrix.android.internal.auth.SessionParamsStore
@ -46,6 +47,8 @@ internal interface MatrixComponent {
fun authenticator(): Authenticator
fun registrationService(): RegistrationService
fun context(): Context
fun resources(): Resources

View File

@ -32,8 +32,8 @@ import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsB
import im.vector.riotx.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
import im.vector.riotx.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet
import im.vector.riotx.features.home.room.filtered.FilteredRoomsActivity
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.riotx.features.home.room.list.RoomListModule
import im.vector.riotx.features.home.room.list.actions.RoomListQuickActionsBottomSheet
import im.vector.riotx.features.invite.VectorInviteView
import im.vector.riotx.features.link.LinkHandlerActivity
import im.vector.riotx.features.login.LoginActivity
@ -47,7 +47,7 @@ import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
import im.vector.riotx.features.reactions.widget.ReactionButton
import im.vector.riotx.features.roomdirectory.RoomDirectoryActivity
import im.vector.riotx.features.roomdirectory.createroom.CreateRoomActivity
import im.vector.riotx.features.settings.*
import im.vector.riotx.features.settings.VectorSettingsActivity
import im.vector.riotx.features.share.IncomingShareActivity
import im.vector.riotx.features.ui.UiStateRepository

View File

@ -22,6 +22,7 @@ import dagger.BindsInstance
import dagger.Component
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.registration.RegistrationService
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.ActiveSessionDataSource
import im.vector.riotx.EmojiCompatFontProvider
@ -99,6 +100,8 @@ interface VectorComponent {
fun authenticator(): Authenticator
fun registrationService(): RegistrationService
fun bugReporter(): BugReporter
fun vectorUncaughtExceptionHandler(): VectorUncaughtExceptionHandler

View File

@ -25,6 +25,7 @@ import dagger.Module
import dagger.Provides
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.registration.RegistrationService
import im.vector.matrix.android.api.session.Session
import im.vector.riotx.features.navigation.DefaultNavigator
import im.vector.riotx.features.navigation.Navigator
@ -67,6 +68,12 @@ abstract class VectorModule {
fun providesAuthenticator(matrix: Matrix): Authenticator {
return matrix.authenticator()
}
@Provides
@JvmStatic
fun providesRegistrationService(matrix: Matrix): RegistrationService {
return matrix.registrationService()
}
}
@Binds

View File

@ -28,6 +28,15 @@ sealed class LoginAction : VectorViewModelAction {
data class InitWith(val loginConfig: LoginConfig) : LoginAction()
data class ResetPassword(val email: String, val newPassword: String) : LoginAction()
// Register actions
open class RegisterAction : LoginAction()
data class RegisterWith(val username: String, val password: String) : RegisterAction()
data class AddEmail(val email: String) : RegisterAction()
data class AddMsisdn(val msisdn: String) : RegisterAction()
data class ConfirmMsisdn(val code: String) : RegisterAction()
data class PerformCaptcha(val captcha: String /* TODO Add other params */) : RegisterAction()
// Reset actions
open class ResetAction : LoginAction()

View File

@ -126,7 +126,7 @@ class LoginActivity : VectorBaseActivity() {
private fun onSignModeSelected() {
when (loginViewModel.signMode) {
SignMode.Unknown -> error("Sign mode has to be set before calling this method")
SignMode.SignUp -> Unit // TODO addFragmentToBackstack(R.id.loginFragmentContainer, SignUpFragment::class.java)
SignMode.SignUp -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginFragment::class.java)
SignMode.SignIn -> {
// It depends on the LoginMode
withState(loginViewModel) {

View File

@ -35,8 +35,11 @@ import kotlinx.android.synthetic.main.fragment_login.*
import javax.inject.Inject
/**
* In this screen, the user is asked for login and password to sign in to a homeserver.
* He also can reset his password
* In this screen, in signin mode:
* - the user is asked for login and password to sign in to a homeserver.
* - He also can reset his password
* In signup mode:
* - the user is asked for login and password
*/
class LoginFragment @Inject constructor(
private val errorFormatter: ErrorFormatter
@ -53,38 +56,61 @@ class LoginFragment @Inject constructor(
setupUi()
setupSubmitButton()
setupPasswordReveal()
setupButtons()
}
private fun authenticate() {
@OnClick(R.id.loginSubmit)
fun submit() {
val login = loginField.text?.trim().toString()
val password = passwordField.text?.trim().toString()
loginViewModel.handle(LoginAction.Login(login, password))
when (loginViewModel.signMode) {
SignMode.Unknown -> error("developer error")
SignMode.SignUp -> loginViewModel.handle(LoginAction.RegisterWith(login, password))
SignMode.SignIn -> loginViewModel.handle(LoginAction.Login(login, password))
}
}
private fun setupUi() {
val resId = when (loginViewModel.signMode) {
SignMode.Unknown -> error("developer error")
SignMode.SignUp -> R.string.login_signup_to
SignMode.SignIn -> R.string.login_connect_to
}
when (loginViewModel.serverType) {
ServerType.MatrixOrg -> {
loginServerIcon.isVisible = true
loginServerIcon.setImageResource(R.drawable.ic_logo_matrix_org)
loginTitle.text = getString(R.string.login_connect_to, loginViewModel.getHomeServerUrlSimple())
loginTitle.text = getString(resId, loginViewModel.getHomeServerUrlSimple())
loginNotice.text = getString(R.string.login_server_matrix_org_text)
}
ServerType.Modular -> {
loginServerIcon.isVisible = true
loginServerIcon.setImageResource(R.drawable.ic_logo_modular)
// TODO
loginTitle.text = getString(R.string.login_connect_to, "TODO")
loginTitle.text = getString(resId, "TODO")
loginNotice.text = loginViewModel.getHomeServerUrlSimple()
}
ServerType.Other -> {
loginServerIcon.isVisible = false
loginTitle.text = getString(R.string.login_server_other_title)
loginNotice.text = getString(R.string.login_connect_to, loginViewModel.getHomeServerUrlSimple())
loginNotice.text = getString(resId, loginViewModel.getHomeServerUrlSimple())
}
}
}
private fun setupButtons() {
forgetPasswordButton.isVisible = loginViewModel.signMode == SignMode.SignIn
loginSubmit.text = getString(when (loginViewModel.signMode) {
SignMode.Unknown -> error("developer error")
SignMode.SignUp -> R.string.login_signup_submit
SignMode.SignIn -> R.string.login_signin
})
}
private fun setupSubmitButton() {
Observable
.combineLatest(
@ -100,8 +126,6 @@ class LoginFragment @Inject constructor(
loginSubmit.isEnabled = it
}
.disposeOnDestroyView()
loginSubmit.setOnClickListener { authenticate() }
}
@OnClick(R.id.forgetPasswordButton)

View File

@ -20,6 +20,12 @@ import android.os.Bundle
import android.view.View
import androidx.core.view.isVisible
import butterknife.OnClick
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.auth.registration.FlowResult
import im.vector.matrix.android.api.auth.registration.RegistrationResult
import im.vector.matrix.android.api.auth.registration.Stage
import im.vector.riotx.R
import kotlinx.android.synthetic.main.fragment_login_signup_signin_selection.*
import javax.inject.Inject
@ -63,7 +69,6 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractLoginFr
@OnClick(R.id.loginSignupSigninSignUp)
fun signUp() {
loginViewModel.handle(LoginAction.UpdateSignMode(SignMode.SignUp))
loginSharedActionViewModel.post(LoginNavigation.OnSignModeSelected)
}
@OnClick(R.id.loginSignupSigninSignIn)
@ -75,4 +80,35 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractLoginFr
override fun resetViewModel() {
loginViewModel.handle(LoginAction.ResetSignMode)
}
override fun invalidate() = withState(loginViewModel) {
when (it.asyncRegistration) {
is Success -> {
when (val res = it.asyncRegistration()) {
is RegistrationResult.Success ->
// Should not happen
Unit
is RegistrationResult.FlowResponse -> handleFlowResult(res.flowResult)
}
}
is Fail -> {
// TODO Registration disabled, etc
when (it.asyncRegistration.error) {
}
}
}
}
private fun handleFlowResult(flowResult: FlowResult) {
// Check that all flows are supported by the application
if (flowResult.missingStages.any { it is Stage.Other }) {
// Display a popup to propose use web fallback
// TODO
} else {
// Go on with registration flow
loginSharedActionViewModel.post(LoginNavigation.OnSignModeSelected)
}
}
}

View File

@ -23,6 +23,10 @@ import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.auth.registration.FlowResult
import im.vector.matrix.android.api.auth.registration.RegistrationResult
import im.vector.matrix.android.api.auth.registration.RegistrationService
import im.vector.matrix.android.api.auth.registration.RegistrationWizard
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.android.internal.auth.data.InteractiveAuthenticationFlow
@ -36,6 +40,7 @@ import timber.log.Timber
class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginViewState,
private val authenticator: Authenticator,
private val registrationService: RegistrationService,
private val activeSessionHolder: ActiveSessionHolder,
private val pushRuleTriggerListener: PushRuleTriggerListener,
private val sessionListener: SessionListener)
@ -55,6 +60,8 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
}
}
private var registrationWizard: RegistrationWizard? = null
var serverType: ServerType = ServerType.MatrixOrg
private set
var signMode: SignMode = SignMode.Unknown
@ -89,7 +96,8 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
LoginAction.ResetLogin -> {
setState {
copy(
asyncLoginAction = Uninitialized
asyncLoginAction = Uninitialized,
asyncRegistration = Uninitialized
)
}
}
@ -124,6 +132,10 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
private fun handleUpdateSignMode(action: LoginAction.UpdateSignMode) {
signMode = action.signMode
if (signMode == SignMode.SignUp) {
startRegistrationFlow()
}
}
private fun handleUpdateServerType(action: LoginAction.UpdateServerType) {
@ -206,6 +218,53 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
}
}
private fun startRegistrationFlow() {
val homeServerConnectionConfigFinal = homeServerConnectionConfig
if (homeServerConnectionConfigFinal == null) {
setState {
copy(
asyncRegistration = Fail(Throwable("Bad configuration"))
)
}
} else {
setState {
copy(
asyncRegistration = Loading()
)
}
registrationWizard = registrationService.getOrCreateRegistrationWizard(homeServerConnectionConfigFinal)
currentTask = registrationWizard?.getRegistrationFlow(object : MatrixCallback<RegistrationResult> {
override fun onSuccess(data: RegistrationResult) {
when (data) {
is RegistrationResult.Success -> onSessionCreated(data.session)
is RegistrationResult.FlowResponse -> onFlowResponse(data.flowResult)
}
}
override fun onFailure(failure: Throwable) {
// TODO Handled JobCancellationException
setState {
copy(
asyncRegistration = Fail(failure)
)
}
}
})
}
}
private fun onFlowResponse(flowResult: FlowResult) {
setState {
copy(
asyncRegistration = Success(RegistrationResult.FlowResponse(flowResult))
)
}
}
private fun onSessionCreated(session: Session) {
activeSessionHolder.setActiveSession(session)
session.configureAndStart(pushRuleTriggerListener, sessionListener)
@ -246,7 +305,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
// Do not retry if we already have flows for this config -> causes infinite focus loop
if (newConfig?.homeServerUri?.toString() == homeServerConnectionConfig?.homeServerUri?.toString()
&& state.asyncHomeServerLoginFlowRequest is Success) return@withState
&& state.asyncHomeServerLoginFlowRequest is Success) return@withState
currentTask?.cancel()
currentTask = null

View File

@ -17,11 +17,13 @@
package im.vector.riotx.features.login
import com.airbnb.mvrx.*
import im.vector.matrix.android.api.auth.registration.RegistrationResult
data class LoginViewState(
val asyncLoginAction: Async<Unit> = Uninitialized,
val asyncHomeServerLoginFlowRequest: Async<LoginMode> = Uninitialized,
val asyncResetPassword: Async<Unit> = Uninitialized
val asyncResetPassword: Async<Unit> = Uninitialized,
val asyncRegistration: Async<RegistrationResult> = Uninitialized
) : MvRxState {
fun isLoading(): Boolean {
@ -29,10 +31,10 @@ data class LoginViewState(
return asyncLoginAction is Loading
|| asyncHomeServerLoginFlowRequest is Loading
|| asyncResetPassword is Loading
|| asyncRegistration is Loading
}
fun isUserLogged(): Boolean {
// TODO Add other async here
return asyncLoginAction is Success
}
}

View File

@ -58,7 +58,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:hint="@string/auth_user_name_placeholder"
android:hint="@string/login_signup_username_hint"
app:errorEnabled="true">
<com.google.android.material.textfield.TextInputEditText
@ -81,7 +81,7 @@
style="@style/VectorTextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/auth_password_placeholder"
android:hint="@string/login_signup_password_hint"
app:errorEnabled="true"
app:errorIconDrawable="@null">

View File

@ -88,4 +88,10 @@
<string name="login_msisdn_confirm_send_again">Send again</string>
<string name="login_msisdn_confirm_submit">Next</string>
<!-- Replaced string is the homeserver url -->
<string name="login_signup_to">Sign up to %1$s</string>
<string name="login_signup_username_hint">Username</string>
<string name="login_signup_password_hint">Password</string>
<string name="login_signup_submit">Next</string>
</resources>