mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Sync appearance preferences across windows
Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz> Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -3,7 +3,11 @@ import test from "node:test";
|
||||
|
||||
const values = new Map();
|
||||
const attributes = new Map();
|
||||
const windowListeners = new Map();
|
||||
|
||||
globalThis.window = {
|
||||
addEventListener: (type, listener) => windowListeners.set(type, listener),
|
||||
};
|
||||
globalThis.localStorage = {
|
||||
getItem: (key) => values.get(key) ?? null,
|
||||
setItem: (key, value) => values.set(key, String(value)),
|
||||
@@ -57,3 +61,12 @@ test("initializes from the persisted conversation density", () => {
|
||||
assert.equal(preference.getConversationDensity(), "spacious");
|
||||
assert.equal(attributes.get("data-conversation-density"), "spacious");
|
||||
});
|
||||
|
||||
test("applies conversation density changes from another window", () => {
|
||||
values.set(preference.CONVERSATION_DENSITY_STORAGE_KEY, "compact");
|
||||
windowListeners.get("storage")({
|
||||
key: preference.CONVERSATION_DENSITY_STORAGE_KEY,
|
||||
});
|
||||
assert.equal(preference.getConversationDensity(), "compact");
|
||||
assert.equal(attributes.get("data-conversation-density"), "compact");
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ export const DEFAULT_CONVERSATION_DENSITY: ConversationDensity = "comfortable";
|
||||
|
||||
const listeners = new Set<() => void>();
|
||||
let conversationDensity: ConversationDensity = DEFAULT_CONVERSATION_DENSITY;
|
||||
let listeningForStorageChanges = false;
|
||||
|
||||
export function parseConversationDensity(
|
||||
value: string | null | undefined,
|
||||
@@ -39,8 +40,7 @@ function notifyListeners(): void {
|
||||
for (const listener of listeners) listener();
|
||||
}
|
||||
|
||||
/** Apply the persisted preference before React renders to avoid a layout jump. */
|
||||
export function initializeConversationDensityPreference(): void {
|
||||
function applyStoredConversationDensity(): void {
|
||||
const nextDensity = readStoredConversationDensity();
|
||||
const changed = nextDensity !== conversationDensity;
|
||||
conversationDensity = nextDensity;
|
||||
@@ -48,6 +48,23 @@ export function initializeConversationDensityPreference(): void {
|
||||
if (changed) notifyListeners();
|
||||
}
|
||||
|
||||
function listenForStorageChanges(): void {
|
||||
if (listeningForStorageChanges || !globalThis.window?.addEventListener)
|
||||
return;
|
||||
globalThis.window.addEventListener("storage", (event) => {
|
||||
if (event.key === CONVERSATION_DENSITY_STORAGE_KEY) {
|
||||
applyStoredConversationDensity();
|
||||
}
|
||||
});
|
||||
listeningForStorageChanges = true;
|
||||
}
|
||||
|
||||
/** Apply the persisted preference before React renders to avoid a layout jump. */
|
||||
export function initializeConversationDensityPreference(): void {
|
||||
applyStoredConversationDensity();
|
||||
listenForStorageChanges();
|
||||
}
|
||||
|
||||
function subscribe(listener: () => void): () => void {
|
||||
listeners.add(listener);
|
||||
return () => listeners.delete(listener);
|
||||
|
||||
@@ -4,10 +4,14 @@ import test from "node:test";
|
||||
const values = new Map();
|
||||
const attributes = new Map();
|
||||
const styleValues = new Map();
|
||||
const windowListeners = new Map();
|
||||
const style = {
|
||||
setProperty: (name, value) => styleValues.set(name, value),
|
||||
};
|
||||
|
||||
globalThis.window = {
|
||||
addEventListener: (type, listener) => windowListeners.set(type, listener),
|
||||
};
|
||||
globalThis.localStorage = {
|
||||
getItem: (key) => values.get(key) ?? null,
|
||||
setItem: (key, value) => values.set(key, String(value)),
|
||||
@@ -60,3 +64,11 @@ test("initializes from the stored font size", () => {
|
||||
assert.equal(attributes.get("data-font-size"), "larger");
|
||||
assert.equal(styleValues.get("--buzz-type-rem"), "18.285714px");
|
||||
});
|
||||
|
||||
test("applies font size changes from another window", () => {
|
||||
values.set(preference.FONT_SIZE_STORAGE_KEY, "smaller");
|
||||
windowListeners.get("storage")({ key: preference.FONT_SIZE_STORAGE_KEY });
|
||||
assert.equal(preference.getFontSize(), "smaller");
|
||||
assert.equal(attributes.get("data-font-size"), "smaller");
|
||||
assert.equal(styleValues.get("--buzz-type-rem"), "16px");
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ const TYPE_REM_PROPERTY = "--buzz-type-rem";
|
||||
const listeners = new Set<() => void>();
|
||||
let fontSize: FontSize = DEFAULT_FONT_SIZE;
|
||||
let textZoomFactor = 1;
|
||||
let listeningForStorageChanges = false;
|
||||
|
||||
export function parseFontSize(value: string | null | undefined): FontSize {
|
||||
return value === "smaller" || value === "default" || value === "larger"
|
||||
@@ -54,8 +55,7 @@ function notifyListeners(): void {
|
||||
for (const listener of listeners) listener();
|
||||
}
|
||||
|
||||
/** Apply the persisted preference before React renders to avoid a layout jump. */
|
||||
export function initializeFontSizePreference(): void {
|
||||
function applyStoredFontSize(): void {
|
||||
const nextSize = readStoredFontSize();
|
||||
const changed = nextSize !== fontSize;
|
||||
fontSize = nextSize;
|
||||
@@ -63,6 +63,21 @@ export function initializeFontSizePreference(): void {
|
||||
if (changed) notifyListeners();
|
||||
}
|
||||
|
||||
function listenForStorageChanges(): void {
|
||||
if (listeningForStorageChanges || !globalThis.window?.addEventListener)
|
||||
return;
|
||||
globalThis.window.addEventListener("storage", (event) => {
|
||||
if (event.key === FONT_SIZE_STORAGE_KEY) applyStoredFontSize();
|
||||
});
|
||||
listeningForStorageChanges = true;
|
||||
}
|
||||
|
||||
/** Apply the persisted preference before React renders to avoid a layout jump. */
|
||||
export function initializeFontSizePreference(): void {
|
||||
applyStoredFontSize();
|
||||
listenForStorageChanges();
|
||||
}
|
||||
|
||||
/** Combine Cmd +/- zoom with the selected app-wide type scale. */
|
||||
export function applyTextZoomFactor(zoomFactor: number): void {
|
||||
if (!Number.isFinite(zoomFactor) || zoomFactor <= 0) return;
|
||||
|
||||
Reference in New Issue
Block a user