2020-04-14 16:21:04 +01:00
|
|
|
/*
|
2024-09-09 14:57:16 +01:00
|
|
|
Copyright 2024 New Vector Ltd.
|
|
|
|
|
Copyright 2020-2023 The Matrix.org Foundation C.I.C.
|
2020-04-14 16:21:04 +01:00
|
|
|
|
2025-01-06 11:18:54 +00:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
2024-09-09 14:57:16 +01:00
|
|
|
Please see LICENSE files in the repository root for full details.
|
2020-04-14 16:21:04 +01:00
|
|
|
*/
|
|
|
|
|
|
2020-05-28 13:55:07 +01:00
|
|
|
import dis from "../../dispatcher/dispatcher";
|
2020-07-28 11:53:43 -06:00
|
|
|
import SettingsStore from "../SettingsStore";
|
2020-05-28 13:55:07 +01:00
|
|
|
import IWatcher from "./Watcher";
|
|
|
|
|
import { toPx } from "../../utils/units";
|
2020-06-15 15:33:52 +01:00
|
|
|
import { Action } from "../../dispatcher/actions";
|
2020-07-28 11:53:43 -06:00
|
|
|
import { SettingLevel } from "../SettingLevel";
|
2022-05-26 09:56:53 +01:00
|
|
|
import { UpdateSystemFontPayload } from "../../dispatcher/payloads/UpdateSystemFontPayload";
|
|
|
|
|
import { ActionPayload } from "../../dispatcher/payloads";
|
2020-04-14 16:21:04 +01:00
|
|
|
|
2020-05-28 13:55:07 +01:00
|
|
|
export class FontWatcher implements IWatcher {
|
2023-06-29 11:30:25 +01:00
|
|
|
/**
|
2024-02-21 12:23:07 +01:00
|
|
|
* This Compound value is using `100%` of the default browser font size.
|
|
|
|
|
* It allows EW to use the browser's default font size instead of a fixed value.
|
|
|
|
|
* All the Compound font size are using `rem`, they are relative to the root font size
|
|
|
|
|
* and therefore of the browser font size.
|
2023-06-29 11:30:25 +01:00
|
|
|
*/
|
2024-02-21 12:23:07 +01:00
|
|
|
private static readonly DEFAULT_SIZE = "var(--cpd-font-size-root)";
|
|
|
|
|
/**
|
|
|
|
|
* Default delta added to the ${@link DEFAULT_SIZE}
|
|
|
|
|
*/
|
|
|
|
|
public static readonly DEFAULT_DELTA = 0;
|
2020-05-28 13:55:07 +01:00
|
|
|
|
2024-11-01 15:15:04 +00:00
|
|
|
private dispatcherRef?: string;
|
2020-04-14 16:21:04 +01:00
|
|
|
|
2023-06-29 11:30:25 +01:00
|
|
|
public async start(): Promise<void> {
|
2022-05-26 09:56:53 +01:00
|
|
|
this.updateFont();
|
2020-05-28 13:55:07 +01:00
|
|
|
this.dispatcherRef = dis.register(this.onAction);
|
2023-06-29 11:30:25 +01:00
|
|
|
/**
|
|
|
|
|
* baseFontSize is an account level setting which is loaded after the initial
|
|
|
|
|
* sync. Hence why we can't do that in the `constructor`
|
|
|
|
|
*/
|
|
|
|
|
await this.migrateBaseFontSize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-02-21 12:23:07 +01:00
|
|
|
* Migrate the base font size from the V1 and V2 version to the V3 version
|
|
|
|
|
* @private
|
2023-06-29 11:30:25 +01:00
|
|
|
*/
|
|
|
|
|
private async migrateBaseFontSize(): Promise<void> {
|
2024-02-21 12:23:07 +01:00
|
|
|
await this.migrateBaseFontV1toFontSizeDelta();
|
|
|
|
|
await this.migrateBaseFontV2toFontSizeDelta();
|
|
|
|
|
}
|
2023-06-29 11:30:25 +01:00
|
|
|
|
2024-02-21 12:23:07 +01:00
|
|
|
/**
|
|
|
|
|
* Migrating from the V1 version of the base font size to the new delta system.
|
|
|
|
|
* The delta system is using the default browser font size as a base
|
|
|
|
|
* Everything will become slightly larger, and getting rid of the `SIZE_DIFF`
|
|
|
|
|
* weirdness for locally persisted values
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
private async migrateBaseFontV1toFontSizeDelta(): Promise<void> {
|
2024-12-23 20:25:15 +00:00
|
|
|
const legacyBaseFontSize = SettingsStore.getValue("baseFontSize");
|
2024-02-21 12:23:07 +01:00
|
|
|
// No baseFontV1 found, nothing to migrate
|
|
|
|
|
if (!legacyBaseFontSize) return;
|
2023-06-29 11:30:25 +01:00
|
|
|
|
2024-02-21 12:23:07 +01:00
|
|
|
console.log(
|
|
|
|
|
"Migrating base font size -> base font size V2 -> font size delta for Compound, current value",
|
|
|
|
|
legacyBaseFontSize,
|
|
|
|
|
);
|
2023-06-29 11:30:25 +01:00
|
|
|
|
2024-02-21 12:23:07 +01:00
|
|
|
// Compute the V1 to V2 version before migrating to fontSizeDelta
|
|
|
|
|
const baseFontSizeV2 = this.computeBaseFontSizeV1toV2(legacyBaseFontSize);
|
|
|
|
|
|
|
|
|
|
// Compute the difference between the V2 and the fontSizeDelta
|
|
|
|
|
const delta = this.computeFontSizeDeltaFromV2BaseFontSize(baseFontSizeV2);
|
|
|
|
|
|
|
|
|
|
await SettingsStore.setValue("fontSizeDelta", null, SettingLevel.DEVICE, delta);
|
|
|
|
|
await SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, 0);
|
|
|
|
|
console.log("Migration complete, deleting legacy `baseFontSize`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Migrating from the V2 version of the base font size to the new delta system
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
private async migrateBaseFontV2toFontSizeDelta(): Promise<void> {
|
2024-12-23 20:25:15 +00:00
|
|
|
const legacyBaseFontV2Size = SettingsStore.getValue("baseFontSizeV2");
|
2024-02-21 12:23:07 +01:00
|
|
|
// No baseFontV2 found, nothing to migrate
|
|
|
|
|
if (!legacyBaseFontV2Size) return;
|
|
|
|
|
|
|
|
|
|
console.log("Migrating base font size V2 for Compound, current value", legacyBaseFontV2Size);
|
|
|
|
|
|
|
|
|
|
// Compute the difference between the V2 and the fontSizeDelta
|
|
|
|
|
const delta = this.computeFontSizeDeltaFromV2BaseFontSize(legacyBaseFontV2Size);
|
|
|
|
|
|
|
|
|
|
await SettingsStore.setValue("fontSizeDelta", null, SettingLevel.DEVICE, delta);
|
|
|
|
|
await SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, 0);
|
|
|
|
|
console.log("Migration complete, deleting legacy `baseFontSizeV2`");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Compute the V2 font size from the V1 font size
|
|
|
|
|
* @param legacyBaseFontSize
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
private computeBaseFontSizeV1toV2(legacyBaseFontSize: number): number {
|
|
|
|
|
// For some odd reason, the persisted value in user storage has an offset
|
|
|
|
|
// of 5 pixels for all values stored under `baseFontSize`
|
|
|
|
|
const LEGACY_SIZE_DIFF = 5;
|
|
|
|
|
|
|
|
|
|
// Compound uses a base font size of `16px`, whereas the old Element
|
|
|
|
|
// styles based their calculations off a `15px` root font size.
|
|
|
|
|
const ROOT_FONT_SIZE_INCREASE = 1;
|
|
|
|
|
|
|
|
|
|
// Compute the font size of the V2 version before migrating to V3
|
|
|
|
|
return legacyBaseFontSize + ROOT_FONT_SIZE_INCREASE + LEGACY_SIZE_DIFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Compute the difference between the V2 font size and the default browser font size
|
|
|
|
|
* @param legacyBaseFontV2Size
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
private computeFontSizeDeltaFromV2BaseFontSize(legacyBaseFontV2Size: number): number {
|
|
|
|
|
const browserDefaultFontSize = FontWatcher.getRootFontSize();
|
|
|
|
|
|
|
|
|
|
// Compute the difference between the V2 font size and the default browser font size
|
|
|
|
|
return legacyBaseFontV2Size - browserDefaultFontSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the root font size of the document
|
|
|
|
|
* Fallback to 16px if the value is not found
|
|
|
|
|
* @returns {number}
|
|
|
|
|
*/
|
|
|
|
|
public static getRootFontSize(): number {
|
|
|
|
|
return parseInt(window.getComputedStyle(document.documentElement).getPropertyValue("font-size"), 10) || 16;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the browser default font size
|
|
|
|
|
* @returns {number} the default font size of the browser
|
|
|
|
|
*/
|
|
|
|
|
public static getBrowserDefaultFontSize(): number {
|
2024-12-23 20:25:15 +00:00
|
|
|
return this.getRootFontSize() - SettingsStore.getValue("fontSizeDelta");
|
2020-04-14 16:21:04 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
public stop(): void {
|
2020-05-28 13:55:07 +01:00
|
|
|
dis.unregister(this.dispatcherRef);
|
2020-04-14 16:21:04 +01:00
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
private updateFont(): void {
|
2024-12-23 20:25:15 +00:00
|
|
|
this.setRootFontSize(SettingsStore.getValue("fontSizeDelta"));
|
2022-05-26 09:56:53 +01:00
|
|
|
this.setSystemFont({
|
2023-11-23 13:04:05 +00:00
|
|
|
useBundledEmojiFont: SettingsStore.getValue("useBundledEmojiFont"),
|
2022-05-26 09:56:53 +01:00
|
|
|
useSystemFont: SettingsStore.getValue("useSystemFont"),
|
|
|
|
|
font: SettingsStore.getValue("systemFont"),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
private onAction = (payload: ActionPayload): void => {
|
2023-06-29 11:30:25 +01:00
|
|
|
if (payload.action === Action.MigrateBaseFontSize) {
|
|
|
|
|
this.migrateBaseFontSize();
|
2024-02-21 12:23:07 +01:00
|
|
|
} else if (payload.action === Action.UpdateFontSizeDelta) {
|
|
|
|
|
this.setRootFontSize(payload.delta);
|
2020-06-15 15:33:52 +01:00
|
|
|
} else if (payload.action === Action.UpdateSystemFont) {
|
2022-05-26 09:56:53 +01:00
|
|
|
this.setSystemFont(payload as UpdateSystemFontPayload);
|
|
|
|
|
} else if (payload.action === Action.OnLoggedOut) {
|
|
|
|
|
// Clear font overrides when logging out
|
2024-02-21 12:23:07 +01:00
|
|
|
this.setRootFontSize(FontWatcher.DEFAULT_DELTA);
|
2022-05-26 09:56:53 +01:00
|
|
|
this.setSystemFont({
|
2023-11-23 13:04:05 +00:00
|
|
|
useBundledEmojiFont: false,
|
2022-05-26 09:56:53 +01:00
|
|
|
useSystemFont: false,
|
|
|
|
|
font: "",
|
|
|
|
|
});
|
|
|
|
|
} else if (payload.action === Action.OnLoggedIn) {
|
|
|
|
|
// Font size can be saved on the account, so grab value when logging in
|
|
|
|
|
this.updateFont();
|
2020-04-14 16:21:04 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-21 12:23:07 +01:00
|
|
|
/**
|
|
|
|
|
* Set the root font size of the document
|
|
|
|
|
* @param delta {number} the delta to add to the default font size
|
|
|
|
|
*/
|
|
|
|
|
private setRootFontSize = async (delta: number): Promise<void> => {
|
|
|
|
|
// Add the delta to the browser default font size
|
|
|
|
|
document.querySelector<HTMLElement>(":root")!.style.fontSize =
|
|
|
|
|
`calc(${FontWatcher.DEFAULT_SIZE} + ${toPx(delta)})`;
|
2020-05-06 17:25:54 +01:00
|
|
|
};
|
2020-06-15 15:33:52 +01:00
|
|
|
|
2023-06-29 11:30:25 +01:00
|
|
|
public static readonly FONT_FAMILY_CUSTOM_PROPERTY = "--cpd-font-family-sans";
|
2023-11-23 13:04:05 +00:00
|
|
|
public static readonly EMOJI_FONT_FAMILY_CUSTOM_PROPERTY = "--emoji-font-family";
|
|
|
|
|
public static readonly BUNDLED_EMOJI_FONT = "Twemoji";
|
2023-06-29 11:30:25 +01:00
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
private setSystemFont = ({
|
2023-11-23 13:04:05 +00:00
|
|
|
useBundledEmojiFont,
|
2023-01-12 13:25:14 +00:00
|
|
|
useSystemFont,
|
|
|
|
|
font,
|
2023-11-23 13:04:05 +00:00
|
|
|
}: Pick<UpdateSystemFontPayload, "useBundledEmojiFont" | "useSystemFont" | "font">): void => {
|
2022-05-23 18:29:16 +02:00
|
|
|
if (useSystemFont) {
|
2023-11-23 13:04:05 +00:00
|
|
|
let fontString = font
|
|
|
|
|
.split(",")
|
|
|
|
|
.map((font) => {
|
|
|
|
|
font = font.trim();
|
|
|
|
|
if (!font.startsWith('"') && !font.endsWith('"')) {
|
|
|
|
|
font = `"${font}"`;
|
|
|
|
|
}
|
|
|
|
|
return font;
|
|
|
|
|
})
|
|
|
|
|
.join(",");
|
|
|
|
|
|
|
|
|
|
if (useBundledEmojiFont) {
|
|
|
|
|
fontString += ", " + FontWatcher.BUNDLED_EMOJI_FONT;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-29 11:30:25 +01:00
|
|
|
/**
|
|
|
|
|
* Overrides the default font family from Compound
|
|
|
|
|
* Make sure that fonts with spaces in their names get interpreted properly
|
|
|
|
|
*/
|
2023-11-23 13:04:05 +00:00
|
|
|
document.body.style.setProperty(FontWatcher.FONT_FAMILY_CUSTOM_PROPERTY, fontString);
|
2022-05-23 18:29:16 +02:00
|
|
|
} else {
|
2023-06-29 11:30:25 +01:00
|
|
|
document.body.style.removeProperty(FontWatcher.FONT_FAMILY_CUSTOM_PROPERTY);
|
2023-11-23 13:04:05 +00:00
|
|
|
|
|
|
|
|
if (useBundledEmojiFont) {
|
|
|
|
|
document.body.style.setProperty(
|
|
|
|
|
FontWatcher.EMOJI_FONT_FAMILY_CUSTOM_PROPERTY,
|
|
|
|
|
FontWatcher.BUNDLED_EMOJI_FONT,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
document.body.style.removeProperty(FontWatcher.EMOJI_FONT_FAMILY_CUSTOM_PROPERTY);
|
|
|
|
|
}
|
2022-05-23 18:29:16 +02:00
|
|
|
}
|
2020-06-22 11:39:11 +01:00
|
|
|
};
|
2020-04-16 11:56:43 +01:00
|
|
|
}
|