forked from mystiq/hydrogen-web
Refactor to pull loadvm into login vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
This commit is contained in:
parent
80ea48e8a1
commit
bdc860eb79
8 changed files with 128 additions and 87 deletions
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
]);
|
||||
|
|
|
@ -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`),
|
||||
]),
|
||||
])
|
||||
|
|
Loading…
Reference in a new issue