Login screens: improve LoginFragment

This commit is contained in:
Benoit Marty 2019-11-14 21:48:06 +01:00
parent c6b0ae63ea
commit d50b690523
4 changed files with 103 additions and 12 deletions

View file

@ -0,0 +1,55 @@
/*
* 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.core.utils
import android.text.Editable
import android.view.ViewGroup
import androidx.core.view.children
import com.google.android.material.textfield.TextInputLayout
import im.vector.riotx.core.platform.SimpleTextWatcher
/**
* Find all TextInputLayout in a ViewGroup and in all its descendants
*/
fun ViewGroup.findAllTextInputLayout(): List<TextInputLayout> {
val res = ArrayList<TextInputLayout>()
children.forEach {
if (it is TextInputLayout) {
res.add(it)
} else if (it is ViewGroup) {
// Recursive call
res.addAll(it.findAllTextInputLayout())
}
}
return res
}
/**
* Add a text change listener to all TextInputEditText to reset error on its TextInputLayout when the text is changed
*/
fun autoResetTextInputLayoutErrors(textInputLayouts: List<TextInputLayout>) {
textInputLayouts.forEach {
it.editText?.addTextChangedListener(object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
// Reset the error
it.error = null
}
})
}
}

View file

@ -18,7 +18,7 @@ package im.vector.riotx.features.login
import android.os.Bundle
import android.view.View
import androidx.transition.TransitionManager
import androidx.core.view.isVisible
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
@ -69,9 +69,14 @@ class LoginFragment @Inject constructor(
isLoginNotEmpty && isPasswordNotEmpty
}
)
.subscribeBy { authenticateButton.isEnabled = it }
.disposeOnDestroyView()
authenticateButton.setOnClickListener { authenticate() }
.subscribeBy {
loginFieldTil.error = null
passwordFieldTil.error = null
loginSubmit.isEnabled = it
}
.disposeOnDestroy()
loginSubmit.setOnClickListener { authenticate() }
}
// // TODO Move to server selection screen
@ -108,7 +113,28 @@ class LoginFragment @Inject constructor(
}
override fun invalidate() = withState(viewModel) { state ->
TransitionManager.beginDelayedTransition(login_fragment)
when (state.serverType) {
ServerType.MatrixOrg -> {
loginServerIcon.isVisible = true
loginServerIcon.setImageResource(R.drawable.ic_logo_matrix_org)
loginTitle.text = getString(R.string.login_connect_to, "matrix.org")
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")
// TODO Remove https://
loginNotice.text = viewModel.getHomeServerUrl()
}
ServerType.Other -> {
loginServerIcon.isVisible = false
loginTitle.text = getString(R.string.login_server_other_title)
// TODO Remove https://
loginNotice.text = viewModel.getHomeServerUrl()
}
}
when (state.asyncLoginAction) {
is Loading -> {
@ -117,8 +143,9 @@ class LoginFragment @Inject constructor(
renderPasswordField()
}
is Fail -> {
// TODO This does not work, we want the error to be on without text. Fix that
loginFieldTil.error = ""
// TODO Handle error text properly
// TODO Reset error when text is changed
passwordFieldTil.error = errorFormatter.toHumanReadable(state.asyncLoginAction.error)
}
// Success is handled by the LoginActivity

View file

@ -152,6 +152,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi
}
override fun onFailure(failure: Throwable) {
// TODO Handled JobCancellationException
setState {
copy(
asyncLoginAction = Fail(failure)

View file

@ -25,24 +25,32 @@
style="@style/LoginTopIcon"
android:layout_gravity="center_horizontal" />
<TextView
android:id="@+id/loginServerTitle"
<ImageView
android:id="@+id/loginServerIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="84dp"
tools:src="@drawable/ic_logo_matrix_org" />
<TextView
android:id="@+id/loginTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_vertical_margin"
android:textAppearance="@style/TextAppearance.Vector.Login.Title"
tools:text="@string/login_signin_to" />
<TextView
android:id="@+id/loginServerChoiceMatrixOrgText"
android:id="@+id/loginNotice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_vertical_margin"
android:gravity="start"
android:text="@string/login_server_matrix_org_text"
android:textAppearance="@style/TextAppearance.Vector.Login.Text.Small" />
android:textAppearance="@style/TextAppearance.Vector.Login.Text.Small"
tools:text="@string/login_server_matrix_org_text" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginFieldTil"
style="@style/VectorTextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -116,7 +124,7 @@
android:text="@string/auth_forgot_password" />
<com.google.android.material.button.MaterialButton
android:id="@+id/authenticateButton"
android:id="@+id/loginSubmit"
style="@style/Style.Vector.Login.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"