1
Fork 0

Add the Theme Switcher content script.

This commit is contained in:
Bauke 2023-12-20 14:44:49 +01:00
parent 9d5f0d1afc
commit 0c1fadb784
Signed by: Bauke
GPG Key ID: C1C0F29952BCF558
4 changed files with 97 additions and 0 deletions

View File

@ -11,6 +11,7 @@ export * from "./miscellaneous/hide-own-username.js";
export * from "./miscellaneous/show-topic-author.js";
export * from "./miscellaneous/topic-info-ignore.js";
export * from "./miscellaneous/unignore-all-button.js";
export * from "./theme-switcher.js";
export * from "./themed-logo.js";
export * from "./user-labels.js";
export * from "./username-colors.js";

View File

@ -0,0 +1,75 @@
import {
Feature,
type ThemeSwitcherData,
fromStorage,
} from "../../storage/exports.js";
import {log, setBodyThemeClass, sleep} from "../../utilities/exports.js";
export async function runThemeSwitcherFeature(): Promise<void> {
const data = await fromStorage(Feature.ThemeSwitcher);
const switchedTheme = await themeSwitcher(data.value);
if (switchedTheme !== undefined) {
log(`Theme Switcher: Switched to ${switchedTheme}`);
}
}
async function themeSwitcher(
data: ThemeSwitcherData,
): Promise<string | undefined> {
// Get the millisecond Unix times of each date for easier comparison.
const [now, dateA, dateB] = [
new Date(),
getDateWithSpecificTime(data.hourA),
getDateWithSpecificTime(data.hourB),
].map((date) => date.getTime());
// If we're in the range between date A and B then set it to theme A,
// otherwise set it to theme B as we'll be in the other range.
const theme = now > dateA && now <= dateB ? data.themeA : data.themeB;
const themeSelector =
document.querySelector<HTMLSelectElement>("select#theme") ?? undefined;
if (themeSelector === undefined) {
// If there is no theme selector on the page only change the body class.
setBodyThemeClass(theme);
return theme;
}
if (themeSelector.value === theme) {
// If the theme is already set to the one we want to change to, do nothing.
return;
}
themeSelector.value = theme;
setBodyThemeClass(theme);
// After 2 seconds dispatch a synthetic change event on the theme selector so
// it changes the user's theme cookie. This isn't ideal because if it takes
// longer than 2 seconds to initialize the Tildes JS handler for the theme
// selector it won't do anything, but for now it's better than trying to set
// the cookie ourselves which would require extra WebExtension permissions.
void sleep(2000).then(() => {
themeSelector.dispatchEvent(new Event("change"));
});
return theme;
}
/**
* Create a date of today with the hours and minutes set to the given `HH:MM`
* string, and the seconds and milliseconds set to 0.
*/
function getDateWithSpecificTime(time: string): Date {
const components = time
.split(":")
.map(Number)
// Make sure any conversions don't return `NaN`.
.map((value) => (Number.isNaN(value) ? 0 : value));
const date = new Date();
date.setHours(components[0]);
date.setMinutes(components[1]);
date.setSeconds(0, 0);
return date;
}

View File

@ -30,6 +30,7 @@ import {
runTopicInfoIgnore,
runUnignoreAllButtonFeature,
runUsernameColorsFeature,
runThemeSwitcherFeature,
} from "./features/exports.js";
async function initialize() {
@ -139,6 +140,10 @@ async function initialize() {
});
}
if (enabledFeatures.value.has(Feature.ThemeSwitcher)) {
await runThemeSwitcherFeature();
}
if (enabledFeatures.value.has(Feature.ThemedLogo)) {
observerFeatures.push(() => {
runThemedLogoFeature();

View File

@ -17,3 +17,19 @@ export function extractThemes(): Array<[string, string]> | undefined {
(theme.textContent ?? "<unknown>").trim(),
]);
}
/**
* Set the theme class on the `<body>` element and remove any other existing
* theme classes.
*/
export function setBodyThemeClass(theme: string): void {
for (const value of Array.from(document.body.classList)) {
if (!value.startsWith("theme-")) {
continue;
}
document.body.classList.remove(value);
}
document.body.classList.add(`theme-${theme}`);
}