From 657ec9aa6211b75a4f4f4e6bc1d6facf5a7531a1 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 23 Apr 2020 09:06:04 +0200 Subject: [PATCH] move loading view state to own view model, so we're more free how to show it, and we can better reuse it --- src/domain/LoginViewModel.js | 36 +++++---- src/domain/SessionLoadViewModel.js | 106 +++++++++++++++++++++++++++ src/domain/SessionPickerViewModel.js | 74 ++----------------- src/domain/common.js | 22 ------ 4 files changed, 135 insertions(+), 103 deletions(-) create mode 100644 src/domain/SessionLoadViewModel.js delete mode 100644 src/domain/common.js diff --git a/src/domain/LoginViewModel.js b/src/domain/LoginViewModel.js index f5488790..3d7853f3 100644 --- a/src/domain/LoginViewModel.js +++ b/src/domain/LoginViewModel.js @@ -3,19 +3,7 @@ import {LoadStatus, LoginFailure} from "../matrix/SessionContainer.js"; import {AbortError} from "../utils/error.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 { constructor({sessionCallback, defaultHomeServer, createSessionContainer}) { @@ -81,6 +69,28 @@ export class LoginViewModel extends EventEmitter { } 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) { return loadLabel(null, this._error); } diff --git a/src/domain/SessionLoadViewModel.js b/src/domain/SessionLoadViewModel.js new file mode 100644 index 00000000..a5d2e971 --- /dev/null +++ b/src/domain/SessionLoadViewModel.js @@ -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…`; + } +} diff --git a/src/domain/SessionPickerViewModel.js b/src/domain/SessionPickerViewModel.js index 7c122b16..1959ff85 100644 --- a/src/domain/SessionPickerViewModel.js +++ b/src/domain/SessionPickerViewModel.js @@ -1,8 +1,5 @@ import {SortedArray} from "../observable/index.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 { 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 { constructor({storageFactory, sessionInfoStorage, sessionCallback, createSessionContainer}) { @@ -193,7 +128,11 @@ export class SessionPickerViewModel extends EventEmitter { const sessionVM = this._sessions.array.find(s => s.id === id); if (sessionVM) { this._loadViewModel = new LoadViewModel({ - createSessionContainer: this._createSessionContainer, + createAndStartSessionContainer: () => { + const sessionContainer = this._createSessionContainer(); + sessionContainer.startWithExistingSession(sessionVM.id); + return sessionContainer; + }, sessionCallback: sessionContainer => { if (sessionContainer) { // make parent view model move away @@ -203,8 +142,7 @@ export class SessionPickerViewModel extends EventEmitter { this._loadViewModel = null; this.emit("change", "loadViewModel"); } - }, - sessionId: sessionVM.id, + } }); this._loadViewModel.start(); this.emit("change", "loadViewModel"); diff --git a/src/domain/common.js b/src/domain/common.js deleted file mode 100644 index 99ade58e..00000000 --- a/src/domain/common.js +++ /dev/null @@ -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…`; -}