diff --git a/src/SEQTA.ts b/src/SEQTA.ts index 8d3527e6..084dd264 100644 --- a/src/SEQTA.ts +++ b/src/SEQTA.ts @@ -12,7 +12,7 @@ import { MessageHandler } from './seqta/utils/listeners/MessageListener' import { SettingsState } from "./types/storage" import ShortcutLinks from './seqta/content/links.json' import Sortable from 'sortablejs' -import StorageListener from './seqta/utils/listeners/StorageListener' +//import StorageListener from './seqta/utils/listeners/StorageChanges' import { appendBackgroundToUI } from './seqta/ui/ImageBackgrounds' import assessmentsicon from './seqta/icons/assessmentsIcon' import browser from 'webextension-polyfill' @@ -28,6 +28,8 @@ import { SettingsResizer } from "./seqta/ui/SettingsResizer"; import documentLoadCSS from './css/documentload.scss?inline' import injectedCSS from './css/injected.scss?inline' import { injectYouTubeVideo } from './seqta/ui/VideoLoader' +import { settingsState } from './seqta/utils/listeners/SettingsState' +import { StorageChangeHandler } from './seqta/utils/listeners/StorageChanges' declare global { interface Window { @@ -788,7 +790,7 @@ function main(storedSetting: SettingsState) { document.querySelector('.legacy-root')?.classList.add('hidden') - new StorageListener() + new StorageChangeHandler(); new MessageHandler() updateAllColors(storedSetting) @@ -1378,14 +1380,14 @@ async function AddBetterSEQTAElements(toggle: any) { const result = browser.storage.local.get() result.then(resolve, onError) }) + + console.log(!settingsState.DarkMode) + settingsState.DarkMode = !settingsState.DarkMode; - const newDarkMode = !result!.DarkMode - browser.storage.local.set({ DarkMode: newDarkMode }) - - updateAllColors(newDarkMode, result.selectedColor) + updateAllColors(!settingsState.DarkMode, result.selectedColor) const darklightText = document.getElementById('darklighttooliptext') - darklightText!.innerText = GetLightDarkModeString(newDarkMode) + darklightText!.innerText = GetLightDarkModeString(!settingsState.DarkMode) }) // Locate the menuToggle element diff --git a/src/seqta/ui/colors/Manager.ts b/src/seqta/ui/colors/Manager.ts index b7262571..eedf0390 100644 --- a/src/seqta/ui/colors/Manager.ts +++ b/src/seqta/ui/colors/Manager.ts @@ -2,7 +2,7 @@ import browser from 'webextension-polyfill' import { GetThresholdOfColor } from '../../../SEQTA'; import { lightenAndPaleColor } from './lightenAndPaleColor'; import ColorLuminance from './ColorLuminance'; -import { SettingsState } from '../../../types/storage'; +import { settingsState } from '../../utils/listeners/SettingsState'; import icon48 from '../../../resources/icons/icon-48.png'; @@ -14,7 +14,6 @@ const setCSSVar = (varName: any, value: any) => document.documentElement.style.s const getChromeURL = (path: any) => browser.runtime.getURL(path); const applyProperties = (props: any) => Object.entries(props).forEach(([key, value]) => setCSSVar(key, value)); -let DarkMode: any = null; export function updateAllColors(storedSetting: any, newColor = null) { // Determine the color to use @@ -24,30 +23,24 @@ export function updateAllColors(storedSetting: any, newColor = null) { document.documentElement.classList.add('transparencyEffects'); } - DarkMode = (typeof storedSetting?.DarkMode === 'boolean') ? storedSetting.DarkMode : DarkMode; - - if (typeof storedSetting === 'boolean') { - DarkMode = storedSetting; - } - // Common properties, always applied const commonProps = { '--better-sub': '#161616', '--better-alert-highlight': '#c61851', - '--better-main': selectedColor + '--better-main': settingsState.selectedColor }; // Mode-based properties, applied if storedSetting is provided let modeProps = {}; - if (DarkMode !== null) { - modeProps = DarkMode ? { + if (settingsState.DarkMode) { + modeProps = settingsState.DarkMode ? { '--betterseqta-logo': `url(${browser.runtime.getURL(darkLogo)})` } : { '--better-pale': lightenAndPaleColor(selectedColor), '--betterseqta-logo': `url(${browser.runtime.getURL(lightLogo)})` }; - if (DarkMode) { + if (settingsState.DarkMode) { document.documentElement.style.removeProperty('--better-pale'); document.documentElement.classList.add('dark'); } else { @@ -67,7 +60,7 @@ export function updateAllColors(storedSetting: any, newColor = null) { applyProperties({ ...commonProps, ...modeProps, ...dynamicProps }); // Set favicon, if storedSetting is provided - if (DarkMode !== null) { + if (settingsState.DarkMode !== null) { (document.querySelector('link[rel*=\'icon\']')! as HTMLLinkElement).href = getChromeURL(icon48); } @@ -80,19 +73,10 @@ export function updateAllColors(storedSetting: any, newColor = null) { continue; } - if (DarkMode) { + if (settingsState.DarkMode) { element.contentDocument?.body.classList.add('dark'); } else { element.contentDocument?.body.classList.remove('dark'); } } -} - -export async function getDarkMode() { - try { - const result = await browser.storage.local.get() as SettingsState; - return result.DarkMode; - } catch (error) { - throw error; - } } \ No newline at end of file diff --git a/src/seqta/utils/listeners/SettingsState.ts b/src/seqta/utils/listeners/SettingsState.ts new file mode 100644 index 00000000..1fd98c02 --- /dev/null +++ b/src/seqta/utils/listeners/SettingsState.ts @@ -0,0 +1,78 @@ +import browser from 'webextension-polyfill'; +import { SettingsState } from '../../../types/storage'; + +type ChangeListener = (newValue: any, oldValue: any) => void; + +class StorageManager { + private static instance: StorageManager; + private data: SettingsState; + private listeners: { [key: string]: ChangeListener[] }; + + private constructor() { + this.data = {} as SettingsState; + this.listeners = {}; + this.loadFromStorage(); + + const handler: ProxyHandler = { + get: (target, prop: keyof SettingsState | 'register') => { + if (prop in target) { + return (target as any)[prop]; + } + return Reflect.get(target.data, prop); + }, + set: (target, prop: keyof SettingsState, value) => { + Reflect.set(target.data, prop, value); + target.saveToStorage(); + return true; + } + }; + + this.initStorageListener(); + + return new Proxy(this, handler) as StorageManager & SettingsState; + } + + public static getInstance(): StorageManager & SettingsState { + if (!StorageManager.instance) { + StorageManager.instance = new StorageManager(); + } + return StorageManager.instance as StorageManager & SettingsState; + } + + private async loadFromStorage(): Promise { + const result = await browser.storage.local.get(); + this.data = { ...this.data, ...result }; + } + + private async saveToStorage(): Promise { + await browser.storage.local.set(this.data); + } + + private initStorageListener(): void { + browser.storage.onChanged.addListener((changes, areaName) => { + if (areaName === 'local') { + for (const [key, { oldValue, newValue }] of Object.entries(changes)) { + if (newValue !== undefined) { + (this.data as any)[key] = newValue; + } else { + delete (this.data as any)[key]; + } + if (this.listeners[key]) { + for (const listener of this.listeners[key]) { + listener(newValue, oldValue); + } + } + } + } + }); + } + + public register(prop: keyof SettingsState, listener: ChangeListener): void { + if (!this.listeners[prop]) { + this.listeners[prop] = []; + } + this.listeners[prop].push(listener); + } +} + +export const settingsState = StorageManager.getInstance(); diff --git a/src/seqta/utils/listeners/StorageChanges.ts b/src/seqta/utils/listeners/StorageChanges.ts new file mode 100644 index 00000000..d371d902 --- /dev/null +++ b/src/seqta/utils/listeners/StorageChanges.ts @@ -0,0 +1,115 @@ +import { settingsState } from './SettingsState'; +import { updateAllColors } from '../../ui/colors/Manager'; +import { + CreateBackground, + CreateCustomShortcutDiv, + RemoveBackground, + RemoveShortcutDiv, + addShortcuts, + disableNotificationCollector, + enableNotificationCollector, +} from '../../../SEQTA'; +import { updateBgDurations } from '../../ui/Animation'; +import browser from 'webextension-polyfill'; + +export class StorageChangeHandler { + constructor() { + this.registerHandlers(); + } + + private registerHandlers() { + console.log(settingsState.onoff); + settingsState.register('selectedColor', this.handleSelectedColorChange.bind(this)); + settingsState.register('onoff', this.handleOnOffChange.bind(this)); + settingsState.register('shortcuts', this.handleShortcutsChange.bind(this)); + settingsState.register('customshortcuts', this.handleCustomShortcutsChange.bind(this)); + settingsState.register('notificationcollector', this.handleNotificationCollectorChange.bind(this)); + settingsState.register('bksliderinput', this.handleBksliderInputChange.bind(this)); + settingsState.register('animatedbk', this.handleAnimatedBkChange.bind(this)); + settingsState.register('transparencyEffects', this.handleTransparencyEffectsChange.bind(this)); + } + + private handleSelectedColorChange(newColor: any) { + try { + updateAllColors(newColor); + } catch (err) { + console.error(err); + } + } + + private handleOnOffChange() { + browser.runtime.sendMessage({ type: 'reloadTabs' }); + } + + private handleNotificationCollectorChange(newValue: any) { + if (newValue) { + enableNotificationCollector(); + } else { + disableNotificationCollector(); + } + } + + private handleCustomShortcutsChange(oldValue: any, newValue: any) { + if (newValue) { + if (newValue.length > oldValue.length) { + CreateCustomShortcutDiv(newValue[oldValue.length]); + } else if (newValue.length < oldValue.length) { + const removedElement = oldValue.find( + (oldItem: any) => !newValue.some((newItem: any) => JSON.stringify(oldItem) === JSON.stringify(newItem)) + ); + + if (removedElement) { + RemoveShortcutDiv([removedElement]); + } + } + } + } + + private handleShortcutsChange(oldValue: any, newValue: any) { + const addedShortcuts = newValue.filter((newItem: any) => { + const isAdded = oldValue.some((oldItem: any) => { + const match = oldItem.name === newItem.name; + const wasDisabled = !oldItem.enabled; + const isEnabled = newItem.enabled; + return match && wasDisabled && isEnabled; + }); + + return isAdded; + }); + + const removedShortcuts = newValue.filter((newItem: any) => { + const isRemoved = oldValue.some((oldItem: any) => { + const match = oldItem.name === newItem.name; + const wasEnabled = oldItem.enabled; + const isDisabled = !newItem.enabled; + return match && wasEnabled && isDisabled; + }); + + return isRemoved; + }); + + addShortcuts(addedShortcuts); + RemoveShortcutDiv(removedShortcuts); + } + + private handleBksliderInputChange(newValue: any) { + updateBgDurations(newValue); + } + + private handleAnimatedBkChange(newValue: boolean) { + if (newValue) { + CreateBackground(); + } else { + RemoveBackground(); + document.getElementById('container')!.style.background = 'var(--background-secondary)'; + } + } + + private handleTransparencyEffectsChange(newValue: boolean) { + if (newValue) { + document.documentElement.classList.add('transparencyEffects'); + } else { + document.documentElement.classList.remove('transparencyEffects'); + } + } +} \ No newline at end of file diff --git a/src/seqta/utils/listeners/StorageListener.ts b/src/seqta/utils/listeners/StorageListener.ts deleted file mode 100644 index 6bcc45bb..00000000 --- a/src/seqta/utils/listeners/StorageListener.ts +++ /dev/null @@ -1,162 +0,0 @@ -import browser from 'webextension-polyfill' - -import { - CreateBackground, - CreateCustomShortcutDiv, - RemoveBackground, - RemoveShortcutDiv, - addShortcuts, - disableNotificationCollector, - enableNotificationCollector, -} from '../../../SEQTA'; -import { updateBgDurations } from '../../ui/Animation'; -import { getDarkMode, updateAllColors } from '../../ui/colors/Manager'; - - -export default class StorageListener { - darkMode: any; - constructor() { - this.darkMode = getDarkMode(); - browser.storage.onChanged.addListener(this.handleStorageChanges.bind(this)); - } - - handleStorageChanges(changes: any) { - Object.keys(changes).forEach((changeKey) => { - switch (changeKey) { - - case 'selectedColor': - this.handleSelectedColorChange(changes.selectedColor.newValue); - break; - - case 'onoff': - this.handleOnOffChange(); - break; - - case 'shortcuts': - this.handleShortcutsChange( - changes.shortcuts.oldValue, - changes.shortcuts.newValue - ); - break; - - case 'DarkMode': - this.darkMode = changes.DarkMode.newValue; - break; - - case 'customshortcuts': - if (changes.customshortcuts.newValue) { - this.handleCustomShortcutsChange( - changes.customshortcuts.oldValue, - changes.customshortcuts.newValue - ); - } - break; - - case 'notificationcollector': - this.handleNotificationCollectorChange(changes.notificationcollector); - break; - - case 'bksliderinput': - updateBgDurations(changes.bksliderinput.newValue); - break; - - case 'animatedbk': - if (changes.animatedbk.newValue) { - CreateBackground(); - } else { - RemoveBackground(); - document.getElementById('container')!.style.background = 'var(--background-secondary)'; - } - break; - - case 'transparencyEffects': - if (changes.transparencyEffects.newValue) { - document.documentElement.classList.add('transparencyEffects'); - } else { - document.documentElement.classList.remove('transparencyEffects'); - } - break; - - case 'theme': - console.debug(changes.theme.newValue) - break; - - default: - break; - } - }); - } - - handleSelectedColorChange(newColor: any) { - try { - updateAllColors(this.darkMode, newColor); - } catch (err) { - console.error(err); - } - } - - handleOnOffChange() { - browser.runtime.sendMessage({ type: 'reloadTabs' }) - } - - handleNotificationCollectorChange(details: any) { - if (details.newValue) { - enableNotificationCollector(); - } else { - disableNotificationCollector(); - } - } - - handleCustomShortcutsChange(oldValue: any, newValue: any) { - // Check for addition - if (newValue.length > oldValue.length) { - CreateCustomShortcutDiv(newValue[oldValue.length]); - } - // Check for removal - else if (newValue.length < oldValue.length) { - const removedElement = oldValue.find( - (oldItem: any) => - !newValue.some( - (newItem: any) => JSON.stringify(oldItem) === JSON.stringify(newItem) - ) - ); - - if (removedElement) { - RemoveShortcutDiv([ removedElement ]); - } - } - } - - handleShortcutsChange(oldValue: any, newValue: any) { - // Find Added Shortcuts - const addedShortcuts = newValue.filter((newItem: any) => { - const isAdded = oldValue.some((oldItem: any) => { - const match = oldItem.name === newItem.name; - const wasDisabled = !oldItem.enabled; - const isEnabled = newItem.enabled; - return match && wasDisabled && isEnabled; - }); - - return isAdded; - }); - - // Find Removed Shortcuts - const removedShortcuts = newValue.filter((newItem: any) => { - const isRemoved = oldValue.some((oldItem: any) => { - const match = oldItem.name === newItem.name; - const wasEnabled = oldItem.enabled; // Was enabled in the old array - const isDisabled = !newItem.enabled; // Is disabled in the new array - - return match && wasEnabled && isDisabled; - }); - - return isRemoved; - }); - - // Add new shortcuts to the UI - addShortcuts(addedShortcuts); - - // Remove deleted shortcuts from the UI - RemoveShortcutDiv(removedShortcuts); - } -} \ No newline at end of file