Skip to content

Commit

Permalink
Merge pull request #5648 from vector-im/feature/adm/ftue-sign-up
Browse files Browse the repository at this point in the history
FTUE - Combined sign up + server selection screen
  • Loading branch information
ouchadam authored Apr 1, 2022
2 parents 7067c50 + 5120e7a commit 27b727e
Show file tree
Hide file tree
Showing 25 changed files with 632 additions and 28 deletions.
1 change: 1 addition & 0 deletions changelog.d/5277.wip
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adding combined account creation and server selection screen as part of the new FTUE
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
<item name="endIconTint">?vctr_content_secondary</item>
</style>

<style name="Widget.Vector.TextInputLayout.Username">
<item name="endIconMode">clear_text</item>
<item name="endIconTint">?vctr_content_secondary</item>
</style>

<style name="Widget.Vector.TextInputLayout.Form">
<item name="boxStrokeColor">@color/form_edit_text_stroke_color_selector</item>
<item name="android:textColorHint">@color/form_edit_text_hint_color_selector</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ class DebugFeaturesStateFactory @Inject constructor(
key = DebugFeatureKeys.onboardingPersonalize,
factory = VectorFeatures::isOnboardingPersonalizeEnabled
),
createBooleanFeature(
label = "FTUE Combined register",
key = DebugFeatureKeys.onboardingCombinedRegister,
factory = VectorFeatures::isOnboardingCombinedRegisterEnabled
),
createBooleanFeature(
label = "Live location sharing",
key = DebugFeatureKeys.liveLocationSharing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class DebugVectorFeatures(
override fun isOnboardingPersonalizeEnabled(): Boolean = read(DebugFeatureKeys.onboardingPersonalize)
?: vectorFeatures.isOnboardingPersonalizeEnabled()

override fun isOnboardingCombinedRegisterEnabled(): Boolean = read(DebugFeatureKeys.onboardingCombinedRegister)
?: vectorFeatures.isOnboardingCombinedRegisterEnabled()

override fun isLiveLocationEnabled(): Boolean = read(DebugFeatureKeys.liveLocationSharing)
?: vectorFeatures.isLiveLocationEnabled()

Expand Down Expand Up @@ -107,7 +110,8 @@ private fun <T : Enum<T>> enumPreferencesKey(type: KClass<T>) = stringPreference
object DebugFeatureKeys {
val onboardingAlreadyHaveAnAccount = booleanPreferencesKey("onboarding-already-have-an-account")
val onboardingSplashCarousel = booleanPreferencesKey("onboarding-splash-carousel")
val onboardingUseCase = booleanPreferencesKey("onbboarding-splash-carousel")
val onboardingPersonalize = booleanPreferencesKey("onbboarding-personalize")
val onboardingUseCase = booleanPreferencesKey("onboarding-splash-carousel")
val onboardingPersonalize = booleanPreferencesKey("onboarding-personalize")
val onboardingCombinedRegister = booleanPreferencesKey("onboarding-combined-register")
val liveLocationSharing = booleanPreferencesKey("live-location-sharing")
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@

package im.vector.app.core.extensions

import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.children
import androidx.core.view.doOnLayout
import kotlin.math.roundToInt

fun ConstraintLayout.updateConstraintSet(block: (ConstraintSet) -> Unit) {
ConstraintSet().let {
Expand All @@ -26,3 +30,21 @@ fun ConstraintLayout.updateConstraintSet(block: (ConstraintSet) -> Unit) {
it.applyTo(this)
}
}

/**
* Helper to recalculate all ConstraintLayout child views with percentage based height against the parent's height.
* This is helpful when using a ConstraintLayout within a ScrollView as any percentages will use the total scrolling size
* instead of the viewport/ScrollView height
*/
fun ConstraintLayout.realignPercentagesToParent() {
doOnLayout {
val rootHeight = (parent as View).height
children.forEach { child ->
val params = child.layoutParams as ConstraintLayout.LayoutParams
if (params.matchConstraintPercentHeight != 1.0f) {
params.height = (rootHeight * params.matchConstraintPercentHeight).roundToInt()
child.layoutParams = params
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2022 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.app.core.extensions

import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.flow.map
import reactivecircus.flowbinding.android.widget.textChanges

fun TextInputLayout.editText() = this.editText!!

/**
* Detect if a field starts or ends with spaces
*/
fun TextInputLayout.hasSurroundingSpaces() = editText().text.toString().let { it.trim() != it }

fun TextInputLayout.hasContentFlow(mapper: (CharSequence) -> CharSequence = { it }) = editText().textChanges().map { mapper(it).isNotEmpty() }

fun TextInputLayout.content() = editText().text.toString()
2 changes: 2 additions & 0 deletions vector/src/main/java/im/vector/app/features/VectorFeatures.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface VectorFeatures {
fun isOnboardingSplashCarouselEnabled(): Boolean
fun isOnboardingUseCaseEnabled(): Boolean
fun isOnboardingPersonalizeEnabled(): Boolean
fun isOnboardingCombinedRegisterEnabled(): Boolean
fun isLiveLocationEnabled(): Boolean

enum class OnboardingVariant {
Expand All @@ -40,5 +41,6 @@ class DefaultVectorFeatures : VectorFeatures {
override fun isOnboardingSplashCarouselEnabled() = true
override fun isOnboardingUseCaseEnabled() = true
override fun isOnboardingPersonalizeEnabled() = false
override fun isOnboardingCombinedRegisterEnabled() = false
override fun isLiveLocationEnabled(): Boolean = BuildConfig.ENABLE_LIVE_LOCATION_SHARING
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment<FragmentLog
error++
}
if (isSignupMode && isNumericOnlyUserIdForbidden && login.isDigitsOnly()) {
views.loginFieldTil.error = "The homeserver does not accept username with only digits."
views.loginFieldTil.error = getString(R.string.error_forbidden_digits_only_username)
error++
}
if (password.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) :
LinearLayout(context, attrs, defStyle) {

interface InteractionListener {
fun interface InteractionListener {
fun onProviderSelected(id: String?)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ sealed interface OnboardingAction : VectorViewModelAction {

// Login or Register, depending on the signMode
data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
data class Register(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction
object StopEmailValidationCheck : OnboardingAction

data class PostRegisterAction(val registerAction: RegisterAction) : OnboardingAction
Expand All @@ -51,7 +52,7 @@ sealed interface OnboardingAction : VectorViewModelAction {
object ResetHomeServerType : ResetAction
object ResetHomeServerUrl : ResetAction
object ResetSignMode : ResetAction
object ResetLogin : ResetAction
object ResetAuthenticationAttempt : ResetAction
object ResetResetPassword : ResetAction

// Homeserver history
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ sealed class OnboardingViewEvents : VectorViewEvents {

object OpenUseCaseSelection : OnboardingViewEvents()
object OpenServerSelection : OnboardingViewEvents()
object OpenCombinedRegister : OnboardingViewEvents()
data class OnServerSelectionDone(val serverType: ServerType) : OnboardingViewEvents()
object OnLoginFlowRetrieved : OnboardingViewEvents()
data class OnSignModeSelected(val signMode: SignMode) : OnboardingViewEvents()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class OnboardingViewModel @AssistedInject constructor(
is OnboardingAction.InitWith -> handleInitWith(action)
is OnboardingAction.UpdateHomeServer -> handleUpdateHomeserver(action).also { lastAction = action }
is OnboardingAction.LoginOrRegister -> handleLoginOrRegister(action).also { lastAction = action }
is OnboardingAction.Register -> handleRegisterWith(action).also { lastAction = action }
is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
is OnboardingAction.ResetPassword -> handleResetPassword(action)
Expand Down Expand Up @@ -276,7 +277,7 @@ class OnboardingViewModel @AssistedInject constructor(
}
}

private fun handleRegisterWith(action: OnboardingAction.LoginOrRegister) {
private fun handleRegisterWith(action: OnboardingAction.Register) {
reAuthHelper.data = action.password
handleRegisterAction(RegisterAction.CreateAccount(
action.username,
Expand Down Expand Up @@ -312,7 +313,7 @@ class OnboardingViewModel @AssistedInject constructor(
}
}
}
OnboardingAction.ResetSignMode -> {
OnboardingAction.ResetSignMode -> {
setState {
copy(
isLoading = false,
Expand All @@ -322,13 +323,13 @@ class OnboardingViewModel @AssistedInject constructor(
)
}
}
OnboardingAction.ResetLogin -> {
OnboardingAction.ResetAuthenticationAttempt -> {
viewModelScope.launch {
authenticationService.cancelPendingLoginOrRegistration()
setState { copy(isLoading = false) }
}
}
OnboardingAction.ResetResetPassword -> {
OnboardingAction.ResetResetPassword -> {
setState {
copy(
isLoading = false,
Expand Down Expand Up @@ -356,7 +357,13 @@ class OnboardingViewModel @AssistedInject constructor(

private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) {
setState { copy(useCase = action.useCase) }
_viewEvents.post(OnboardingViewEvents.OpenServerSelection)
when (vectorFeatures.isOnboardingCombinedRegisterEnabled()) {
true -> {
handle(OnboardingAction.UpdateHomeServer(matrixOrgUrl))
OnboardingViewEvents.OpenCombinedRegister
}
false -> _viewEvents.post(OnboardingViewEvents.OpenServerSelection)
}
}

private fun resetUseCase() {
Expand Down Expand Up @@ -459,7 +466,7 @@ class OnboardingViewModel @AssistedInject constructor(
when (state.signMode) {
SignMode.Unknown -> error("Developer error, invalid sign mode")
SignMode.SignIn -> handleLogin(action)
SignMode.SignUp -> handleRegisterWith(action)
SignMode.SignUp -> handleRegisterWith(OnboardingAction.Register(action.username, action.password, action.initialDeviceName))
SignMode.SignInWithMatrixId -> handleDirectLogin(action, null)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019 New Vector Ltd
* Copyright 2022 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.
Expand Down Expand Up @@ -191,7 +191,7 @@ class FtueAuthCaptchaFragment @Inject constructor(
}

override fun resetViewModel() {
viewModel.handle(OnboardingAction.ResetLogin)
viewModel.handle(OnboardingAction.ResetAuthenticationAttempt)
}

override fun updateWithState(state: OnboardingViewState) {
Expand Down
Loading

0 comments on commit 27b727e

Please sign in to comment.