From 1bec1033d12b8af2954f6282ace568018ef76c07 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 13:52:13 +0200 Subject: [PATCH 1/5] translate last-session to a real session id --- src/domain/navigation/URLRouter.js | 10 +++++++++- src/domain/navigation/index.js | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/domain/navigation/URLRouter.js b/src/domain/navigation/URLRouter.js index 36a1c244..ab6c9205 100644 --- a/src/domain/navigation/URLRouter.js +++ b/src/domain/navigation/URLRouter.js @@ -23,6 +23,14 @@ export class URLRouter { this._subscription = null; this._pathSubscription = null; this._isApplyingUrl = false; + this._defaultSessionId = this._getLastSessionId(); + + } + + _getLastSessionId() { + const urlPath = this._history.urlAsPath(this._history.getLastUrl()); + const navPath = this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path)); + return navPath.get("session")?.value; } attach() { @@ -50,7 +58,7 @@ export class URLRouter { _applyUrl(url) { const urlPath = this._history.urlAsPath(url) - const navPath = this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path)); + const navPath = this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path, this._defaultSessionId)); // this will cause _applyNavigationPath to be called, // so set a flag whether this request came from ourselves // (in which case it is a redirect if the url does not match the current one) diff --git a/src/domain/navigation/index.js b/src/domain/navigation/index.js index 5de73aef..34151c62 100644 --- a/src/domain/navigation/index.js +++ b/src/domain/navigation/index.js @@ -85,7 +85,7 @@ function roomsSegmentWithRoom(rooms, roomId, path) { } } -export function parseUrlPath(urlPath, currentNavPath) { +export function parseUrlPath(urlPath, currentNavPath, defaultSessionId) { // substr(1) to take of initial / const parts = urlPath.substr(1).split("/"); const iterator = parts[Symbol.iterator](); @@ -113,6 +113,14 @@ export function parseUrlPath(urlPath, currentNavPath) { segments.push(roomsSegmentWithRoom(rooms, roomId, currentNavPath)); } segments.push(new Segment("room", roomId)); + } else if (type === "last-session") { + let sessionSegment = currentNavPath.get("session"); + if (!sessionSegment?.value && defaultSessionId) { + sessionSegment = new Segment("session", defaultSessionId); + } + if (sessionSegment) { + segments.push(sessionSegment); + } } else { // might be undefined, which will be turned into true by Segment const value = iterator.next().value; From dd880529accca2d33a5c701d7ce301a47266969e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 13:52:31 +0200 Subject: [PATCH 2/5] make redirects in initial url be reflected in url bar --- src/domain/navigation/URLRouter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/domain/navigation/URLRouter.js b/src/domain/navigation/URLRouter.js index ab6c9205..1c60a467 100644 --- a/src/domain/navigation/URLRouter.js +++ b/src/domain/navigation/URLRouter.js @@ -35,8 +35,10 @@ export class URLRouter { attach() { this._subscription = this._history.subscribe(url => this._applyUrl(url)); - this._applyUrl(this._history.get()); + // subscribe to path before applying initial url + // so redirects in _applyNavigationPath are reflected in url bar this._pathSubscription = this._navigation.pathObservable.subscribe(path => this._applyNavigationPath(path)); + this._applyUrl(this._history.get()); } dispose() { From 2c7ea44afd273a3e7d2ad9484fc89df9f4329014 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 14:27:09 +0200 Subject: [PATCH 3/5] don't expose last url to root VM so it isn't confused by empty path instead, check the length of the path in a dedicated method in the router --- src/domain/RootViewModel.js | 8 +++--- src/domain/navigation/URLRouter.js | 41 ++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/domain/RootViewModel.js b/src/domain/RootViewModel.js index f6e566e3..8d9a032f 100644 --- a/src/domain/RootViewModel.js +++ b/src/domain/RootViewModel.js @@ -35,10 +35,10 @@ export class RootViewModel extends ViewModel { async load() { this.track(this.navigation.observe("login").subscribe(() => this._applyNavigation())); this.track(this.navigation.observe("session").subscribe(() => this._applyNavigation())); - this._applyNavigation(this.urlCreator.getLastUrl()); + this._applyNavigation(true); } - async _applyNavigation(restoreUrlIfAtDefault) { + async _applyNavigation(shouldRestoreLastUrl) { const isLogin = this.navigation.observe("login").get(); const sessionId = this.navigation.observe("session").get(); if (isLogin) { @@ -67,9 +67,7 @@ export class RootViewModel extends ViewModel { } } else { try { - if (restoreUrlIfAtDefault) { - this.urlCreator.pushUrl(restoreUrlIfAtDefault); - } else { + if (!(shouldRestoreLastUrl && this.urlCreator.tryRestoreLastUrl())) { const sessionInfos = await this.platform.sessionInfoStorage.getAll(); if (sessionInfos.length === 0) { this.navigation.push("login"); diff --git a/src/domain/navigation/URLRouter.js b/src/domain/navigation/URLRouter.js index 1c60a467..b38791a0 100644 --- a/src/domain/navigation/URLRouter.js +++ b/src/domain/navigation/URLRouter.js @@ -24,20 +24,22 @@ export class URLRouter { this._pathSubscription = null; this._isApplyingUrl = false; this._defaultSessionId = this._getLastSessionId(); - } _getLastSessionId() { - const urlPath = this._history.urlAsPath(this._history.getLastUrl()); - const navPath = this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path)); - return navPath.get("session")?.value; + const navPath = this._urlAsNavPath(this._history.getLastUrl() || ""); + const sessionId = navPath.get("session")?.value; + if (typeof sessionId === "string") { + return sessionId; + } + return null; } attach() { this._subscription = this._history.subscribe(url => this._applyUrl(url)); // subscribe to path before applying initial url - // so redirects in _applyNavigationPath are reflected in url bar - this._pathSubscription = this._navigation.pathObservable.subscribe(path => this._applyNavigationPath(path)); + // so redirects in _applyNavPathToHistory are reflected in url bar + this._pathSubscription = this._navigation.pathObservable.subscribe(path => this._applyNavPathToHistory(path)); this._applyUrl(this._history.get()); } @@ -46,7 +48,7 @@ export class URLRouter { this._pathSubscription = this._pathSubscription(); } - _applyNavigationPath(path) { + _applyNavPathToHistory(path) { const url = this.urlForPath(path); if (url !== this._history.get()) { if (this._isApplyingUrl) { @@ -58,10 +60,8 @@ export class URLRouter { } } - _applyUrl(url) { - const urlPath = this._history.urlAsPath(url) - const navPath = this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path, this._defaultSessionId)); - // this will cause _applyNavigationPath to be called, + _applyNavPathToNavigation(navPath) { + // this will cause _applyNavPathToHistory to be called, // so set a flag whether this request came from ourselves // (in which case it is a redirect if the url does not match the current one) this._isApplyingUrl = true; @@ -69,12 +69,27 @@ export class URLRouter { this._isApplyingUrl = false; } + _urlAsNavPath(url) { + const urlPath = this._history.urlAsPath(url); + return this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path, this._defaultSessionId)); + } + + _applyUrl(url) { + const navPath = this._urlAsNavPath(url); + this._applyNavPathToNavigation(navPath); + } + pushUrl(url) { this._history.pushUrl(url); } - getLastUrl() { - return this._history.getLastUrl(); + tryRestoreLastUrl() { + const lastNavPath = this._urlAsNavPath(this._history.getLastUrl() || ""); + if (lastNavPath.segments.length !== 0) { + this._applyNavPathToNavigation(navPath); + return true; + } + return false; } urlForSegments(segments) { From d567664d8bfa01fc90235d5338c1c3a3f6b79450 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 14:47:45 +0200 Subject: [PATCH 4/5] ensure there is a session id in last-session action --- src/domain/navigation/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/navigation/index.js b/src/domain/navigation/index.js index 34151c62..3a9b4a07 100644 --- a/src/domain/navigation/index.js +++ b/src/domain/navigation/index.js @@ -115,7 +115,7 @@ export function parseUrlPath(urlPath, currentNavPath, defaultSessionId) { segments.push(new Segment("room", roomId)); } else if (type === "last-session") { let sessionSegment = currentNavPath.get("session"); - if (!sessionSegment?.value && defaultSessionId) { + if (typeof sessionSegment?.value !== "string" && defaultSessionId) { sessionSegment = new Segment("session", defaultSessionId); } if (sessionSegment) { From c3ef8015666f7e592d5c3daf6dba41bd390e7f0e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 18 May 2021 15:27:31 +0200 Subject: [PATCH 5/5] offer back button when session loading fails --- src/domain/LoginViewModel.js | 4 ++-- src/domain/RootViewModel.js | 4 ++-- src/domain/SessionLoadViewModel.js | 1 + src/platform/web/ui/css/themes/element/theme.css | 4 ++++ src/platform/web/ui/login/SessionLoadView.js | 5 +++-- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/domain/LoginViewModel.js b/src/domain/LoginViewModel.js index c0bea189..1b52e1a5 100644 --- a/src/domain/LoginViewModel.js +++ b/src/domain/LoginViewModel.js @@ -46,7 +46,7 @@ export class LoginViewModel extends ViewModel { if (this._loadViewModel) { this._loadViewModel = this.disposeTracked(this._loadViewModel); } - this._loadViewModel = this.track(new SessionLoadViewModel({ + this._loadViewModel = this.track(new SessionLoadViewModel(this.childOptions({ createAndStartSessionContainer: () => { this._sessionContainer = this._createSessionContainer(); this._sessionContainer.startWithLogin(homeserver, username, password); @@ -58,7 +58,7 @@ export class LoginViewModel extends ViewModel { this._ready(sessionContainer); }, homeserver, - })); + }))); this._loadViewModel.start(); this.emitChange("loadViewModel"); this._loadViewModelSubscription = this.track(this._loadViewModel.disposableOn("change", () => { diff --git a/src/domain/RootViewModel.js b/src/domain/RootViewModel.js index 8d9a032f..fca8d779 100644 --- a/src/domain/RootViewModel.js +++ b/src/domain/RootViewModel.js @@ -124,14 +124,14 @@ export class RootViewModel extends ViewModel { _showSessionLoader(sessionId) { this._setSection(() => { - this._sessionLoadViewModel = new SessionLoadViewModel({ + this._sessionLoadViewModel = new SessionLoadViewModel(this.childOptions({ createAndStartSessionContainer: () => { const sessionContainer = this._createSessionContainer(); sessionContainer.startWithExistingSession(sessionId); return sessionContainer; }, ready: sessionContainer => this._showSession(sessionContainer) - }); + })); this._sessionLoadViewModel.start(); }); } diff --git a/src/domain/SessionLoadViewModel.js b/src/domain/SessionLoadViewModel.js index d8736077..0b785e47 100644 --- a/src/domain/SessionLoadViewModel.js +++ b/src/domain/SessionLoadViewModel.js @@ -28,6 +28,7 @@ export class SessionLoadViewModel extends ViewModel { this._deleteSessionOnCancel = deleteSessionOnCancel; this._loading = false; this._error = null; + this.backUrl = this.urlCreator.urlForSegment("session", true); } async start() { diff --git a/src/platform/web/ui/css/themes/element/theme.css b/src/platform/web/ui/css/themes/element/theme.css index 706bd90d..c6c18445 100644 --- a/src/platform/web/ui/css/themes/element/theme.css +++ b/src/platform/web/ui/css/themes/element/theme.css @@ -215,6 +215,10 @@ a.button-action { text-align: center; } +.SessionLoadView { + padding-top: 16px; +} + @media screen and (min-width: 600px) { .PreSessionScreen { box-shadow: 0px 6px 32px rgba(0, 0, 0, 0.1); diff --git a/src/platform/web/ui/login/SessionLoadView.js b/src/platform/web/ui/login/SessionLoadView.js index 30489335..6837cf21 100644 --- a/src/platform/web/ui/login/SessionLoadView.js +++ b/src/platform/web/ui/login/SessionLoadView.js @@ -22,9 +22,10 @@ export class SessionLoadView extends TemplateView { return t.div({className: "PreSessionScreen"}, [ t.div({className: "logo"}), t.div({className: "SessionLoadView"}, [ - t.h1(vm.i18n`Loading…`), t.view(new SessionLoadStatusView(vm)) - ]) + ]), + t.div({className: {"button-row": true, hidden: vm => vm.loading}}, + t.a({className: "button-action primary", href: vm.backUrl}, vm.i18n`Go back`)) ]); } }