diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt index 1c2f1d53f0..b0c2dc546a 100644 --- a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt @@ -17,11 +17,8 @@ package im.vector.riotx.core.platform import com.airbnb.mvrx.* -import im.vector.matrix.android.api.util.CancelableBag -import im.vector.riotx.BuildConfig import io.reactivex.Observable import io.reactivex.Single -import io.reactivex.disposables.Disposable abstract class VectorViewModel(initialState: S) : BaseMvRxViewModel(initialState, false) { diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginActions.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginActions.kt new file mode 100644 index 0000000000..be04e6c029 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginActions.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.riotx.features.login + +sealed class LoginActions { + + data class UpdateHomeServer(val homeServerUrl: String) : LoginActions() + data class Login(val login: String, val password: String) : LoginActions() + +} diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt index 0471ab6a8a..d803b451ac 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt @@ -20,26 +20,18 @@ import android.os.Bundle import android.view.View import android.widget.Toast import androidx.core.view.isVisible -import arrow.core.Try -import com.airbnb.mvrx.activityViewModel -import com.airbnb.mvrx.withState +import com.airbnb.mvrx.* +import com.jakewharton.rxbinding3.view.focusChanges import com.jakewharton.rxbinding3.widget.textChanges -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.session.Session import im.vector.riotx.R -import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.error.ErrorFormatter -import im.vector.riotx.core.extensions.configureAndStart import im.vector.riotx.core.extensions.setTextWithColoredPart import im.vector.riotx.core.extensions.showPassword import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.utils.openUrlInExternalBrowser import im.vector.riotx.features.home.HomeActivity import im.vector.riotx.features.homeserver.ServerUrlsRepository -import im.vector.riotx.features.notifications.PushRuleTriggerListener import io.reactivex.Observable import io.reactivex.functions.Function3 import io.reactivex.rxkotlin.subscribeBy @@ -55,9 +47,6 @@ class LoginFragment : VectorBaseFragment() { private val viewModel: LoginViewModel by activityViewModel() - @Inject lateinit var authenticator: Authenticator - @Inject lateinit var activeSessionHolder: ActiveSessionHolder - @Inject lateinit var pushRuleTriggerListener: PushRuleTriggerListener private var passwordShown = false @Inject lateinit var errorFormatter: ErrorFormatter @@ -75,13 +64,19 @@ class LoginFragment : VectorBaseFragment() { setupNotice() setupAuthButton() setupPasswordReveal() + + homeServerField.focusChanges() + .subscribe { + if (!it) { + // TODO Also when clicking on button? + viewModel.handle(LoginActions.UpdateHomeServer(homeServerField.text.toString())) + } + } + .disposeOnDestroy() + + homeServerField.setText(ServerUrlsRepository.getDefaultHomeServerUrl(requireContext())) - - -// viewModel.joinRoomErrorLiveData.observeEvent(this) { throwable -> -// Snackbar.make(publicRoomsCoordinator, errorFormatter.toHumanReadable(throwable), Snackbar.LENGTH_SHORT) -// .show() -// } + viewModel.handle(LoginActions.UpdateHomeServer(homeServerField.text.toString())) } private fun setupNotice() { @@ -93,42 +88,10 @@ class LoginFragment : VectorBaseFragment() { } private fun authenticate() { - passwordShown = false - renderPasswordField() - val login = loginField.text?.trim().toString() val password = passwordField.text?.trim().toString() - buildHomeServerConnectionConfig().fold( - { Toast.makeText(requireActivity(), "Authenticate failure: $it", Toast.LENGTH_LONG).show() }, - { authenticateWith(it, login, password) } - ) - } - private fun authenticateWith(homeServerConnectionConfig: HomeServerConnectionConfig, login: String, password: String) { - progressBar.isVisible = true - touchArea.isVisible = true - authenticator.authenticate(homeServerConnectionConfig, login, password, object : MatrixCallback { - override fun onSuccess(data: Session) { - activeSessionHolder.setActiveSession(data) - data.configureAndStart(pushRuleTriggerListener) - goToHome() - } - - override fun onFailure(failure: Throwable) { - progressBar.isVisible = false - touchArea.isVisible = false - Toast.makeText(requireActivity(), "Authenticate failure: $failure", Toast.LENGTH_LONG).show() - } - }) - } - - private fun buildHomeServerConnectionConfig(): Try { - return Try { - val homeServerUri = homeServerField.text?.trim().toString() - HomeServerConnectionConfig.Builder() - .withHomeServerUri(homeServerUri) - .build() - } + viewModel.handle(LoginActions.Login(login, password)) } private fun setupAuthButton() { @@ -164,14 +127,25 @@ class LoginFragment : VectorBaseFragment() { passwordReveal.setImageResource(if (passwordShown) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black) } - private fun goToHome() { - val intent = HomeActivity.newIntent(requireActivity()) - startActivity(intent) - requireActivity().finish() - } - - override fun invalidate() = withState(viewModel) { state -> + when (state.asyncLoginAction) { + is Loading -> { + progressBar.isVisible = true + touchArea.isVisible = true + passwordShown = false + renderPasswordField() + } + is Fail -> { + progressBar.isVisible = false + touchArea.isVisible = false + Toast.makeText(requireActivity(), "Authenticate failure: ${state.asyncLoginAction.error}", Toast.LENGTH_LONG).show() + } + is Success -> { + val intent = HomeActivity.newIntent(requireActivity()) + startActivity(intent) + requireActivity().finish() + } + } } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt index a34e112e57..cf73f2517a 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt @@ -16,14 +16,24 @@ package im.vector.riotx.features.login -import com.airbnb.mvrx.ActivityViewModelContext -import com.airbnb.mvrx.MvRxViewModelFactory -import com.airbnb.mvrx.ViewModelContext +import arrow.core.Try +import com.airbnb.mvrx.* import com.squareup.inject.assisted.Assisted 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.session.Session +import im.vector.matrix.android.api.util.Cancelable +import im.vector.riotx.core.di.ActiveSessionHolder +import im.vector.riotx.core.extensions.configureAndStart import im.vector.riotx.core.platform.VectorViewModel +import im.vector.riotx.features.notifications.PushRuleTriggerListener -class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginViewState) : VectorViewModel(initialState) { +class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginViewState, + val authenticator: Authenticator, + val activeSessionHolder: ActiveSessionHolder, + val pushRuleTriggerListener: PushRuleTriggerListener) : VectorViewModel(initialState) { @AssistedInject.Factory interface Factory { @@ -43,5 +53,87 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi } + var homeServerConnectionConfig: HomeServerConnectionConfig? = null + private var currentTask: Cancelable? = null + + + fun handle(action: LoginActions) { + when (action) { + is LoginActions.UpdateHomeServer -> handleUpdateHomeserver(action) + is LoginActions.Login -> handleLogin(action) + } + } + + private fun handleLogin(action: LoginActions.Login) { + val homeServerConnectionConfigFinal = homeServerConnectionConfig + + if (homeServerConnectionConfigFinal == null) { + setState { + copy( + asyncLoginAction = Fail(Throwable("Bad configuration")) + ) + } + } else { + setState { + copy( + asyncLoginAction = Loading() + ) + } + + authenticator.authenticate(homeServerConnectionConfigFinal, action.login, action.password, object : MatrixCallback { + override fun onSuccess(data: Session) { + activeSessionHolder.setActiveSession(data) + data.configureAndStart(pushRuleTriggerListener) + + setState { + copy( + asyncLoginAction = Success(Unit) + ) + } + } + + override fun onFailure(failure: Throwable) { + setState { + copy( + asyncLoginAction = Fail(failure) + ) + } + } + }) + } + } + + private fun handleUpdateHomeserver(action: LoginActions.UpdateHomeServer) { + Try { + val homeServerUri = action.homeServerUrl + homeServerConnectionConfig = HomeServerConnectionConfig.Builder() + .withHomeServerUri(homeServerUri) + .build() + } + + + // TODO Do request + + /* + currentTask?.cancel() + + setState { + copy( + asyncHomeServerLoginFlowRequest = Loading() + ) + } + + + // TODO currentTask = + + */ + } + + + override fun onCleared() { + super.onCleared() + + currentTask?.cancel() + } } \ No newline at end of file diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginViewState.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginViewState.kt index ea63f777b7..f296e5e064 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginViewState.kt @@ -21,7 +21,7 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized data class LoginViewState( - // Current pagination request + val asyncLoginAction: Async = Uninitialized, val asyncHomeServerLoginFlowRequest: Async = Uninitialized ) : MvRxState