feat: add more callback listeners to the theme system

This commit is contained in:
SethBurkart123
2025-03-30 09:11:41 +11:00
parent 600456f28e
commit a07323499c
2 changed files with 41 additions and 19 deletions
+39 -12
View File
@@ -43,6 +43,7 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
const storageKey = `plugin.${plugin.id}.settings`; const storageKey = `plugin.${plugin.id}.settings`;
const listeners = new Map<keyof T, Set<(value: any) => void>>(); const listeners = new Map<keyof T, Set<(value: any) => void>>();
let settings: { [K in keyof T]: T[K]['default'] }; let settings: { [K in keyof T]: T[K]['default'] };
const storageListeners = new Set<(changes: { [key: string]: any }, area: string) => void>();
// Initialize settings with defaults // Initialize settings with defaults
settings = Object.entries(plugin.settings).reduce((acc, [key, setting]) => { settings = Object.entries(plugin.settings).reduce((acc, [key, setting]) => {
@@ -69,7 +70,7 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
})(); })();
// Listen for storage changes // Listen for storage changes
browser.storage.onChanged.addListener((changes, area) => { const handleStorageChange = (changes: { [key: string]: any }, area: string) => {
if (area === 'local' && changes[storageKey]) { if (area === 'local' && changes[storageKey]) {
const newValue = changes[storageKey].newValue; const newValue = changes[storageKey].newValue;
if (newValue) { if (newValue) {
@@ -80,7 +81,9 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
}); });
} }
} }
}); };
browser.storage.onChanged.addListener(handleStorageChange);
storageListeners.add(handleStorageChange);
// Create a proxy to handle direct property access // Create a proxy to handle direct property access
const proxy = new Proxy(settings, { const proxy = new Proxy(settings, {
@@ -91,13 +94,13 @@ function createSettingsAPI<T extends PluginSettings>(plugin: Plugin<T>): Setting
listeners.set(key, new Set()); listeners.set(key, new Set());
} }
listeners.get(key)!.add(callback); listeners.get(key)!.add(callback);
return {
unregister: () => {
listeners.get(key)?.delete(callback);
}
};
}; };
} }
if (prop === 'offChange') {
return (key: keyof T, callback: (value: any) => void) => {
listeners.get(key)?.delete(callback);
};
}
if (prop === 'loaded') { if (prop === 'loaded') {
return loaded; return loaded;
} }
@@ -125,6 +128,7 @@ function createStorageAPI<T = any>(pluginId: string): StorageAPI<T> & { [K in ke
const prefix = `plugin.${pluginId}.storage.`; const prefix = `plugin.${pluginId}.storage.`;
const cache: Record<string, any> = {}; const cache: Record<string, any> = {};
const listeners = new Map<string, Set<(value: any) => void>>(); const listeners = new Map<string, Set<(value: any) => void>>();
const storageListeners = new Set<(changes: { [key: string]: any }, area: string) => void>();
// Load all existing storage values for this plugin // Load all existing storage values for this plugin
const loadStoragePromise = (async () => { const loadStoragePromise = (async () => {
@@ -144,7 +148,7 @@ function createStorageAPI<T = any>(pluginId: string): StorageAPI<T> & { [K in ke
})(); })();
// Listen for storage changes // Listen for storage changes
browser.storage.onChanged.addListener((changes, area) => { const handleStorageChange = (changes: { [key: string]: any }, area: string) => {
if (area === 'local') { if (area === 'local') {
Object.entries(changes).forEach(([key, change]) => { Object.entries(changes).forEach(([key, change]) => {
if (key.startsWith(prefix)) { if (key.startsWith(prefix)) {
@@ -156,7 +160,9 @@ function createStorageAPI<T = any>(pluginId: string): StorageAPI<T> & { [K in ke
} }
}); });
} }
}); };
browser.storage.onChanged.addListener(handleStorageChange);
storageListeners.add(handleStorageChange);
// Create the proxy for direct property access // Create the proxy for direct property access
return new Proxy(cache, { return new Proxy(cache, {
@@ -167,6 +173,11 @@ function createStorageAPI<T = any>(pluginId: string): StorageAPI<T> & { [K in ke
listeners.set(key as string, new Set()); listeners.set(key as string, new Set());
} }
listeners.get(key as string)!.add(callback); listeners.get(key as string)!.add(callback);
return {
unregister: () => {
listeners.get(key as string)?.delete(callback);
}
};
}; };
} }
if (prop === 'offChange') { if (prop === 'offChange') {
@@ -200,12 +211,28 @@ function createStorageAPI<T = any>(pluginId: string): StorageAPI<T> & { [K in ke
function createEventsAPI(pluginId: string): EventsAPI { function createEventsAPI(pluginId: string): EventsAPI {
const prefix = `plugin.${pluginId}.`; const prefix = `plugin.${pluginId}.`;
const eventListeners = new Map<string, Set<{ callback: (...args: any[]) => void, listener: EventListener }>>();
return { return {
on: (event, callback) => { on: (event, callback) => {
document.addEventListener(prefix + event, ((e: CustomEvent) => { const fullEventName = prefix + event;
const listener = ((e: CustomEvent) => {
callback(...(e.detail || [])); callback(...(e.detail || []));
}) as EventListener); }) as EventListener;
document.addEventListener(fullEventName, listener);
if (!eventListeners.has(event)) {
eventListeners.set(event, new Set());
}
eventListeners.get(event)!.add({ callback, listener });
return {
unregister: () => {
document.removeEventListener(fullEventName, listener);
eventListeners.get(event)?.delete({ callback, listener });
}
};
}, },
emit: (event, ...args) => { emit: (event, ...args) => {
document.dispatchEvent( document.dispatchEvent(
+2 -7
View File
@@ -66,12 +66,7 @@ export interface StorageAPI<T = any> {
/** /**
* Register a callback to be called when a storage value changes * Register a callback to be called when a storage value changes
*/ */
onChange: <K extends keyof T>(key: K, callback: (value: T[K]) => void) => void; onChange: <K extends keyof T>(key: K, callback: (value: T[K]) => void) => { unregister: () => void };
/**
* Remove a previously registered callback
*/
offChange: <K extends keyof T>(key: K, callback: (value: T[K]) => void) => void;
/** /**
* Promise that resolves when storage values are loaded * Promise that resolves when storage values are loaded
@@ -84,7 +79,7 @@ export type TypedStorageAPI<T> = StorageAPI<T> & {
} }
export interface EventsAPI { export interface EventsAPI {
on: (event: string, callback: (...args: any[]) => void) => void; on: (event: string, callback: (...args: any[]) => void) => { unregister: () => void };
emit: (event: string, ...args: any[]) => void; emit: (event: string, ...args: any[]) => void;
} }