Refactor to pull loadvm into login vm

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
This commit is contained in:
RMidhunSuresh 2021-08-20 15:19:42 +05:30
parent 80ea48e8a1
commit bdc860eb79
8 changed files with 128 additions and 87 deletions

View file

@ -133,13 +133,11 @@ export class RootViewModel extends ViewModel {
}
_showSessionLoader(sessionId) {
this._setSection(() => {
this._sessionLoadViewModel = new SessionLoadViewModel(this.childOptions({
createAndStartSessionContainer: () => {
const sessionContainer = this._createSessionContainer();
sessionContainer.startWithExistingSession(sessionId);
return sessionContainer;
},
this._setSection(() => {
this._sessionLoadViewModel = new SessionLoadViewModel(this.childOptions({
sessionContainer,
ready: sessionContainer => this._showSession(sessionContainer)
}));
this._sessionLoadViewModel.start();

View file

@ -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:

View file

@ -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;
}
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);
if (error) {
this._showError(error);
this._sessionContainer.resetStatus();
}
this.emitChange("isBusy");
}));
}
}

View file

@ -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)
}
}

View file

@ -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;
}
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);
if (error) {
this._showError(error);
this._sessionContainer.resetStatus();
}
this.emitChange("isBusy");
}));
}
}

View file

@ -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;
}
}

View file

@ -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))
]);

View file

@ -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`),
]),
])