move loading view state to own view model, so we're more free how to show it, and we can better reuse it

This commit is contained in:
Bruno Windels 2020-04-23 09:06:04 +02:00
parent acc511e69f
commit 657ec9aa62
4 changed files with 135 additions and 103 deletions

View file

@ -3,19 +3,7 @@ import {LoadStatus, LoginFailure} from "../matrix/SessionContainer.js";
import {AbortError} from "../utils/error.js"; import {AbortError} from "../utils/error.js";
import {loadLabel} from "./common.js"; import {loadLabel} from "./common.js";
function loadLoginLabel(loadStatus, loadError, loginFailure, homeserver) {
if (!loadError && loadStatus && loadStatus.get() === LoadStatus.LoginFailed) {
switch (loginFailure) {
case LoginFailure.LoginFailure:
return `Your username and/or password don't seem to be correct.`;
case LoginFailure.Connection:
return `Can't connect to ${homeserver}.`;
case LoginFailure.Unknown:
return `Something went wrong while checking your login and password.`;
}
}
return loadLabel(loadStatus, loadError);
}
export class LoginViewModel extends EventEmitter { export class LoginViewModel extends EventEmitter {
constructor({sessionCallback, defaultHomeServer, createSessionContainer}) { constructor({sessionCallback, defaultHomeServer, createSessionContainer}) {
@ -81,6 +69,28 @@ export class LoginViewModel extends EventEmitter {
} }
get loadLabel() { get loadLabel() {
const sc = this._sessionContainer;
const error = this._error || (sc && sc.loadError);
if (error || (sc && sc.loadStatus.get() === LoadStatus.Error)) {
return `Something went wrong: ${error && error.message}.`;
}
if (loadStatus) {
switch (loadStatus.get()) {
case LoadStatus.NotLoading:
return `Preparing…`;
case LoadStatus.Login:
return `Checking your login and password…`;
case LoadStatus.Loading:
return `Loading your conversations…`;
case LoadStatus.FirstSync:
return `Getting your conversations from the server…`;
default:
return this._sessionContainer.loadStatus.get();
}
}
return `Preparing…`;
if (this._error) { if (this._error) {
return loadLabel(null, this._error); return loadLabel(null, this._error);
} }

View file

@ -0,0 +1,106 @@
import {EventEmitter} from "../utils/EventEmitter.js";
import {LoadStatus, LoginFailure} from "../matrix/SessionContainer.js";
import {SyncStatus} from "../matrix/Sync.js";
export class SessionLoadViewModel extends EventEmitter {
constructor({createAndStartSessionContainer, sessionCallback, homeserver}) {
super();
this._createAndStartSessionContainer = createAndStartSessionContainer;
this._sessionCallback = sessionCallback;
this._homeserver = homeserver;
this._loading = false;
this._error = null;
}
async start() {
if (this._loading) {
return;
}
try {
this._loading = true;
this.emit("change");
this._sessionContainer = this._createAndStartSessionContainer();
this._waitHandle = this._sessionContainer.loadStatus.waitFor(s => {
this.emit("change");
// wait for initial sync, but not catchup sync
const isCatchupSync = s === LoadStatus.FirstSync &&
this._sessionContainer.sync.status === SyncStatus.CatchupSync;
return isCatchupSync ||
s === LoadStatus.LoginFailed ||
s === LoadStatus.Error ||
s === LoadStatus.Ready;
});
try {
await this._waitHandle.promise;
} catch (err) {
// swallow AbortError
}
// TODO: should we deal with no connection during initial sync
// and we're retrying as well here?
// e.g. show in the label what is going on wrt connectionstatus
// much like we will once you are in the app. Probably a good idea
// did it finish or get stuck at LoginFailed or Error?
const loadStatus = this._sessionContainer.loadStatus.get();
if (loadStatus === LoadStatus.FirstSync || loadStatus === LoadStatus.Ready) {
this._sessionCallback(this._sessionContainer);
}
} catch (err) {
this._error = err;
} finally {
this._loading = false;
this.emit("change");
}
}
get loading() {
return this._loading;
}
goBack() {
if (this._sessionContainer) {
this._sessionContainer.stop();
this._sessionContainer = null;
if (this._waitHandle) {
this._waitHandle.dispose();
}
}
this._sessionCallback();
}
get loadLabel() {
const sc = this._sessionContainer;
const error = this._error || (sc && sc.loadError);
if (error || (sc && sc.loadStatus.get() === LoadStatus.Error)) {
return `Something went wrong: ${error && error.message}.`;
}
if (sc) {
switch (sc.loadStatus.get()) {
case LoadStatus.NotLoading:
return `Preparing…`;
case LoadStatus.Login:
return `Checking your login and password…`;
case LoadStatus.LoginFailed:
switch (sc.loginFailure) {
case LoginFailure.LoginFailure:
return `Your username and/or password 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 login and password.`;
}
break;
case LoadStatus.Loading:
return `Loading your conversations…`;
case LoadStatus.FirstSync:
return `Getting your conversations from the server…`;
default:
return this._sessionContainer.loadStatus.get();
}
}
return `Preparing…`;
}
}

View file

@ -1,8 +1,5 @@
import {SortedArray} from "../observable/index.js"; import {SortedArray} from "../observable/index.js";
import {EventEmitter} from "../utils/EventEmitter.js"; import {EventEmitter} from "../utils/EventEmitter.js";
import {LoadStatus} from "../matrix/SessionContainer.js";
import {SyncStatus} from "../matrix/Sync.js";
import {loadLabel} from "./common.js";
class SessionItemViewModel extends EventEmitter { class SessionItemViewModel extends EventEmitter {
constructor(sessionInfo, pickerVM) { constructor(sessionInfo, pickerVM) {
@ -100,68 +97,6 @@ class SessionItemViewModel extends EventEmitter {
} }
} }
class LoadViewModel extends EventEmitter {
constructor({createSessionContainer, sessionCallback, sessionId}) {
super();
this._createSessionContainer = createSessionContainer;
this._sessionCallback = sessionCallback;
this._sessionId = sessionId;
this._loading = false;
}
async _start() {
try {
this._loading = true;
this.emit("change", "loading");
this._sessionContainer = this._createSessionContainer();
this._sessionContainer.startWithExistingSession(this._sessionId);
this._waitHandle = this._sessionContainer.loadStatus.waitFor(s => {
this.emit("change", "loadStatus");
// wait for initial sync, but not catchup sync
const isCatchupSync = s === LoadStatus.FirstSync &&
this._sessionContainer.sync.status === SyncStatus.CatchupSync;
return isCatchupSync ||
s === LoadStatus.Error ||
s === LoadStatus.Ready;
});
try {
await this._waitHandle.promise;
} catch (err) {
// swallow AbortError
}
if (this._sessionContainer.loadStatus.get() !== LoadStatus.Error) {
this._sessionCallback(this._sessionContainer);
}
} catch (err) {
this._error = err;
} finally {
this._loading = false;
this.emit("change", "loading");
}
}
get loading() {
return this._loading;
}
goBack() {
if (this._sessionContainer) {
this._sessionContainer.stop();
this._sessionContainer = null;
if (this._waitHandle) {
this._waitHandle.dispose();
}
}
this._sessionCallback();
}
get loadLabel() {
const sc = this._sessionContainer;
return loadLabel(
sc && sc.loadStatus,
sc && sc.loadError || this._error);
}
}
export class SessionPickerViewModel extends EventEmitter { export class SessionPickerViewModel extends EventEmitter {
constructor({storageFactory, sessionInfoStorage, sessionCallback, createSessionContainer}) { constructor({storageFactory, sessionInfoStorage, sessionCallback, createSessionContainer}) {
@ -193,7 +128,11 @@ export class SessionPickerViewModel extends EventEmitter {
const sessionVM = this._sessions.array.find(s => s.id === id); const sessionVM = this._sessions.array.find(s => s.id === id);
if (sessionVM) { if (sessionVM) {
this._loadViewModel = new LoadViewModel({ this._loadViewModel = new LoadViewModel({
createSessionContainer: this._createSessionContainer, createAndStartSessionContainer: () => {
const sessionContainer = this._createSessionContainer();
sessionContainer.startWithExistingSession(sessionVM.id);
return sessionContainer;
},
sessionCallback: sessionContainer => { sessionCallback: sessionContainer => {
if (sessionContainer) { if (sessionContainer) {
// make parent view model move away // make parent view model move away
@ -203,8 +142,7 @@ export class SessionPickerViewModel extends EventEmitter {
this._loadViewModel = null; this._loadViewModel = null;
this.emit("change", "loadViewModel"); this.emit("change", "loadViewModel");
} }
}, }
sessionId: sessionVM.id,
}); });
this._loadViewModel.start(); this._loadViewModel.start();
this.emit("change", "loadViewModel"); this.emit("change", "loadViewModel");

View file

@ -1,22 +0,0 @@
import {LoadStatus} from "../matrix/SessionContainer.js";
export function loadLabel(loadStatus, loadError) {
if (loadError || loadStatus.get() === LoadStatus.Error) {
return `Something went wrong: ${loadError && loadError.message}.`;
}
if (loadStatus) {
switch (loadStatus.get()) {
case LoadStatus.NotLoading:
return `Preparing…`;
case LoadStatus.Login:
return `Checking your login and password…`;
case LoadStatus.Loading:
return `Loading your conversations…`;
case LoadStatus.FirstSync:
return `Getting your conversations from the server…`;
default:
return this._sessionContainer.loadStatus.get();
}
}
return `Preparing…`;
}