From bdc860eb798fdae49bd08c0e6983e6aa31791bea Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Fri, 20 Aug 2021 15:19:42 +0530 Subject: [PATCH] Refactor to pull loadvm into login vm Signed-off-by: RMidhunSuresh --- src/domain/RootViewModel.js | 8 +-- src/domain/SessionLoadViewModel.js | 21 +------ src/domain/login/CompleteSSOLoginViewModel.js | 45 +++++++------ src/domain/login/LoginViewModel.js | 63 +++++++++++++++++-- src/domain/login/PasswordLoginViewModel.js | 58 ++++++++--------- src/matrix/SessionContainer.js | 10 +++ src/platform/web/ui/login/LoginView.js | 5 +- .../web/ui/login/PasswordLoginView.js | 5 +- 8 files changed, 128 insertions(+), 87 deletions(-) diff --git a/src/domain/RootViewModel.js b/src/domain/RootViewModel.js index 24982a71..4a0a969e 100644 --- a/src/domain/RootViewModel.js +++ b/src/domain/RootViewModel.js @@ -133,13 +133,11 @@ export class RootViewModel extends ViewModel { } _showSessionLoader(sessionId) { + const sessionContainer = this._createSessionContainer(); + sessionContainer.startWithExistingSession(sessionId); this._setSection(() => { this._sessionLoadViewModel = new SessionLoadViewModel(this.childOptions({ - createAndStartSessionContainer: () => { - const sessionContainer = this._createSessionContainer(); - sessionContainer.startWithExistingSession(sessionId); - return sessionContainer; - }, + sessionContainer, ready: sessionContainer => this._showSession(sessionContainer) })); this._sessionLoadViewModel.start(); diff --git a/src/domain/SessionLoadViewModel.js b/src/domain/SessionLoadViewModel.js index 80837677..956ba3a4 100644 --- a/src/domain/SessionLoadViewModel.js +++ b/src/domain/SessionLoadViewModel.js @@ -14,15 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {LoadStatus, LoginFailure} from "../matrix/SessionContainer.js"; +import {LoadStatus} from "../matrix/SessionContainer.js"; import {SyncStatus} from "../matrix/Sync.js"; import {ViewModel} from "./ViewModel.js"; export class SessionLoadViewModel extends ViewModel { constructor(options) { super(options); - const {createAndStartSessionContainer, ready, homeserver, deleteSessionOnCancel} = options; - this._createAndStartSessionContainer = createAndStartSessionContainer; + const {sessionContainer, ready, homeserver, deleteSessionOnCancel} = options; + this._sessionContainer = sessionContainer; this._ready = ready; this._homeserver = homeserver; this._deleteSessionOnCancel = deleteSessionOnCancel; @@ -38,7 +38,6 @@ export class SessionLoadViewModel extends ViewModel { try { this._loading = true; this.emitChange("loading"); - this._sessionContainer = await this._createAndStartSessionContainer(); this._waitHandle = this._sessionContainer.loadStatus.waitFor(s => { this.emitChange("loadLabel"); // wait for initial sync, but not catchup sync @@ -111,20 +110,6 @@ export class SessionLoadViewModel extends ViewModel { if (sc) { switch (sc.loadStatus.get()) { - case LoadStatus.NotLoading: - return `Preparing…`; - case LoadStatus.Login: - return `Checking your credentials…`; - case LoadStatus.LoginFailed: - switch (sc.loginFailure) { - case LoginFailure.LoginFailure: - return `Your credentials don't seem to be correct.`; - case LoginFailure.Connection: - return `Can't connect to ${this._homeserver}.`; - case LoginFailure.Unknown: - return `Something went wrong while checking your credentials.`; - } - break; case LoadStatus.SessionSetup: return `Setting up your encryption keys…`; case LoadStatus.Loading: diff --git a/src/domain/login/CompleteSSOLoginViewModel.js b/src/domain/login/CompleteSSOLoginViewModel.js index bf98cefc..90d25fda 100644 --- a/src/domain/login/CompleteSSOLoginViewModel.js +++ b/src/domain/login/CompleteSSOLoginViewModel.js @@ -15,7 +15,7 @@ limitations under the License. */ import {ViewModel} from "../ViewModel.js"; -import {SessionLoadViewModel} from "../SessionLoadViewModel.js"; +import {LoginFailure} from "../../matrix/SessionContainer.js"; export class CompleteSSOLoginViewModel extends ViewModel { constructor(options) { @@ -24,17 +24,17 @@ export class CompleteSSOLoginViewModel extends ViewModel { loginToken, sessionContainer, ready, + attemptLogin, + showError, } = options; this._loginToken = loginToken; this._ready = ready; this._sessionContainer = sessionContainer; - this._loadViewModelSubscription = null; - this._loadViewModel = null; + this._attemptLogin = attemptLogin; + this._showError = showError; this.performSSOLoginCompletion(); } - get loadViewModel() { return this._loadViewModel; } - async performSSOLoginCompletion() { if (!this._loginToken) { return; @@ -46,25 +46,22 @@ export class CompleteSSOLoginViewModel extends ViewModel { this.navigation.applyPath(path); return; } - this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription); - if (this._loadViewModel) { - this._loadViewModel = this.disposeTracked(this._loadViewModel); + const status = await this._attemptLogin(loginOptions.token(this._loginToken)); + let error = ""; + switch (status) { + case LoginFailure.Credentials: + error = `Your login-token is invalid.`; + break; + case LoginFailure.Connection: + error = `Can't connect to ${this._homeserver}.`; + break; + case LoginFailure.Unknown: + error = `Something went wrong while checking your login-token.`; + break; + } + if (error) { + this._showError(error); + this._sessionContainer.resetStatus(); } - this._loadViewModel = this.track(new SessionLoadViewModel(this.childOptions({ - createAndStartSessionContainer: async () => { - this._sessionContainer.startWithLogin(loginOptions.token(this._loginToken)); - return this._sessionContainer; - }, - ready: this._ready, - homeserver, - }))); - this._loadViewModel.start(); - this.emitChange("loadViewModel"); - this._loadViewModelSubscription = this.track(this._loadViewModel.disposableOn("change", () => { - if (!this._loadViewModel.loading) { - this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription); - } - this.emitChange("isBusy"); - })); } } diff --git a/src/domain/login/LoginViewModel.js b/src/domain/login/LoginViewModel.js index d6cd6eed..63c4d253 100644 --- a/src/domain/login/LoginViewModel.js +++ b/src/domain/login/LoginViewModel.js @@ -18,6 +18,8 @@ import {ViewModel} from "../ViewModel.js"; import {PasswordLoginViewModel} from "./PasswordLoginViewModel.js"; import {StartSSOLoginViewModel} from "./StartSSOLoginViewModel.js"; import {CompleteSSOLoginViewModel} from "./CompleteSSOLoginViewModel.js"; +import {LoadStatus} from "../../matrix/SessionContainer.js"; +import {SessionLoadViewModel} from "../SessionLoadViewModel.js"; export class LoginViewModel extends ViewModel { constructor(options) { @@ -31,8 +33,12 @@ export class LoginViewModel extends ViewModel { this._passwordLoginViewModel = null; this._startSSOLoginViewModel = null; this._completeSSOLoginViewModel = null; + this._loadViewModel = null; + this._loadViewModelSubscription = null; this._homeserver = defaultHomeServer; this._errorMessage = ""; + this._hideHomeserver = false; + this._isBusy = false; this._createViewModels(this._homeserver); } @@ -41,11 +47,14 @@ export class LoginViewModel extends ViewModel { get completeSSOLoginViewModel(){ return this._completeSSOLoginViewModel; } get defaultHomeServer() { return this._homeserver; } get errorMessage() { return this._errorMessage; } - get showHomeserver() { return !this._completeSSOLoginViewModel; } + get showHomeserver() { return !this._hideHomeserver; } get cancelUrl() { return this.urlCreator.urlForSegment("session"); } + get loadViewModel() {return this._loadViewModel; } + get isBusy() { return this._isBusy; } async _createViewModels(homeserver) { if (this._loginToken) { + this._hideHomeserver = true; this._completeSSOLoginViewModel = this.track(new CompleteSSOLoginViewModel(this.childOptions({loginToken: this._loginToken}))); this.emitChange("completeSSOLoginViewModel"); } @@ -61,11 +70,11 @@ export class LoginViewModel extends ViewModel { if (this._loginOptions.sso) { this._showSSOLogin(); } if (this._loginOptions.password) { this._showPasswordLogin(); } if (!this._loginOptions.sso && !this._loginOptions.password) { - this._showError("This homeserver neither supports SSO nor Password based login flows"); + this.showError("This homeserver neither supports SSO nor Password based login flows"); } } else { - this._showError("Could not query login methods supported by the homeserver"); + this.showError("Could not query login methods supported by the homeserver"); } } } @@ -80,14 +89,56 @@ export class LoginViewModel extends ViewModel { this.emitChange("startSSOLoginViewModel"); } - _showError(message) { + showError(message) { this._errorMessage = message; this.emitChange("errorMessage"); + this._errorMessage = ""; + } + + _toggleBusy(status) { + this._isBusy = status; + this.emitChange("isBusy"); + } + + async attemptLogin(loginMethod) { + this._toggleBusy(true); + this._sessionContainer.startWithLogin(loginMethod); + const loadStatus = this._sessionContainer.loadStatus; + const handle = loadStatus.waitFor(status => status !== LoadStatus.Login); + await handle.promise; + this._toggleBusy(false); + const status = loadStatus.get(); + if (status === LoadStatus.LoginFailed) { + return this._sessionContainer.loginFailure; + } + this._hideHomeserver = true; + this._disposeViewModels(); + this._createLoadViewModel(); + return null; + } + + _createLoadViewModel() { + this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription); + if (this._loadViewModel) { + this._loadViewModel = this.disposeTracked(this._loadViewModel); + } + this._loadViewModel = this.track(new SessionLoadViewModel(this.childOptions())); + this._loadViewModel.start(); + this.emitChange("loadViewModel"); + this._loadViewModelSubscription = this.track( + this._loadViewModel.disposableOn("change", () => { + if (!this._loadViewModel.loading) { + this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription); + } + this.emitChange("isBusy"); + }) + ); } _disposeViewModels() { this._startSSOLoginViewModel = this.disposeTracked(this._ssoLoginViewModel); this._passwordLoginViewModel = this.disposeTracked(this._passwordLoginViewModel); + this._completeSSOLoginViewModel = this.disposeTracked(this._completeSSOLoginViewModel); this.emitChange("disposeViewModels"); } @@ -107,7 +158,9 @@ export class LoginViewModel extends ViewModel { }, sessionContainer: this._sessionContainer, loginOptions: this._loginOptions, - homeserver: this._homeserver + homeserver: this._homeserver, + attemptLogin: loginMethod => this.attemptLogin(loginMethod), + showError: message => this.showError(message) } } diff --git a/src/domain/login/PasswordLoginViewModel.js b/src/domain/login/PasswordLoginViewModel.js index f53869ec..99f54826 100644 --- a/src/domain/login/PasswordLoginViewModel.js +++ b/src/domain/login/PasswordLoginViewModel.js @@ -15,51 +15,47 @@ limitations under the License. */ import {ViewModel} from "../ViewModel.js"; -import {SessionLoadViewModel} from "../SessionLoadViewModel.js"; +import {LoginFailure} from "../../matrix/SessionContainer.js"; export class PasswordLoginViewModel extends ViewModel { constructor(options) { super(options); - const {ready, loginOptions, sessionContainer, homeserver} = options; + const {ready, loginOptions, sessionContainer, homeserver, attemptLogin, showError} = options; this._ready = ready; this._sessionContainer = sessionContainer; - this._loadViewModel = null; - this._loadViewModelSubscription = null; this._loginOptions = loginOptions; + this._attemptLogin = attemptLogin; + this._showError = showError; this._homeserver = homeserver; + this._isBusy = false; } - get loadViewModel() {return this._loadViewModel; } + get isBusy() { return this._isBusy; } - get isBusy() { - if (!this._loadViewModel) { - return false; - } else { - return this._loadViewModel.loading; - } + _toggleBusy(state) { + this._isBusy = state; + this.emitChange("isBusy"); } async login(username, password) { - const homeserver = this._homeserver; - this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription); - if (this._loadViewModel) { - this._loadViewModel = this.disposeTracked(this._loadViewModel); + this._toggleBusy(true); + const status = await this._attemptLogin(this._loginOptions.password(username, password)); + this._toggleBusy(false); + let error = ""; + switch (status) { + case LoginFailure.Credentials: + error = `Your credentials don't seem to be correct.`; + break; + case LoginFailure.Connection: + error = `Can't connect to ${this._homeserver}.`; + break; + case LoginFailure.Unknown: + error = `Something went wrong while checking your credentials.`; + break; + } + if (error) { + this._showError(error); + this._sessionContainer.resetStatus(); } - this._loadViewModel = this.track(new SessionLoadViewModel(this.childOptions({ - createAndStartSessionContainer: async () => { - this._sessionContainer.startWithLogin(this._loginOptions.password(username, password)); - return this._sessionContainer; - }, - ready: this._ready, - homeserver, - }))); - this._loadViewModel.start(); - this.emitChange("loadViewModel"); - this._loadViewModelSubscription = this.track(this._loadViewModel.disposableOn("change", () => { - if (!this._loadViewModel.loading) { - this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription); - } - this.emitChange("isBusy"); - })); } } diff --git a/src/matrix/SessionContainer.js b/src/matrix/SessionContainer.js index 40bc1be0..c078f95f 100644 --- a/src/matrix/SessionContainer.js +++ b/src/matrix/SessionContainer.js @@ -300,6 +300,10 @@ export class SessionContainer { return this._error; } + get loginFailure() { + return this._loginFailure; + } + /** only set at loadStatus InitialSync, CatchupSync or Ready */ get sync() { return this._sync; @@ -349,4 +353,10 @@ export class SessionContainer { this._sessionId = null; } } + + resetStatus() { + this._status.set(LoadStatus.NotLoading); + this._error = null; + this._loginFailure = null; + } } diff --git a/src/platform/web/ui/login/LoginView.js b/src/platform/web/ui/login/LoginView.js index bd087dd2..ad7f72a4 100644 --- a/src/platform/web/ui/login/LoginView.js +++ b/src/platform/web/ui/login/LoginView.js @@ -18,6 +18,7 @@ import {TemplateView} from "../general/TemplateView.js"; import {hydrogenGithubLink} from "./common.js"; import {PasswordLoginView} from "./PasswordLoginView.js"; import {CompleteSSOView} from "./CompleteSSOView.js"; +import {SessionLoadStatusView} from "./SessionLoadStatusView.js"; export class LoginView extends TemplateView { render(t, vm) { @@ -34,6 +35,7 @@ export class LoginView extends TemplateView { type: "text", placeholder: vm.i18n`Your matrix homeserver`, value: vm.defaultHomeServer, + disabled: vm => vm.isBusy, onChange: event => vm.updateHomeServer(event.target.value), }) ] @@ -41,7 +43,8 @@ export class LoginView extends TemplateView { t.mapView(vm => vm.passwordLoginViewModel, vm => vm ? new PasswordLoginView(vm): null), t.if(vm => vm.passwordLoginViewModel && vm.startSSOLoginViewModel, t => t.p({className: "LoginView_separator"}, vm.i18n`or`)), t.mapView(vm => vm.startSSOLoginViewModel, vm => vm ? new StartSSOLoginView(vm) : null), - t.if(vm => vm.errorMessage, (t, vm) => t.h5({className: "LoginView_error"}, vm.i18n(vm.errorMessage))), + t.if(vm => vm.errorMessage, (t, vm) => t.p({className: "LoginView_error"}, vm.i18n(vm.errorMessage))), + t.mapView(vm => vm.loadViewModel, loadViewModel => loadViewModel ? new SessionLoadStatusView(loadViewModel) : null), // use t.mapView rather than t.if to create a new view when the view model changes too t.p(hydrogenGithubLink(t)) ]); diff --git a/src/platform/web/ui/login/PasswordLoginView.js b/src/platform/web/ui/login/PasswordLoginView.js index 0f330993..ca26d1cc 100644 --- a/src/platform/web/ui/login/PasswordLoginView.js +++ b/src/platform/web/ui/login/PasswordLoginView.js @@ -15,7 +15,6 @@ limitations under the License. */ import {TemplateView} from "../general/TemplateView.js"; -import {SessionLoadStatusView} from "./SessionLoadStatusView.js"; export class PasswordLoginView extends TemplateView { render(t, vm) { @@ -43,11 +42,11 @@ export class PasswordLoginView extends TemplateView { }, [ t.div({ className: "form-row" }, [t.label({ for: "username" }, vm.i18n`Username`), username]), t.div({ className: "form-row" }, [t.label({ for: "password" }, vm.i18n`Password`), password]), - t.mapView(vm => vm.loadViewModel, loadViewModel => loadViewModel ? new SessionLoadStatusView(loadViewModel) : null), t.div({ className: "button-row" }, [ t.button({ className: "button-action primary", - type: "submit" + type: "submit", + disabled }, vm.i18n`Log In`), ]), ])