Merge pull request #9320 from matrix-org/florianduros/fix-white-black-theme-switch

Fix dark and white theme switch in Chrome
This commit is contained in:
Florian Duros 2022-10-07 09:40:12 +02:00 committed by GitHub
commit a11c1e17a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 167 additions and 27 deletions

View file

@ -237,13 +237,13 @@ export async function setTheme(theme?: string): Promise<void> {
// look for the stylesheet elements.
// styleElements is a map from style name to HTMLLinkElement.
const styleElements = Object.create(null);
const themes = Array.from(document.querySelectorAll('[data-mx-theme]'));
const styleElements = new Map<string, HTMLLinkElement>();
const themes = Array.from(document.querySelectorAll<HTMLLinkElement>('[data-mx-theme]'));
themes.forEach(theme => {
styleElements[theme.attributes['data-mx-theme'].value.toLowerCase()] = theme;
styleElements.set(theme.attributes['data-mx-theme'].value.toLowerCase(), theme);
});
if (!(stylesheetName in styleElements)) {
if (!styleElements.has(stylesheetName)) {
throw new Error("Unknown theme " + stylesheetName);
}
@ -258,17 +258,18 @@ export async function setTheme(theme?: string): Promise<void> {
// having them interact badly... but this causes a flash of unstyled app
// which is even uglier. So we don't.
styleElements[stylesheetName].disabled = false;
const styleSheet = styleElements.get(stylesheetName);
styleSheet.disabled = false;
return new Promise((resolve) => {
return new Promise(((resolve, reject) => {
const switchTheme = function() {
// we re-enable our theme here just in case we raced with another
// theme set request as per https://github.com/vector-im/element-web/issues/5601.
// We could alternatively lock or similar to stop the race, but
// this is probably good enough for now.
styleElements[stylesheetName].disabled = false;
Object.values(styleElements).forEach((a: HTMLStyleElement) => {
if (a == styleElements[stylesheetName]) return;
styleSheet.disabled = false;
styleElements.forEach(a => {
if (a == styleSheet) return;
a.disabled = true;
});
const bodyStyles = global.getComputedStyle(document.body);
@ -279,26 +280,50 @@ export async function setTheme(theme?: string): Promise<void> {
resolve();
};
// turns out that Firefox preloads the CSS for link elements with
// the disabled attribute, but Chrome doesn't.
const isStyleSheetLoaded = () => Boolean(
[...document.styleSheets]
.find(_styleSheet => _styleSheet?.href === styleSheet.href),
);
let cssLoaded = false;
styleElements[stylesheetName].onload = () => {
switchTheme();
};
for (let i = 0; i < document.styleSheets.length; i++) {
const ss = document.styleSheets[i];
if (ss && ss.href === styleElements[stylesheetName].href) {
cssLoaded = true;
break;
function waitForStyleSheetLoading() {
// turns out that Firefox preloads the CSS for link elements with
// the disabled attribute, but Chrome doesn't.
if (isStyleSheetLoaded()) {
switchTheme();
return;
}
let counter = 0;
// In case of theme toggling (white => black => white)
// Chrome doesn't fire the `load` event when the white theme is selected the second times
const intervalId = setInterval(() => {
if (isStyleSheetLoaded()) {
clearInterval(intervalId);
styleSheet.onload = undefined;
styleSheet.onerror = undefined;
switchTheme();
}
// Avoid to be stuck in an endless loop if there is an issue in the stylesheet loading
counter++;
if (counter === 10) {
clearInterval(intervalId);
reject();
}
}, 200);
styleSheet.onload = () => {
clearInterval(intervalId);
switchTheme();
};
styleSheet.onerror = (e) => {
clearInterval(intervalId);
reject(e);
};
}
if (cssLoaded) {
styleElements[stylesheetName].onload = undefined;
switchTheme();
}
});
waitForStyleSheetLoading();
}));
}