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.
This commit is contained in:
James Salter 2022-03-16 12:20:06 +00:00 committed by GitHub
parent 1f97bc2325
commit adad5e16c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 94 additions and 58 deletions

View file

@ -29,70 +29,18 @@ import SdkConfig, { parseSsoRedirectOptions } from "matrix-react-sdk/src/SdkConf
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { createClient } from "matrix-js-sdk/src/matrix"; import { createClient } from "matrix-js-sdk/src/matrix";
import type MatrixChatType from "matrix-react-sdk/src/components/structures/MatrixChat"; import { parseQs } from './url_utils';
import { parseQs, parseQsFromFragment } from './url_utils';
import VectorBasePlatform from "./platform/VectorBasePlatform"; 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 // 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. // this incidentally means we can forget our React imports in JSX files without penalty.
window.React = React; window.React = React;
let lastLocationHashSet: string = null;
logger.log(`Application is running in ${process.env.NODE_ENV} mode`); logger.log(`Application is running in ${process.env.NODE_ENV} mode`);
window.matrixLogger = logger; 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 // We use this to work out what URL the SDK should
// pass through when registering to allow the user to // pass through when registering to allow the user to
// click back to the client having registered. // click back to the client having registered.
@ -141,8 +89,7 @@ function onTokenLoginCompleted() {
} }
export async function loadApp(fragParams: {}) { export async function loadApp(fragParams: {}) {
window.addEventListener('hashchange', onHashChange); initRouting();
const platform = PlatformPeg.get(); const platform = PlatformPeg.get();
const params = parseQs(window.location); const params = parseQs(window.location);

View file

@ -28,6 +28,7 @@ import { logger } from "matrix-js-sdk/src/logger";
import VectorBasePlatform from './VectorBasePlatform'; import VectorBasePlatform from './VectorBasePlatform';
import { parseQs } from "../url_utils"; import { parseQs } from "../url_utils";
import { reloadPage } from "../routing";
const POKE_RATE_MS = 10 * 60 * 1000; // 10 min 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 // Ideally, loading an old copy would be impossible with the
// cache-control: nocache HTTP header set, but Firefox doesn't always obey it :/ // 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) => { this.pollForUpdate((version: string, newVersion: string) => {
const query = parseQs(location); const query = parseQs(location);
if (query.updated === "1") { 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! // We just reloaded already and are still on the old version!
// Show the toast rather than reload in a loop. // Show the toast rather than reload in a loop.
showUpdateToast(version, newVersion); showUpdateToast(version, newVersion);
@ -146,8 +149,7 @@ export default class WebPlatform extends VectorBasePlatform {
suffix = "&" + suffix; suffix = "&" + suffix;
} }
// This line has the effect of loading the page at the new location reloadPage(window.location.href + suffix);
window.location.href = window.location.href + suffix;
}); });
setInterval(() => this.pollForUpdate(showUpdateToast, hideUpdateToast), POKE_RATE_MS); setInterval(() => this.pollForUpdate(showUpdateToast, hideUpdateToast), POKE_RATE_MS);
} }
@ -165,10 +167,14 @@ export default class WebPlatform extends VectorBasePlatform {
if (currentVersion !== mostRecentVersion) { if (currentVersion !== mostRecentVersion) {
if (this.shouldShowUpdate(mostRecentVersion)) { if (this.shouldShowUpdate(mostRecentVersion)) {
console.log("Update available to " + mostRecentVersion + ", will notify user");
showUpdate(currentVersion, mostRecentVersion); showUpdate(currentVersion, mostRecentVersion);
} else {
console.log("Update available to " + mostRecentVersion + " but won't be shown");
} }
return { status: UpdateCheckStatus.Ready }; return { status: UpdateCheckStatus.Ready };
} else { } else {
console.log("No update available, already on " + mostRecentVersion);
showNoUpdate?.(); showNoUpdate?.();
} }

83
src/vector/routing.ts Normal file
View file

@ -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);
}