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:
parent
acc511e69f
commit
657ec9aa62
4 changed files with 135 additions and 103 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
106
src/domain/SessionLoadViewModel.js
Normal file
106
src/domain/SessionLoadViewModel.js
Normal 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…`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
|
|
@ -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…`;
|
|
||||||
}
|
|
Reference in a new issue