From adad5e16c274aebeec8d62159c48b9a30733d95c Mon Sep 17 00:00:00 2001 From: James Salter Date: Wed, 16 Mar 2022 12:20:06 +0000 Subject: [PATCH] Fix page reloading on initial update (#21410) Remove the hash event listener before reloading the page, as the reload was being swallowed by our router. Along the way, refactor out routing logic form app.tsx to avoid causing a circular import. --- src/vector/app.tsx | 59 ++------------------- src/vector/platform/WebPlatform.ts | 10 +++- src/vector/routing.ts | 83 ++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 58 deletions(-) create mode 100644 src/vector/routing.ts diff --git a/src/vector/app.tsx b/src/vector/app.tsx index e92ad90136..ee12585a63 100644 --- a/src/vector/app.tsx +++ b/src/vector/app.tsx @@ -29,70 +29,18 @@ import SdkConfig, { parseSsoRedirectOptions } from "matrix-react-sdk/src/SdkConf import { logger } from "matrix-js-sdk/src/logger"; import { createClient } from "matrix-js-sdk/src/matrix"; -import type MatrixChatType from "matrix-react-sdk/src/components/structures/MatrixChat"; -import { parseQs, parseQsFromFragment } from './url_utils'; +import { parseQs } from './url_utils'; import VectorBasePlatform from "./platform/VectorBasePlatform"; +import { getScreenFromLocation, init as initRouting, onNewScreen } from "./routing"; // add React and ReactPerf to the global namespace, to make them easier to access via the console // this incidentally means we can forget our React imports in JSX files without penalty. window.React = React; -let lastLocationHashSet: string = null; - logger.log(`Application is running in ${process.env.NODE_ENV} mode`); window.matrixLogger = logger; -// Parse the given window.location and return parameters that can be used when calling -// MatrixChat.showScreen(screen, params) -function getScreenFromLocation(location: Location) { - const fragparts = parseQsFromFragment(location); - return { - screen: fragparts.location.substring(1), - params: fragparts.params, - }; -} - -// Here, we do some crude URL analysis to allow -// deep-linking. -function routeUrl(location: Location) { - if (!window.matrixChat) return; - - logger.log("Routing URL ", location.href); - const s = getScreenFromLocation(location); - (window.matrixChat as MatrixChatType).showScreen(s.screen, s.params); -} - -function onHashChange(ev: HashChangeEvent) { - if (decodeURIComponent(window.location.hash) === lastLocationHashSet) { - // we just set this: no need to route it! - return; - } - routeUrl(window.location); -} - -// This will be called whenever the SDK changes screens, -// so a web page can update the URL bar appropriately. -function onNewScreen(screen: string, replaceLast = false) { - logger.log("newscreen " + screen); - const hash = '#/' + screen; - lastLocationHashSet = hash; - - // if the new hash is a substring of the old one then we are stripping fields e.g `via` so replace history - if (screen.startsWith("room/") && - window.location.hash.includes("/$") === hash.includes("/$") && // only if both did or didn't contain event link - window.location.hash.startsWith(hash) - ) { - replaceLast = true; - } - - if (replaceLast) { - window.location.replace(hash); - } else { - window.location.assign(hash); - } -} - // We use this to work out what URL the SDK should // pass through when registering to allow the user to // click back to the client having registered. @@ -141,8 +89,7 @@ function onTokenLoginCompleted() { } export async function loadApp(fragParams: {}) { - window.addEventListener('hashchange', onHashChange); - + initRouting(); const platform = PlatformPeg.get(); const params = parseQs(window.location); diff --git a/src/vector/platform/WebPlatform.ts b/src/vector/platform/WebPlatform.ts index 52401b5a55..6b1d179857 100644 --- a/src/vector/platform/WebPlatform.ts +++ b/src/vector/platform/WebPlatform.ts @@ -28,6 +28,7 @@ import { logger } from "matrix-js-sdk/src/logger"; import VectorBasePlatform from './VectorBasePlatform'; import { parseQs } from "../url_utils"; +import { reloadPage } from "../routing"; const POKE_RATE_MS = 10 * 60 * 1000; // 10 min @@ -128,9 +129,11 @@ export default class WebPlatform extends VectorBasePlatform { // // Ideally, loading an old copy would be impossible with the // cache-control: nocache HTTP header set, but Firefox doesn't always obey it :/ + console.log("startUpdater, current version is " + this.getNormalizedAppVersion(process.env.VERSION)); this.pollForUpdate((version: string, newVersion: string) => { const query = parseQs(location); if (query.updated === "1") { + console.log("Update reloaded but still on an old version, stopping"); // We just reloaded already and are still on the old version! // Show the toast rather than reload in a loop. showUpdateToast(version, newVersion); @@ -146,8 +149,7 @@ export default class WebPlatform extends VectorBasePlatform { suffix = "&" + suffix; } - // This line has the effect of loading the page at the new location - window.location.href = window.location.href + suffix; + reloadPage(window.location.href + suffix); }); setInterval(() => this.pollForUpdate(showUpdateToast, hideUpdateToast), POKE_RATE_MS); } @@ -165,10 +167,14 @@ export default class WebPlatform extends VectorBasePlatform { if (currentVersion !== mostRecentVersion) { if (this.shouldShowUpdate(mostRecentVersion)) { + console.log("Update available to " + mostRecentVersion + ", will notify user"); showUpdate(currentVersion, mostRecentVersion); + } else { + console.log("Update available to " + mostRecentVersion + " but won't be shown"); } return { status: UpdateCheckStatus.Ready }; } else { + console.log("No update available, already on " + mostRecentVersion); showNoUpdate?.(); } diff --git a/src/vector/routing.ts b/src/vector/routing.ts new file mode 100644 index 0000000000..07babd6a77 --- /dev/null +++ b/src/vector/routing.ts @@ -0,0 +1,83 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Parse the given window.location and return parameters that can be used when calling +// MatrixChat.showScreen(screen, params) +import { logger } from "matrix-js-sdk/src/logger"; +import MatrixChatType from "matrix-react-sdk/src/components/structures/MatrixChat"; + +import { parseQsFromFragment } from "./url_utils"; + +let lastLocationHashSet: string = null; + +export function getScreenFromLocation(location: Location) { + const fragparts = parseQsFromFragment(location); + return { + screen: fragparts.location.substring(1), + params: fragparts.params, + }; +} + +// Here, we do some crude URL analysis to allow +// deep-linking. +function routeUrl(location: Location) { + if (!window.matrixChat) return; + + logger.log("Routing URL ", location.href); + const s = getScreenFromLocation(location); + (window.matrixChat as MatrixChatType).showScreen(s.screen, s.params); +} + +function onHashChange(ev: HashChangeEvent) { + if (decodeURIComponent(window.location.hash) === lastLocationHashSet) { + // we just set this: no need to route it! + return; + } + routeUrl(window.location); +} + +// This will be called whenever the SDK changes screens, +// so a web page can update the URL bar appropriately. +export function onNewScreen(screen: string, replaceLast = false) { + logger.log("newscreen " + screen); + const hash = '#/' + screen; + lastLocationHashSet = hash; + + // if the new hash is a substring of the old one then we are stripping fields e.g `via` so replace history + if (screen.startsWith("room/") && + window.location.hash.includes("/$") === hash.includes("/$") && // only if both did or didn't contain event link + window.location.hash.startsWith(hash) + ) { + replaceLast = true; + } + + if (replaceLast) { + window.location.replace(hash); + } else { + window.location.assign(hash); + } +} + +// reload the page to a different url +export function reloadPage(newUrl: string) { + console.log("reloadPage to " + newUrl); + window.removeEventListener('hashchange', onHashChange); + window.location.href = newUrl; +} + +export function init() { + window.addEventListener('hashchange', onHashChange); +}