mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-22 01:15:54 +03:00
Merge pull request #4657 from vector-im/feature/adm/cloning-login-domain-to-ftue
Cloning the `LoginViewModel` domain to `Onboarding`
This commit is contained in:
commit
355d8ebdc6
17 changed files with 1162 additions and 93 deletions
|
@ -140,7 +140,7 @@ android {
|
|||
buildConfigField "String", "BUILD_NUMBER", "\"${buildNumber}\""
|
||||
resValue "string", "build_number", "\"${buildNumber}\""
|
||||
|
||||
buildConfigField "im.vector.app.features.VectorFeatures.LoginVariant", "LOGIN_VARIANT", "im.vector.app.features.VectorFeatures.LoginVariant.LEGACY"
|
||||
buildConfigField "im.vector.app.features.VectorFeatures.OnboardingVariant", "ONBOARDING_VARIANT", "im.vector.app.features.VectorFeatures.OnboardingVariant.LEGACY"
|
||||
|
||||
buildConfigField "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy", "outboundSessionKeySharingStrategy", "im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy.WhenTyping"
|
||||
|
||||
|
|
|
@ -27,9 +27,9 @@ class DebugFeaturesStateFactory @Inject constructor(
|
|||
fun create(): FeaturesState {
|
||||
return FeaturesState(listOf(
|
||||
createEnumFeature(
|
||||
label = "Login version",
|
||||
selection = debugFeatures.loginVariant(),
|
||||
default = defaultFeatures.loginVariant()
|
||||
label = "Onboarding variant",
|
||||
selection = debugFeatures.onboardingVariant(),
|
||||
default = defaultFeatures.onboardingVariant()
|
||||
)
|
||||
))
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ class DebugVectorFeatures(
|
|||
|
||||
private val dataStore = context.dataStore
|
||||
|
||||
override fun loginVariant(): VectorFeatures.LoginVariant {
|
||||
return readPreferences().getEnum<VectorFeatures.LoginVariant>() ?: vectorFeatures.loginVariant()
|
||||
override fun onboardingVariant(): VectorFeatures.OnboardingVariant {
|
||||
return readPreferences().getEnum<VectorFeatures.OnboardingVariant>() ?: vectorFeatures.onboardingVariant()
|
||||
}
|
||||
|
||||
fun <T : Enum<T>> hasEnumOverride(type: KClass<T>) = readPreferences().containsEnum(type)
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
android:windowSoftInputMode="adjustResize" />
|
||||
|
||||
<activity
|
||||
android:name=".features.ftue.FTUEActivity"
|
||||
android:name=".features.onboarding.OnboardingActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ import im.vector.app.features.login.LoginViewModel
|
|||
import im.vector.app.features.login2.LoginViewModel2
|
||||
import im.vector.app.features.login2.created.AccountCreatedViewModel
|
||||
import im.vector.app.features.matrixto.MatrixToBottomSheetViewModel
|
||||
import im.vector.app.features.onboarding.OnboardingViewModel
|
||||
import im.vector.app.features.poll.create.CreatePollViewModel
|
||||
import im.vector.app.features.rageshake.BugReportViewModel
|
||||
import im.vector.app.features.reactions.EmojiSearchResultViewModel
|
||||
|
@ -453,6 +454,11 @@ interface MavericksViewModelModule {
|
|||
@MavericksViewModelKey(AccountCreatedViewModel::class)
|
||||
fun accountCreatedViewModelFactory(factory: AccountCreatedViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@MavericksViewModelKey(OnboardingViewModel::class)
|
||||
fun ftueViewModelFactory(factory: OnboardingViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@MavericksViewModelKey(LoginViewModel2::class)
|
||||
|
|
|
@ -20,12 +20,12 @@ import im.vector.app.BuildConfig
|
|||
|
||||
interface VectorFeatures {
|
||||
|
||||
fun loginVariant(): LoginVariant
|
||||
fun onboardingVariant(): OnboardingVariant
|
||||
|
||||
enum class LoginVariant {
|
||||
enum class OnboardingVariant {
|
||||
LEGACY,
|
||||
FTUE,
|
||||
FTUE_WIP
|
||||
LOGIN_2,
|
||||
FTUE_AUTH
|
||||
}
|
||||
|
||||
enum class NotificationSettingsVersion {
|
||||
|
@ -35,5 +35,5 @@ interface VectorFeatures {
|
|||
}
|
||||
|
||||
class DefaultVectorFeatures : VectorFeatures {
|
||||
override fun loginVariant(): VectorFeatures.LoginVariant = BuildConfig.LOGIN_VARIANT
|
||||
override fun onboardingVariant(): VectorFeatures.OnboardingVariant = BuildConfig.ONBOARDING_VARIANT
|
||||
}
|
||||
|
|
|
@ -34,12 +34,12 @@ import im.vector.app.core.resources.ColorProvider
|
|||
import im.vector.app.databinding.DialogBaseEditTextBinding
|
||||
import im.vector.app.databinding.FragmentLoginAccountCreatedBinding
|
||||
import im.vector.app.features.displayname.getBestName
|
||||
import im.vector.app.features.ftue.FTUEActivity
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||
import im.vector.app.features.login2.AbstractLoginFragment2
|
||||
import im.vector.app.features.login2.LoginAction2
|
||||
import im.vector.app.features.login2.LoginViewState2
|
||||
import im.vector.app.features.onboarding.OnboardingActivity
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
@ -130,7 +130,7 @@ class AccountCreatedFragment @Inject constructor(
|
|||
|
||||
private fun invalidateState(state: AccountCreatedViewState) {
|
||||
// Ugly hack...
|
||||
(activity as? FTUEActivity)?.setIsLoading(state.isLoading)
|
||||
(activity as? OnboardingActivity)?.setIsLoading(state.isLoading)
|
||||
|
||||
views.loginAccountCreatedSubtitle.text = getString(R.string.login_account_created_subtitle, state.userId)
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ import im.vector.app.core.error.fatalError
|
|||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.utils.toast
|
||||
import im.vector.app.features.VectorFeatures
|
||||
import im.vector.app.features.VectorFeatures.OnboardingVariant
|
||||
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
|
||||
import im.vector.app.features.call.conference.JitsiCallViewModel
|
||||
import im.vector.app.features.call.conference.VectorJitsiActivity
|
||||
|
@ -51,7 +52,6 @@ import im.vector.app.features.crypto.verification.SupportedVerificationMethodsPr
|
|||
import im.vector.app.features.crypto.verification.VerificationBottomSheet
|
||||
import im.vector.app.features.debug.DebugMenuActivity
|
||||
import im.vector.app.features.devtools.RoomDevToolActivity
|
||||
import im.vector.app.features.ftue.FTUEActivity
|
||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||
import im.vector.app.features.home.room.detail.RoomDetailArgs
|
||||
import im.vector.app.features.home.room.detail.search.SearchActivity
|
||||
|
@ -64,6 +64,7 @@ import im.vector.app.features.matrixto.MatrixToBottomSheet
|
|||
import im.vector.app.features.media.AttachmentData
|
||||
import im.vector.app.features.media.BigImageViewerActivity
|
||||
import im.vector.app.features.media.VectorAttachmentViewerActivity
|
||||
import im.vector.app.features.onboarding.OnboardingActivity
|
||||
import im.vector.app.features.pin.PinActivity
|
||||
import im.vector.app.features.pin.PinArgs
|
||||
import im.vector.app.features.pin.PinMode
|
||||
|
@ -111,20 +112,20 @@ class DefaultNavigator @Inject constructor(
|
|||
) : Navigator {
|
||||
|
||||
override fun openLogin(context: Context, loginConfig: LoginConfig?, flags: Int) {
|
||||
val intent = when (features.loginVariant()) {
|
||||
VectorFeatures.LoginVariant.LEGACY -> LoginActivity.newIntent(context, loginConfig)
|
||||
VectorFeatures.LoginVariant.FTUE,
|
||||
VectorFeatures.LoginVariant.FTUE_WIP -> FTUEActivity.newIntent(context, loginConfig)
|
||||
val intent = when (features.onboardingVariant()) {
|
||||
OnboardingVariant.LEGACY -> LoginActivity.newIntent(context, loginConfig)
|
||||
OnboardingVariant.LOGIN_2,
|
||||
OnboardingVariant.FTUE_AUTH -> OnboardingActivity.newIntent(context, loginConfig)
|
||||
}
|
||||
intent.addFlags(flags)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
override fun loginSSORedirect(context: Context, data: Uri?) {
|
||||
val intent = when (features.loginVariant()) {
|
||||
VectorFeatures.LoginVariant.LEGACY -> LoginActivity.redirectIntent(context, data)
|
||||
VectorFeatures.LoginVariant.FTUE,
|
||||
VectorFeatures.LoginVariant.FTUE_WIP -> FTUEActivity.redirectIntent(context, data)
|
||||
val intent = when (features.onboardingVariant()) {
|
||||
OnboardingVariant.LEGACY -> LoginActivity.redirectIntent(context, data)
|
||||
OnboardingVariant.LOGIN_2,
|
||||
OnboardingVariant.FTUE_AUTH -> OnboardingActivity.redirectIntent(context, data)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.ftue
|
||||
package im.vector.app.features.onboarding
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
|
@ -72,12 +72,12 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
|
|||
private const val FRAGMENT_REGISTRATION_STAGE_TAG = "FRAGMENT_REGISTRATION_STAGE_TAG"
|
||||
private const val FRAGMENT_LOGIN_TAG = "FRAGMENT_LOGIN_TAG"
|
||||
|
||||
class FTUEWipVariant(
|
||||
class Login2Variant(
|
||||
private val views: ActivityLoginBinding,
|
||||
private val loginViewModel: LoginViewModel2,
|
||||
private val activity: VectorBaseActivity<ActivityLoginBinding>,
|
||||
private val supportFragmentManager: FragmentManager
|
||||
) : FTUEVariant {
|
||||
) : OnboardingVariant {
|
||||
|
||||
private val enterAnim = R.anim.enter_fade_in
|
||||
private val exitAnim = R.anim.exit_fade_out
|
||||
|
@ -112,7 +112,7 @@ class FTUEWipVariant(
|
|||
}
|
||||
|
||||
// Get config extra
|
||||
val loginConfig = activity.intent.getParcelableExtra<LoginConfig?>(FTUEActivity.EXTRA_CONFIG)
|
||||
val loginConfig = activity.intent.getParcelableExtra<LoginConfig?>(OnboardingActivity.EXTRA_CONFIG)
|
||||
if (isFirstCreation) {
|
||||
// TODO Check this
|
||||
loginViewModel.handle(LoginAction2.InitWith(loginConfig))
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.app.features.onboarding
|
||||
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import im.vector.app.features.login.LoginConfig
|
||||
import im.vector.app.features.login.ServerType
|
||||
import im.vector.app.features.login.SignMode
|
||||
import org.matrix.android.sdk.api.auth.data.Credentials
|
||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||
import org.matrix.android.sdk.api.auth.registration.RegisterThreePid
|
||||
import org.matrix.android.sdk.internal.network.ssl.Fingerprint
|
||||
|
||||
sealed class OnboardingAction : VectorViewModelAction {
|
||||
data class OnGetStarted(val resetLoginConfig: Boolean) : OnboardingAction()
|
||||
|
||||
data class UpdateServerType(val serverType: ServerType) : OnboardingAction()
|
||||
data class UpdateHomeServer(val homeServerUrl: String) : OnboardingAction()
|
||||
data class UpdateSignMode(val signMode: SignMode) : OnboardingAction()
|
||||
data class LoginWithToken(val loginToken: String) : OnboardingAction()
|
||||
data class WebLoginSuccess(val credentials: Credentials) : OnboardingAction()
|
||||
data class InitWith(val loginConfig: LoginConfig?) : OnboardingAction()
|
||||
data class ResetPassword(val email: String, val newPassword: String) : OnboardingAction()
|
||||
object ResetPasswordMailConfirmed : OnboardingAction()
|
||||
|
||||
// Login or Register, depending on the signMode
|
||||
data class LoginOrRegister(val username: String, val password: String, val initialDeviceName: String) : OnboardingAction()
|
||||
|
||||
// Register actions
|
||||
open class RegisterAction : OnboardingAction()
|
||||
|
||||
data class AddThreePid(val threePid: RegisterThreePid) : RegisterAction()
|
||||
object SendAgainThreePid : RegisterAction()
|
||||
|
||||
// TODO Confirm Email (from link in the email, open in the phone, intercepted by the app)
|
||||
data class ValidateThreePid(val code: String) : RegisterAction()
|
||||
|
||||
data class CheckIfEmailHasBeenValidated(val delayMillis: Long) : RegisterAction()
|
||||
object StopEmailValidationCheck : RegisterAction()
|
||||
|
||||
data class CaptchaDone(val captchaResponse: String) : RegisterAction()
|
||||
object AcceptTerms : RegisterAction()
|
||||
object RegisterDummy : RegisterAction()
|
||||
|
||||
// Reset actions
|
||||
open class ResetAction : OnboardingAction()
|
||||
|
||||
object ResetHomeServerType : ResetAction()
|
||||
object ResetHomeServerUrl : ResetAction()
|
||||
object ResetSignMode : ResetAction()
|
||||
object ResetLogin : ResetAction()
|
||||
object ResetResetPassword : ResetAction()
|
||||
|
||||
// Homeserver history
|
||||
object ClearHomeServerHistory : OnboardingAction()
|
||||
|
||||
// For the soft logout case
|
||||
data class SetupSsoForSessionRecovery(val homeServerUrl: String,
|
||||
val deviceId: String,
|
||||
val ssoIdentityProviders: List<SsoIdentityProvider>?) : OnboardingAction()
|
||||
|
||||
data class PostViewEvent(val viewEvent: OnboardingViewEvents) : OnboardingAction()
|
||||
|
||||
data class UserAcceptCertificate(val fingerprint: Fingerprint) : OnboardingAction()
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.ftue
|
||||
package im.vector.app.features.onboarding
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
@ -31,13 +31,13 @@ import im.vector.app.features.pin.UnlockedActivity
|
|||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class FTUEActivity : VectorBaseActivity<ActivityLoginBinding>(), ToolbarConfigurable, UnlockedActivity {
|
||||
class OnboardingActivity : VectorBaseActivity<ActivityLoginBinding>(), ToolbarConfigurable, UnlockedActivity {
|
||||
|
||||
private val ftueVariant by lifecycleAwareLazy {
|
||||
ftueVariantFactory.create(this, loginViewModel = lazyViewModel(), loginViewModel2 = lazyViewModel())
|
||||
private val onboardingVariant by lifecycleAwareLazy {
|
||||
onboardingVariantFactory.create(this, onboardingViewModel = lazyViewModel(), loginViewModel2 = lazyViewModel())
|
||||
}
|
||||
|
||||
@Inject lateinit var ftueVariantFactory: FTUEVariantFactory
|
||||
@Inject lateinit var onboardingVariantFactory: OnboardingVariantFactory
|
||||
|
||||
override fun getBinding() = ActivityLoginBinding.inflate(layoutInflater)
|
||||
|
||||
|
@ -49,37 +49,31 @@ class FTUEActivity : VectorBaseActivity<ActivityLoginBinding>(), ToolbarConfigur
|
|||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
ftueVariant.onNewIntent(intent)
|
||||
onboardingVariant.onNewIntent(intent)
|
||||
}
|
||||
|
||||
override fun initUiAndData() {
|
||||
ftueVariant.initUiAndData(isFirstCreation())
|
||||
onboardingVariant.initUiAndData(isFirstCreation())
|
||||
}
|
||||
|
||||
// Hack for AccountCreatedFragment
|
||||
fun setIsLoading(isLoading: Boolean) {
|
||||
ftueVariant.setIsLoading(isLoading)
|
||||
onboardingVariant.setIsLoading(isLoading)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_CONFIG = "EXTRA_CONFIG"
|
||||
|
||||
fun newIntent(context: Context, loginConfig: LoginConfig?): Intent {
|
||||
return Intent(context, FTUEActivity::class.java).apply {
|
||||
return Intent(context, OnboardingActivity::class.java).apply {
|
||||
putExtra(EXTRA_CONFIG, loginConfig)
|
||||
}
|
||||
}
|
||||
|
||||
fun redirectIntent(context: Context, data: Uri?): Intent {
|
||||
return Intent(context, FTUEActivity::class.java).apply {
|
||||
return Intent(context, OnboardingActivity::class.java).apply {
|
||||
setData(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface FTUEVariant {
|
||||
fun onNewIntent(intent: Intent?)
|
||||
fun initUiAndData(isFirstCreation: Boolean)
|
||||
fun setIsLoading(isLoading: Boolean)
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.ftue
|
||||
package im.vector.app.features.onboarding
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
|
@ -35,7 +35,6 @@ import im.vector.app.core.extensions.exhaustive
|
|||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.databinding.ActivityLoginBinding
|
||||
import im.vector.app.features.home.HomeActivity
|
||||
import im.vector.app.features.login.LoginAction
|
||||
import im.vector.app.features.login.LoginCaptchaFragment
|
||||
import im.vector.app.features.login.LoginCaptchaFragmentArgument
|
||||
import im.vector.app.features.login.LoginConfig
|
||||
|
@ -50,9 +49,6 @@ import im.vector.app.features.login.LoginServerSelectionFragment
|
|||
import im.vector.app.features.login.LoginServerUrlFormFragment
|
||||
import im.vector.app.features.login.LoginSignUpSignInSelectionFragment
|
||||
import im.vector.app.features.login.LoginSplashFragment
|
||||
import im.vector.app.features.login.LoginViewEvents
|
||||
import im.vector.app.features.login.LoginViewModel
|
||||
import im.vector.app.features.login.LoginViewState
|
||||
import im.vector.app.features.login.LoginWaitForEmailFragment
|
||||
import im.vector.app.features.login.LoginWaitForEmailFragmentArgument
|
||||
import im.vector.app.features.login.LoginWebFragment
|
||||
|
@ -70,12 +66,12 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
|
|||
private const val FRAGMENT_REGISTRATION_STAGE_TAG = "FRAGMENT_REGISTRATION_STAGE_TAG"
|
||||
private const val FRAGMENT_LOGIN_TAG = "FRAGMENT_LOGIN_TAG"
|
||||
|
||||
class DefaultFTUEVariant(
|
||||
class OnboardingAuthVariant(
|
||||
private val views: ActivityLoginBinding,
|
||||
private val loginViewModel: LoginViewModel,
|
||||
private val onboardingViewModel: OnboardingViewModel,
|
||||
private val activity: VectorBaseActivity<ActivityLoginBinding>,
|
||||
private val supportFragmentManager: FragmentManager
|
||||
) : FTUEVariant {
|
||||
) : OnboardingVariant {
|
||||
|
||||
private val enterAnim = R.anim.enter_fade_in
|
||||
private val exitAnim = R.anim.exit_fade_out
|
||||
|
@ -103,16 +99,16 @@ class DefaultFTUEVariant(
|
|||
}
|
||||
|
||||
with(activity) {
|
||||
loginViewModel.onEach {
|
||||
onboardingViewModel.onEach {
|
||||
updateWithState(it)
|
||||
}
|
||||
loginViewModel.observeViewEvents { handleLoginViewEvents(it) }
|
||||
onboardingViewModel.observeViewEvents { handleLoginViewEvents(it) }
|
||||
}
|
||||
|
||||
// Get config extra
|
||||
val loginConfig = activity.intent.getParcelableExtra<LoginConfig?>(FTUEActivity.EXTRA_CONFIG)
|
||||
val loginConfig = activity.intent.getParcelableExtra<LoginConfig?>(OnboardingActivity.EXTRA_CONFIG)
|
||||
if (isFirstCreation) {
|
||||
loginViewModel.handle(LoginAction.InitWith(loginConfig))
|
||||
onboardingViewModel.handle(OnboardingAction.InitWith(loginConfig))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,17 +120,17 @@ class DefaultFTUEVariant(
|
|||
activity.addFragment(views.loginFragmentContainer, LoginSplashFragment::class.java)
|
||||
}
|
||||
|
||||
private fun handleLoginViewEvents(loginViewEvents: LoginViewEvents) {
|
||||
when (loginViewEvents) {
|
||||
is LoginViewEvents.RegistrationFlowResult -> {
|
||||
private fun handleLoginViewEvents(onboardingViewEvents: OnboardingViewEvents) {
|
||||
when (onboardingViewEvents) {
|
||||
is OnboardingViewEvents.RegistrationFlowResult -> {
|
||||
// Check that all flows are supported by the application
|
||||
if (loginViewEvents.flowResult.missingStages.any { !it.isSupported() }) {
|
||||
if (onboardingViewEvents.flowResult.missingStages.any { !it.isSupported() }) {
|
||||
// Display a popup to propose use web fallback
|
||||
onRegistrationStageNotSupported()
|
||||
} else {
|
||||
if (loginViewEvents.isRegistrationStarted) {
|
||||
if (onboardingViewEvents.isRegistrationStarted) {
|
||||
// Go on with registration flow
|
||||
handleRegistrationNavigation(loginViewEvents.flowResult)
|
||||
handleRegistrationNavigation(onboardingViewEvents.flowResult)
|
||||
} else {
|
||||
// First ask for login and password
|
||||
// I add a tag to indicate that this fragment is a registration stage.
|
||||
|
@ -147,7 +143,7 @@ class DefaultFTUEVariant(
|
|||
}
|
||||
}
|
||||
}
|
||||
is LoginViewEvents.OutdatedHomeserver -> {
|
||||
is OnboardingViewEvents.OutdatedHomeserver -> {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.login_error_outdated_homeserver_title)
|
||||
.setMessage(R.string.login_error_outdated_homeserver_warning_content)
|
||||
|
@ -155,7 +151,7 @@ class DefaultFTUEVariant(
|
|||
.show()
|
||||
Unit
|
||||
}
|
||||
is LoginViewEvents.OpenServerSelection ->
|
||||
is OnboardingViewEvents.OpenServerSelection ->
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
LoginServerSelectionFragment::class.java,
|
||||
option = { ft ->
|
||||
|
@ -167,63 +163,63 @@ class DefaultFTUEVariant(
|
|||
// TODO Disabled because it provokes a flickering
|
||||
// ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
|
||||
})
|
||||
is LoginViewEvents.OnServerSelectionDone -> onServerSelectionDone(loginViewEvents)
|
||||
is LoginViewEvents.OnSignModeSelected -> onSignModeSelected(loginViewEvents)
|
||||
is LoginViewEvents.OnLoginFlowRetrieved ->
|
||||
is OnboardingViewEvents.OnServerSelectionDone -> onServerSelectionDone(onboardingViewEvents)
|
||||
is OnboardingViewEvents.OnSignModeSelected -> onSignModeSelected(onboardingViewEvents)
|
||||
is OnboardingViewEvents.OnLoginFlowRetrieved ->
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
LoginSignUpSignInSelectionFragment::class.java,
|
||||
option = commonOption)
|
||||
is LoginViewEvents.OnWebLoginError -> onWebLoginError(loginViewEvents)
|
||||
is LoginViewEvents.OnForgetPasswordClicked ->
|
||||
is OnboardingViewEvents.OnWebLoginError -> onWebLoginError(onboardingViewEvents)
|
||||
is OnboardingViewEvents.OnForgetPasswordClicked ->
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
LoginResetPasswordFragment::class.java,
|
||||
option = commonOption)
|
||||
is LoginViewEvents.OnResetPasswordSendThreePidDone -> {
|
||||
is OnboardingViewEvents.OnResetPasswordSendThreePidDone -> {
|
||||
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
LoginResetPasswordMailConfirmationFragment::class.java,
|
||||
option = commonOption)
|
||||
}
|
||||
is LoginViewEvents.OnResetPasswordMailConfirmationSuccess -> {
|
||||
is OnboardingViewEvents.OnResetPasswordMailConfirmationSuccess -> {
|
||||
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
LoginResetPasswordSuccessFragment::class.java,
|
||||
option = commonOption)
|
||||
}
|
||||
is LoginViewEvents.OnResetPasswordMailConfirmationSuccessDone -> {
|
||||
is OnboardingViewEvents.OnResetPasswordMailConfirmationSuccessDone -> {
|
||||
// Go back to the login fragment
|
||||
supportFragmentManager.popBackStack(FRAGMENT_LOGIN_TAG, POP_BACK_STACK_EXCLUSIVE)
|
||||
}
|
||||
is LoginViewEvents.OnSendEmailSuccess -> {
|
||||
is OnboardingViewEvents.OnSendEmailSuccess -> {
|
||||
// Pop the enter email Fragment
|
||||
supportFragmentManager.popBackStack(FRAGMENT_REGISTRATION_STAGE_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
LoginWaitForEmailFragment::class.java,
|
||||
LoginWaitForEmailFragmentArgument(loginViewEvents.email),
|
||||
LoginWaitForEmailFragmentArgument(onboardingViewEvents.email),
|
||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
||||
option = commonOption)
|
||||
}
|
||||
is LoginViewEvents.OnSendMsisdnSuccess -> {
|
||||
is OnboardingViewEvents.OnSendMsisdnSuccess -> {
|
||||
// Pop the enter Msisdn Fragment
|
||||
supportFragmentManager.popBackStack(FRAGMENT_REGISTRATION_STAGE_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
activity.addFragmentToBackstack(views.loginFragmentContainer,
|
||||
LoginGenericTextInputFormFragment::class.java,
|
||||
LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, loginViewEvents.msisdn),
|
||||
LoginGenericTextInputFormFragmentArgument(TextInputFormFragmentMode.ConfirmMsisdn, true, onboardingViewEvents.msisdn),
|
||||
tag = FRAGMENT_REGISTRATION_STAGE_TAG,
|
||||
option = commonOption)
|
||||
}
|
||||
is LoginViewEvents.Failure,
|
||||
is LoginViewEvents.Loading ->
|
||||
is OnboardingViewEvents.Failure,
|
||||
is OnboardingViewEvents.Loading ->
|
||||
// This is handled by the Fragments
|
||||
Unit
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun updateWithState(loginViewState: LoginViewState) {
|
||||
if (loginViewState.isUserLogged()) {
|
||||
private fun updateWithState(onboardingViewState: OnboardingViewState) {
|
||||
if (onboardingViewState.isUserLogged()) {
|
||||
val intent = HomeActivity.newIntent(
|
||||
activity,
|
||||
accountCreation = loginViewState.signMode == SignMode.SignUp
|
||||
accountCreation = onboardingViewState.signMode == SignMode.SignUp
|
||||
)
|
||||
activity.startActivity(intent)
|
||||
activity.finish()
|
||||
|
@ -231,10 +227,10 @@ class DefaultFTUEVariant(
|
|||
}
|
||||
|
||||
// Loading
|
||||
views.loginLoading.isVisible = loginViewState.isLoading()
|
||||
views.loginLoading.isVisible = onboardingViewState.isLoading()
|
||||
}
|
||||
|
||||
private fun onWebLoginError(onWebLoginError: LoginViewEvents.OnWebLoginError) {
|
||||
private fun onWebLoginError(onWebLoginError: OnboardingViewEvents.OnWebLoginError) {
|
||||
// Pop the backstack
|
||||
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
|
||||
|
||||
|
@ -246,7 +242,7 @@ class DefaultFTUEVariant(
|
|||
.show()
|
||||
}
|
||||
|
||||
private fun onServerSelectionDone(loginViewEvents: LoginViewEvents.OnServerSelectionDone) {
|
||||
private fun onServerSelectionDone(loginViewEvents: OnboardingViewEvents.OnServerSelectionDone) {
|
||||
when (loginViewEvents.serverType) {
|
||||
ServerType.MatrixOrg -> Unit // In this case, we wait for the login flow
|
||||
ServerType.EMS,
|
||||
|
@ -257,7 +253,7 @@ class DefaultFTUEVariant(
|
|||
}
|
||||
}
|
||||
|
||||
private fun onSignModeSelected(loginViewEvents: LoginViewEvents.OnSignModeSelected) = withState(loginViewModel) { state ->
|
||||
private fun onSignModeSelected(loginViewEvents: OnboardingViewEvents.OnSignModeSelected) = withState(onboardingViewModel) { state ->
|
||||
// state.signMode could not be ready yet. So use value from the ViewEvent
|
||||
when (loginViewEvents.signMode) {
|
||||
SignMode.Unknown -> error("Sign mode has to be set before calling this method")
|
||||
|
@ -290,7 +286,7 @@ class DefaultFTUEVariant(
|
|||
override fun onNewIntent(intent: Intent?) {
|
||||
intent?.data
|
||||
?.let { tryOrNull { it.getQueryParameter("loginToken") } }
|
||||
?.let { loginViewModel.handle(LoginAction.LoginWithToken(it)) }
|
||||
?.let { onboardingViewModel.handle(OnboardingAction.LoginWithToken(it)) }
|
||||
}
|
||||
|
||||
private fun onRegistrationStageNotSupported() {
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.features.onboarding
|
||||
|
||||
import android.content.Intent
|
||||
|
||||
interface OnboardingVariant {
|
||||
fun onNewIntent(intent: Intent?)
|
||||
fun initUiAndData(isFirstCreation: Boolean)
|
||||
fun setIsLoading(isLoading: Boolean)
|
||||
}
|
|
@ -14,26 +14,28 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.ftue
|
||||
package im.vector.app.features.onboarding
|
||||
|
||||
import im.vector.app.features.VectorFeatures
|
||||
import im.vector.app.features.login.LoginViewModel
|
||||
import im.vector.app.features.login2.LoginViewModel2
|
||||
import javax.inject.Inject
|
||||
|
||||
class FTUEVariantFactory @Inject constructor(
|
||||
class OnboardingVariantFactory @Inject constructor(
|
||||
private val vectorFeatures: VectorFeatures,
|
||||
) {
|
||||
|
||||
fun create(activity: FTUEActivity, loginViewModel: Lazy<LoginViewModel>, loginViewModel2: Lazy<LoginViewModel2>) = when (vectorFeatures.loginVariant()) {
|
||||
VectorFeatures.LoginVariant.LEGACY -> error("Legacy is not supported by the FTUE")
|
||||
VectorFeatures.LoginVariant.FTUE -> DefaultFTUEVariant(
|
||||
fun create(activity: OnboardingActivity,
|
||||
onboardingViewModel: Lazy<OnboardingViewModel>,
|
||||
loginViewModel2: Lazy<LoginViewModel2>
|
||||
) = when (vectorFeatures.onboardingVariant()) {
|
||||
VectorFeatures.OnboardingVariant.LEGACY -> error("Legacy is not supported by the FTUE")
|
||||
VectorFeatures.OnboardingVariant.FTUE_AUTH -> OnboardingAuthVariant(
|
||||
views = activity.getBinding(),
|
||||
loginViewModel = loginViewModel.value,
|
||||
onboardingViewModel = onboardingViewModel.value,
|
||||
activity = activity,
|
||||
supportFragmentManager = activity.supportFragmentManager
|
||||
)
|
||||
VectorFeatures.LoginVariant.FTUE_WIP -> FTUEWipVariant(
|
||||
VectorFeatures.OnboardingVariant.LOGIN_2 -> Login2Variant(
|
||||
views = activity.getBinding(),
|
||||
loginViewModel = loginViewModel2.value,
|
||||
activity = activity,
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.app.features.onboarding
|
||||
|
||||
import im.vector.app.core.platform.VectorViewEvents
|
||||
import im.vector.app.features.login.ServerType
|
||||
import im.vector.app.features.login.SignMode
|
||||
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
||||
|
||||
/**
|
||||
* Transient events for Login
|
||||
*/
|
||||
sealed class OnboardingViewEvents : VectorViewEvents {
|
||||
data class Loading(val message: CharSequence? = null) : OnboardingViewEvents()
|
||||
data class Failure(val throwable: Throwable) : OnboardingViewEvents()
|
||||
|
||||
data class RegistrationFlowResult(val flowResult: FlowResult, val isRegistrationStarted: Boolean) : OnboardingViewEvents()
|
||||
object OutdatedHomeserver : OnboardingViewEvents()
|
||||
|
||||
// Navigation event
|
||||
|
||||
object OpenServerSelection : OnboardingViewEvents()
|
||||
data class OnServerSelectionDone(val serverType: ServerType) : OnboardingViewEvents()
|
||||
object OnLoginFlowRetrieved : OnboardingViewEvents()
|
||||
data class OnSignModeSelected(val signMode: SignMode) : OnboardingViewEvents()
|
||||
object OnForgetPasswordClicked : OnboardingViewEvents()
|
||||
object OnResetPasswordSendThreePidDone : OnboardingViewEvents()
|
||||
object OnResetPasswordMailConfirmationSuccess : OnboardingViewEvents()
|
||||
object OnResetPasswordMailConfirmationSuccessDone : OnboardingViewEvents()
|
||||
|
||||
data class OnSendEmailSuccess(val email: String) : OnboardingViewEvents()
|
||||
data class OnSendMsisdnSuccess(val msisdn: String) : OnboardingViewEvents()
|
||||
|
||||
data class OnWebLoginError(val errorCode: Int, val description: String, val failingUrl: String) : OnboardingViewEvents()
|
||||
}
|
|
@ -0,0 +1,840 @@
|
|||
/*
|
||||
* 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.app.features.onboarding
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||
import im.vector.app.core.extensions.configureAndStart
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.ensureTrailingSlash
|
||||
import im.vector.app.features.login.HomeServerConnectionConfigFactory
|
||||
import im.vector.app.features.login.LoginConfig
|
||||
import im.vector.app.features.login.LoginMode
|
||||
import im.vector.app.features.login.ReAuthHelper
|
||||
import im.vector.app.features.login.ServerType
|
||||
import im.vector.app.features.login.SignMode
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.MatrixPatterns.getDomain
|
||||
import org.matrix.android.sdk.api.auth.AuthenticationService
|
||||
import org.matrix.android.sdk.api.auth.HomeServerHistoryService
|
||||
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
|
||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||
import org.matrix.android.sdk.api.auth.login.LoginWizard
|
||||
import org.matrix.android.sdk.api.auth.registration.FlowResult
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
|
||||
import org.matrix.android.sdk.api.auth.registration.RegistrationWizard
|
||||
import org.matrix.android.sdk.api.auth.registration.Stage
|
||||
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.failure.MatrixIdFailure
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.CancellationException
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class OnboardingViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: OnboardingViewState,
|
||||
private val applicationContext: Context,
|
||||
private val authenticationService: AuthenticationService,
|
||||
private val activeSessionHolder: ActiveSessionHolder,
|
||||
private val homeServerConnectionConfigFactory: HomeServerConnectionConfigFactory,
|
||||
private val reAuthHelper: ReAuthHelper,
|
||||
private val stringProvider: StringProvider,
|
||||
private val homeServerHistoryService: HomeServerHistoryService
|
||||
) : VectorViewModel<OnboardingViewState, OnboardingAction, OnboardingViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MavericksAssistedViewModelFactory<OnboardingViewModel, OnboardingViewState> {
|
||||
override fun create(initialState: OnboardingViewState): OnboardingViewModel
|
||||
}
|
||||
|
||||
companion object : MavericksViewModelFactory<OnboardingViewModel, OnboardingViewState> by hiltMavericksViewModelFactory()
|
||||
|
||||
init {
|
||||
getKnownCustomHomeServersUrls()
|
||||
}
|
||||
|
||||
private fun getKnownCustomHomeServersUrls() {
|
||||
setState {
|
||||
copy(knownCustomHomeServersUrls = homeServerHistoryService.getKnownServersUrls())
|
||||
}
|
||||
}
|
||||
|
||||
// Store the last action, to redo it after user has trusted the untrusted certificate
|
||||
private var lastAction: OnboardingAction? = null
|
||||
private var currentHomeServerConnectionConfig: HomeServerConnectionConfig? = null
|
||||
|
||||
private val matrixOrgUrl = stringProvider.getString(R.string.matrix_org_server_url).ensureTrailingSlash()
|
||||
|
||||
val currentThreePid: String?
|
||||
get() = registrationWizard?.currentThreePid
|
||||
|
||||
// True when login and password has been sent with success to the homeserver
|
||||
val isRegistrationStarted: Boolean
|
||||
get() = authenticationService.isRegistrationStarted
|
||||
|
||||
private val registrationWizard: RegistrationWizard?
|
||||
get() = authenticationService.getRegistrationWizard()
|
||||
|
||||
private val loginWizard: LoginWizard?
|
||||
get() = authenticationService.getLoginWizard()
|
||||
|
||||
private var loginConfig: LoginConfig? = null
|
||||
|
||||
private var currentJob: Job? = null
|
||||
set(value) {
|
||||
// Cancel any previous Job
|
||||
field?.cancel()
|
||||
field = value
|
||||
}
|
||||
|
||||
override fun handle(action: OnboardingAction) {
|
||||
when (action) {
|
||||
is OnboardingAction.OnGetStarted -> handleOnGetStarted(action)
|
||||
is OnboardingAction.UpdateServerType -> handleUpdateServerType(action)
|
||||
is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action)
|
||||
is OnboardingAction.InitWith -> handleInitWith(action)
|
||||
is OnboardingAction.UpdateHomeServer -> handleUpdateHomeserver(action).also { lastAction = action }
|
||||
is OnboardingAction.LoginOrRegister -> handleLoginOrRegister(action).also { lastAction = action }
|
||||
is OnboardingAction.LoginWithToken -> handleLoginWithToken(action)
|
||||
is OnboardingAction.WebLoginSuccess -> handleWebLoginSuccess(action)
|
||||
is OnboardingAction.ResetPassword -> handleResetPassword(action)
|
||||
is OnboardingAction.ResetPasswordMailConfirmed -> handleResetPasswordMailConfirmed()
|
||||
is OnboardingAction.RegisterAction -> handleRegisterAction(action)
|
||||
is OnboardingAction.ResetAction -> handleResetAction(action)
|
||||
is OnboardingAction.SetupSsoForSessionRecovery -> handleSetupSsoForSessionRecovery(action)
|
||||
is OnboardingAction.UserAcceptCertificate -> handleUserAcceptCertificate(action)
|
||||
OnboardingAction.ClearHomeServerHistory -> handleClearHomeServerHistory()
|
||||
is OnboardingAction.PostViewEvent -> _viewEvents.post(action.viewEvent)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleOnGetStarted(action: OnboardingAction.OnGetStarted) {
|
||||
if (action.resetLoginConfig) {
|
||||
loginConfig = null
|
||||
}
|
||||
|
||||
val configUrl = loginConfig?.homeServerUrl?.takeIf { it.isNotEmpty() }
|
||||
if (configUrl != null) {
|
||||
// Use config from uri
|
||||
val homeServerConnectionConfig = homeServerConnectionConfigFactory.create(configUrl)
|
||||
if (homeServerConnectionConfig == null) {
|
||||
// Url is invalid, in this case, just use the regular flow
|
||||
Timber.w("Url from config url was invalid: $configUrl")
|
||||
_viewEvents.post(OnboardingViewEvents.OpenServerSelection)
|
||||
} else {
|
||||
getLoginFlow(homeServerConnectionConfig, ServerType.Other)
|
||||
}
|
||||
} else {
|
||||
_viewEvents.post(OnboardingViewEvents.OpenServerSelection)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUserAcceptCertificate(action: OnboardingAction.UserAcceptCertificate) {
|
||||
// It happens when we get the login flow, or during direct authentication.
|
||||
// So alter the homeserver config and retrieve again the login flow
|
||||
when (val finalLastAction = lastAction) {
|
||||
is OnboardingAction.UpdateHomeServer -> {
|
||||
currentHomeServerConnectionConfig
|
||||
?.let { it.copy(allowedFingerprints = it.allowedFingerprints + action.fingerprint) }
|
||||
?.let { getLoginFlow(it) }
|
||||
}
|
||||
is OnboardingAction.LoginOrRegister ->
|
||||
handleDirectLogin(
|
||||
finalLastAction,
|
||||
HomeServerConnectionConfig.Builder()
|
||||
// Will be replaced by the task
|
||||
.withHomeServerUri("https://dummy.org")
|
||||
.withAllowedFingerPrints(listOf(action.fingerprint))
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rememberHomeServer(homeServerUrl: String) {
|
||||
homeServerHistoryService.addHomeServerToHistory(homeServerUrl)
|
||||
getKnownCustomHomeServersUrls()
|
||||
}
|
||||
|
||||
private fun handleClearHomeServerHistory() {
|
||||
homeServerHistoryService.clearHistory()
|
||||
getKnownCustomHomeServersUrls()
|
||||
}
|
||||
|
||||
private fun handleLoginWithToken(action: OnboardingAction.LoginWithToken) {
|
||||
val safeLoginWizard = loginWizard
|
||||
|
||||
if (safeLoginWizard == null) {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(Throwable("Bad configuration"))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
currentJob = viewModelScope.launch {
|
||||
try {
|
||||
safeLoginWizard.loginWithToken(action.loginToken)
|
||||
} catch (failure: Throwable) {
|
||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(failure)
|
||||
)
|
||||
}
|
||||
null
|
||||
}
|
||||
?.let { onSessionCreated(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSetupSsoForSessionRecovery(action: OnboardingAction.SetupSsoForSessionRecovery) {
|
||||
setState {
|
||||
copy(
|
||||
signMode = SignMode.SignIn,
|
||||
loginMode = LoginMode.Sso(action.ssoIdentityProviders),
|
||||
homeServerUrlFromUser = action.homeServerUrl,
|
||||
homeServerUrl = action.homeServerUrl,
|
||||
deviceId = action.deviceId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRegisterAction(action: OnboardingAction.RegisterAction) {
|
||||
when (action) {
|
||||
is OnboardingAction.CaptchaDone -> handleCaptchaDone(action)
|
||||
is OnboardingAction.AcceptTerms -> handleAcceptTerms()
|
||||
is OnboardingAction.RegisterDummy -> handleRegisterDummy()
|
||||
is OnboardingAction.AddThreePid -> handleAddThreePid(action)
|
||||
is OnboardingAction.SendAgainThreePid -> handleSendAgainThreePid()
|
||||
is OnboardingAction.ValidateThreePid -> handleValidateThreePid(action)
|
||||
is OnboardingAction.CheckIfEmailHasBeenValidated -> handleCheckIfEmailHasBeenValidated(action)
|
||||
is OnboardingAction.StopEmailValidationCheck -> handleStopEmailValidationCheck()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCheckIfEmailHasBeenValidated(action: OnboardingAction.CheckIfEmailHasBeenValidated) {
|
||||
// We do not want the common progress bar to be displayed, so we do not change asyncRegistration value in the state
|
||||
currentJob = executeRegistrationStep(withLoading = false) {
|
||||
it.checkIfEmailHasBeenValidated(action.delayMillis)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleStopEmailValidationCheck() {
|
||||
currentJob = null
|
||||
}
|
||||
|
||||
private fun handleValidateThreePid(action: OnboardingAction.ValidateThreePid) {
|
||||
currentJob = executeRegistrationStep {
|
||||
it.handleValidateThreePid(action.code)
|
||||
}
|
||||
}
|
||||
|
||||
private fun executeRegistrationStep(withLoading: Boolean = true,
|
||||
block: suspend (RegistrationWizard) -> RegistrationResult): Job {
|
||||
if (withLoading) {
|
||||
setState { copy(asyncRegistration = Loading()) }
|
||||
}
|
||||
return viewModelScope.launch {
|
||||
try {
|
||||
registrationWizard?.let { block(it) }
|
||||
/*
|
||||
// Simulate registration disabled
|
||||
throw Failure.ServerError(MatrixError(
|
||||
code = MatrixError.FORBIDDEN,
|
||||
message = "Registration is disabled"
|
||||
), 403))
|
||||
*/
|
||||
} catch (failure: Throwable) {
|
||||
if (failure !is CancellationException) {
|
||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
||||
}
|
||||
null
|
||||
}
|
||||
?.let { data ->
|
||||
when (data) {
|
||||
is RegistrationResult.Success -> onSessionCreated(data.session)
|
||||
is RegistrationResult.FlowResponse -> onFlowResponse(data.flowResult)
|
||||
}
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Uninitialized
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAddThreePid(action: OnboardingAction.AddThreePid) {
|
||||
setState { copy(asyncRegistration = Loading()) }
|
||||
currentJob = viewModelScope.launch {
|
||||
try {
|
||||
registrationWizard?.addThreePid(action.threePid)
|
||||
} catch (failure: Throwable) {
|
||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
||||
}
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Uninitialized
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSendAgainThreePid() {
|
||||
setState { copy(asyncRegistration = Loading()) }
|
||||
currentJob = viewModelScope.launch {
|
||||
try {
|
||||
registrationWizard?.sendAgainThreePid()
|
||||
} catch (failure: Throwable) {
|
||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
||||
}
|
||||
setState {
|
||||
copy(
|
||||
asyncRegistration = Uninitialized
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAcceptTerms() {
|
||||
currentJob = executeRegistrationStep {
|
||||
it.acceptTerms()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRegisterDummy() {
|
||||
currentJob = executeRegistrationStep {
|
||||
it.dummy()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRegisterWith(action: OnboardingAction.LoginOrRegister) {
|
||||
reAuthHelper.data = action.password
|
||||
currentJob = executeRegistrationStep {
|
||||
it.createAccount(
|
||||
action.username,
|
||||
action.password,
|
||||
action.initialDeviceName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCaptchaDone(action: OnboardingAction.CaptchaDone) {
|
||||
currentJob = executeRegistrationStep {
|
||||
it.performReCaptcha(action.captchaResponse)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleResetAction(action: OnboardingAction.ResetAction) {
|
||||
// Cancel any request
|
||||
currentJob = null
|
||||
|
||||
when (action) {
|
||||
OnboardingAction.ResetHomeServerType -> {
|
||||
setState {
|
||||
copy(
|
||||
serverType = ServerType.Unknown
|
||||
)
|
||||
}
|
||||
}
|
||||
OnboardingAction.ResetHomeServerUrl -> {
|
||||
viewModelScope.launch {
|
||||
authenticationService.reset()
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
||||
homeServerUrlFromUser = null,
|
||||
homeServerUrl = null,
|
||||
loginMode = LoginMode.Unknown,
|
||||
serverType = ServerType.Unknown,
|
||||
loginModeSupportedTypes = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
OnboardingAction.ResetSignMode -> {
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
||||
signMode = SignMode.Unknown,
|
||||
loginMode = LoginMode.Unknown,
|
||||
loginModeSupportedTypes = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
OnboardingAction.ResetLogin -> {
|
||||
viewModelScope.launch {
|
||||
authenticationService.cancelPendingLoginOrRegistration()
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Uninitialized,
|
||||
asyncRegistration = Uninitialized
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
OnboardingAction.ResetResetPassword -> {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Uninitialized,
|
||||
asyncResetMailConfirmed = Uninitialized,
|
||||
resetPasswordEmail = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUpdateSignMode(action: OnboardingAction.UpdateSignMode) {
|
||||
setState {
|
||||
copy(
|
||||
signMode = action.signMode
|
||||
)
|
||||
}
|
||||
|
||||
when (action.signMode) {
|
||||
SignMode.SignUp -> startRegistrationFlow()
|
||||
SignMode.SignIn -> startAuthenticationFlow()
|
||||
SignMode.SignInWithMatrixId -> _viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.SignInWithMatrixId))
|
||||
SignMode.Unknown -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) {
|
||||
setState {
|
||||
copy(
|
||||
serverType = action.serverType
|
||||
)
|
||||
}
|
||||
|
||||
when (action.serverType) {
|
||||
ServerType.Unknown -> Unit /* Should not happen */
|
||||
ServerType.MatrixOrg ->
|
||||
// Request login flow here
|
||||
handle(OnboardingAction.UpdateHomeServer(matrixOrgUrl))
|
||||
ServerType.EMS,
|
||||
ServerType.Other -> _viewEvents.post(OnboardingViewEvents.OnServerSelectionDone(action.serverType))
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleInitWith(action: OnboardingAction.InitWith) {
|
||||
loginConfig = action.loginConfig
|
||||
|
||||
// If there is a pending email validation continue on this step
|
||||
try {
|
||||
if (registrationWizard?.isRegistrationStarted == true) {
|
||||
currentThreePid?.let {
|
||||
handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnSendEmailSuccess(it)))
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
// NOOP. API is designed to use wizards in a login/registration flow,
|
||||
// but we need to check the state anyway.
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleResetPassword(action: OnboardingAction.ResetPassword) {
|
||||
val safeLoginWizard = loginWizard
|
||||
|
||||
if (safeLoginWizard == null) {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Fail(Throwable("Bad configuration")),
|
||||
asyncResetMailConfirmed = Uninitialized
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Loading(),
|
||||
asyncResetMailConfirmed = Uninitialized
|
||||
)
|
||||
}
|
||||
|
||||
currentJob = viewModelScope.launch {
|
||||
try {
|
||||
safeLoginWizard.resetPassword(action.email, action.newPassword)
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Fail(failure)
|
||||
)
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Success(Unit),
|
||||
resetPasswordEmail = action.email
|
||||
)
|
||||
}
|
||||
|
||||
_viewEvents.post(OnboardingViewEvents.OnResetPasswordSendThreePidDone)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleResetPasswordMailConfirmed() {
|
||||
val safeLoginWizard = loginWizard
|
||||
|
||||
if (safeLoginWizard == null) {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Uninitialized,
|
||||
asyncResetMailConfirmed = Fail(Throwable("Bad configuration"))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetPassword = Uninitialized,
|
||||
asyncResetMailConfirmed = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
currentJob = viewModelScope.launch {
|
||||
try {
|
||||
safeLoginWizard.resetPasswordMailConfirmed()
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
asyncResetMailConfirmed = Fail(failure)
|
||||
)
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
setState {
|
||||
copy(
|
||||
asyncResetMailConfirmed = Success(Unit),
|
||||
resetPasswordEmail = null
|
||||
)
|
||||
}
|
||||
|
||||
_viewEvents.post(OnboardingViewEvents.OnResetPasswordMailConfirmationSuccess)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLoginOrRegister(action: OnboardingAction.LoginOrRegister) = withState { state ->
|
||||
when (state.signMode) {
|
||||
SignMode.Unknown -> error("Developer error, invalid sign mode")
|
||||
SignMode.SignIn -> handleLogin(action)
|
||||
SignMode.SignUp -> handleRegisterWith(action)
|
||||
SignMode.SignInWithMatrixId -> handleDirectLogin(action, null)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleDirectLogin(action: OnboardingAction.LoginOrRegister, homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
currentJob = viewModelScope.launch {
|
||||
val data = try {
|
||||
authenticationService.getWellKnownData(action.username, homeServerConnectionConfig)
|
||||
} catch (failure: Throwable) {
|
||||
onDirectLoginError(failure)
|
||||
return@launch
|
||||
}
|
||||
when (data) {
|
||||
is WellknownResult.Prompt ->
|
||||
onWellknownSuccess(action, data, homeServerConnectionConfig)
|
||||
is WellknownResult.FailPrompt ->
|
||||
// Relax on IS discovery if homeserver is valid
|
||||
if (data.homeServerUrl != null && data.wellKnown != null) {
|
||||
onWellknownSuccess(action, WellknownResult.Prompt(data.homeServerUrl!!, null, data.wellKnown!!), homeServerConnectionConfig)
|
||||
} else {
|
||||
onWellKnownError()
|
||||
}
|
||||
else -> {
|
||||
onWellKnownError()
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
||||
|
||||
private fun onWellKnownError() {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Uninitialized
|
||||
)
|
||||
}
|
||||
_viewEvents.post(OnboardingViewEvents.Failure(Exception(stringProvider.getString(R.string.autodiscover_well_known_error))))
|
||||
}
|
||||
|
||||
private suspend fun onWellknownSuccess(action: OnboardingAction.LoginOrRegister,
|
||||
wellKnownPrompt: WellknownResult.Prompt,
|
||||
homeServerConnectionConfig: HomeServerConnectionConfig?) {
|
||||
val alteredHomeServerConnectionConfig = homeServerConnectionConfig
|
||||
?.copy(
|
||||
homeServerUriBase = Uri.parse(wellKnownPrompt.homeServerUrl),
|
||||
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
|
||||
)
|
||||
?: HomeServerConnectionConfig(
|
||||
homeServerUri = Uri.parse("https://${action.username.getDomain()}"),
|
||||
homeServerUriBase = Uri.parse(wellKnownPrompt.homeServerUrl),
|
||||
identityServerUri = wellKnownPrompt.identityServerUrl?.let { Uri.parse(it) }
|
||||
)
|
||||
|
||||
val data = try {
|
||||
authenticationService.directAuthentication(
|
||||
alteredHomeServerConnectionConfig,
|
||||
action.username,
|
||||
action.password,
|
||||
action.initialDeviceName)
|
||||
} catch (failure: Throwable) {
|
||||
onDirectLoginError(failure)
|
||||
return
|
||||
}
|
||||
onSessionCreated(data)
|
||||
}
|
||||
|
||||
private fun onDirectLoginError(failure: Throwable) {
|
||||
when (failure) {
|
||||
is MatrixIdFailure.InvalidMatrixId,
|
||||
is Failure.UnrecognizedCertificateFailure -> {
|
||||
// Display this error in a dialog
|
||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Uninitialized
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLogin(action: OnboardingAction.LoginOrRegister) {
|
||||
val safeLoginWizard = loginWizard
|
||||
|
||||
if (safeLoginWizard == null) {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(Throwable("Bad configuration"))
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
currentJob = viewModelScope.launch {
|
||||
try {
|
||||
safeLoginWizard.login(
|
||||
action.username,
|
||||
action.password,
|
||||
action.initialDeviceName
|
||||
)
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Fail(failure)
|
||||
)
|
||||
}
|
||||
null
|
||||
}
|
||||
?.let {
|
||||
reAuthHelper.data = action.password
|
||||
onSessionCreated(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startRegistrationFlow() {
|
||||
currentJob = executeRegistrationStep {
|
||||
it.getRegistrationFlow()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startAuthenticationFlow() {
|
||||
// Ensure Wizard is ready
|
||||
loginWizard
|
||||
|
||||
_viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.SignIn))
|
||||
}
|
||||
|
||||
private fun onFlowResponse(flowResult: FlowResult) {
|
||||
// If dummy stage is mandatory, and password is already sent, do the dummy stage now
|
||||
if (isRegistrationStarted &&
|
||||
flowResult.missingStages.any { it is Stage.Dummy && it.mandatory }) {
|
||||
handleRegisterDummy()
|
||||
} else {
|
||||
// Notify the user
|
||||
_viewEvents.post(OnboardingViewEvents.RegistrationFlowResult(flowResult, isRegistrationStarted))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun onSessionCreated(session: Session) {
|
||||
activeSessionHolder.setActiveSession(session)
|
||||
|
||||
authenticationService.reset()
|
||||
session.configureAndStart(applicationContext)
|
||||
setState {
|
||||
copy(
|
||||
asyncLoginAction = Success(Unit)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleWebLoginSuccess(action: OnboardingAction.WebLoginSuccess) = withState { state ->
|
||||
val homeServerConnectionConfigFinal = homeServerConnectionConfigFactory.create(state.homeServerUrl)
|
||||
|
||||
if (homeServerConnectionConfigFinal == null) {
|
||||
// Should not happen
|
||||
Timber.w("homeServerConnectionConfig is null")
|
||||
} else {
|
||||
currentJob = viewModelScope.launch {
|
||||
try {
|
||||
authenticationService.createSessionFromSso(homeServerConnectionConfigFinal, action.credentials)
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(asyncLoginAction = Fail(failure))
|
||||
}
|
||||
null
|
||||
}
|
||||
?.let { onSessionCreated(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUpdateHomeserver(action: OnboardingAction.UpdateHomeServer) {
|
||||
val homeServerConnectionConfig = homeServerConnectionConfigFactory.create(action.homeServerUrl)
|
||||
if (homeServerConnectionConfig == null) {
|
||||
// This is invalid
|
||||
_viewEvents.post(OnboardingViewEvents.Failure(Throwable("Unable to create a HomeServerConnectionConfig")))
|
||||
} else {
|
||||
getLoginFlow(homeServerConnectionConfig)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLoginFlow(homeServerConnectionConfig: HomeServerConnectionConfig,
|
||||
serverTypeOverride: ServerType? = null) {
|
||||
currentHomeServerConnectionConfig = homeServerConnectionConfig
|
||||
|
||||
currentJob = viewModelScope.launch {
|
||||
authenticationService.cancelPendingLoginOrRegistration()
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Loading(),
|
||||
// If user has entered https://matrix.org, ensure that server type is ServerType.MatrixOrg
|
||||
// It is also useful to set the value again in the case of a certificate error on matrix.org
|
||||
serverType = if (homeServerConnectionConfig.homeServerUri.toString() == matrixOrgUrl) {
|
||||
ServerType.MatrixOrg
|
||||
} else {
|
||||
serverTypeOverride ?: serverType
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val data = try {
|
||||
authenticationService.getLoginFlow(homeServerConnectionConfig)
|
||||
} catch (failure: Throwable) {
|
||||
_viewEvents.post(OnboardingViewEvents.Failure(failure))
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
||||
// If we were trying to retrieve matrix.org login flow, also reset the serverType
|
||||
serverType = if (serverType == ServerType.MatrixOrg) ServerType.Unknown else serverType
|
||||
)
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
data ?: return@launch
|
||||
|
||||
// Valid Homeserver, add it to the history.
|
||||
// Note: we add what the user has input, data.homeServerUrlBase can be different
|
||||
rememberHomeServer(homeServerConnectionConfig.homeServerUri.toString())
|
||||
|
||||
val loginMode = when {
|
||||
// SSO login is taken first
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) &&
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders)
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders)
|
||||
data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password
|
||||
else -> LoginMode.Unsupported
|
||||
}
|
||||
|
||||
setState {
|
||||
copy(
|
||||
asyncHomeServerLoginFlowRequest = Uninitialized,
|
||||
homeServerUrlFromUser = homeServerConnectionConfig.homeServerUri.toString(),
|
||||
homeServerUrl = data.homeServerUrl,
|
||||
loginMode = loginMode,
|
||||
loginModeSupportedTypes = data.supportedLoginTypes.toList()
|
||||
)
|
||||
}
|
||||
if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported) ||
|
||||
data.isOutdatedHomeserver) {
|
||||
// Notify the UI
|
||||
_viewEvents.post(OnboardingViewEvents.OutdatedHomeserver)
|
||||
}
|
||||
_viewEvents.post(OnboardingViewEvents.OnLoginFlowRetrieved)
|
||||
}
|
||||
}
|
||||
|
||||
fun getInitialHomeServerUrl(): String? {
|
||||
return loginConfig?.homeServerUrl
|
||||
}
|
||||
|
||||
fun getSsoUrl(redirectUrl: String, deviceId: String?, providerId: String?): String? {
|
||||
return authenticationService.getSsoUrl(redirectUrl, deviceId, providerId)
|
||||
}
|
||||
|
||||
fun getFallbackUrl(forSignIn: Boolean, deviceId: String?): String? {
|
||||
return authenticationService.getFallbackUrl(forSignIn, deviceId)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.app.features.onboarding
|
||||
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import com.airbnb.mvrx.PersistState
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import im.vector.app.features.login.LoginMode
|
||||
import im.vector.app.features.login.ServerType
|
||||
import im.vector.app.features.login.SignMode
|
||||
|
||||
data class OnboardingViewState(
|
||||
val asyncLoginAction: Async<Unit> = Uninitialized,
|
||||
val asyncHomeServerLoginFlowRequest: Async<Unit> = Uninitialized,
|
||||
val asyncResetPassword: Async<Unit> = Uninitialized,
|
||||
val asyncResetMailConfirmed: Async<Unit> = Uninitialized,
|
||||
val asyncRegistration: Async<Unit> = Uninitialized,
|
||||
|
||||
// User choices
|
||||
@PersistState
|
||||
val serverType: ServerType = ServerType.Unknown,
|
||||
@PersistState
|
||||
val signMode: SignMode = SignMode.Unknown,
|
||||
@PersistState
|
||||
val resetPasswordEmail: String? = null,
|
||||
@PersistState
|
||||
val homeServerUrlFromUser: String? = null,
|
||||
|
||||
// Can be modified after a Wellknown request
|
||||
@PersistState
|
||||
val homeServerUrl: String? = null,
|
||||
|
||||
// For SSO session recovery
|
||||
@PersistState
|
||||
val deviceId: String? = null,
|
||||
|
||||
// Network result
|
||||
@PersistState
|
||||
val loginMode: LoginMode = LoginMode.Unknown,
|
||||
// Supported types for the login. We cannot use a sealed class for LoginType because it is not serializable
|
||||
@PersistState
|
||||
val loginModeSupportedTypes: List<String> = emptyList(),
|
||||
val knownCustomHomeServersUrls: List<String> = emptyList()
|
||||
) : MavericksState {
|
||||
|
||||
fun isLoading(): Boolean {
|
||||
return asyncLoginAction is Loading ||
|
||||
asyncHomeServerLoginFlowRequest is Loading ||
|
||||
asyncResetPassword is Loading ||
|
||||
asyncResetMailConfirmed is Loading ||
|
||||
asyncRegistration is Loading ||
|
||||
// Keep loading when it is success because of the delay to switch to the next Activity
|
||||
asyncLoginAction is Success
|
||||
}
|
||||
|
||||
fun isUserLogged(): Boolean {
|
||||
return asyncLoginAction is Success
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue