implement vanilla js SettingsState class to optimise code and clean up source :DDD

This commit is contained in:
sethburkart123
2024-06-09 13:15:33 +10:00
parent 68524fe4d7
commit 1c125b1844
5 changed files with 209 additions and 192 deletions
+9 -7
View File
@@ -12,7 +12,7 @@ import { MessageHandler } from './seqta/utils/listeners/MessageListener'
import { SettingsState } from "./types/storage" import { SettingsState } from "./types/storage"
import ShortcutLinks from './seqta/content/links.json' import ShortcutLinks from './seqta/content/links.json'
import Sortable from 'sortablejs' 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 { appendBackgroundToUI } from './seqta/ui/ImageBackgrounds'
import assessmentsicon from './seqta/icons/assessmentsIcon' import assessmentsicon from './seqta/icons/assessmentsIcon'
import browser from 'webextension-polyfill' import browser from 'webextension-polyfill'
@@ -28,6 +28,8 @@ import { SettingsResizer } from "./seqta/ui/SettingsResizer";
import documentLoadCSS from './css/documentload.scss?inline' import documentLoadCSS from './css/documentload.scss?inline'
import injectedCSS from './css/injected.scss?inline' import injectedCSS from './css/injected.scss?inline'
import { injectYouTubeVideo } from './seqta/ui/VideoLoader' import { injectYouTubeVideo } from './seqta/ui/VideoLoader'
import { settingsState } from './seqta/utils/listeners/SettingsState'
import { StorageChangeHandler } from './seqta/utils/listeners/StorageChanges'
declare global { declare global {
interface Window { interface Window {
@@ -788,7 +790,7 @@ function main(storedSetting: SettingsState) {
document.querySelector('.legacy-root')?.classList.add('hidden') document.querySelector('.legacy-root')?.classList.add('hidden')
new StorageListener() new StorageChangeHandler();
new MessageHandler() new MessageHandler()
updateAllColors(storedSetting) updateAllColors(storedSetting)
@@ -1378,14 +1380,14 @@ async function AddBetterSEQTAElements(toggle: any) {
const result = browser.storage.local.get() const result = browser.storage.local.get()
result.then(resolve, onError) result.then(resolve, onError)
}) })
console.log(!settingsState.DarkMode)
settingsState.DarkMode = !settingsState.DarkMode;
const newDarkMode = !result!.DarkMode updateAllColors(!settingsState.DarkMode, result.selectedColor)
browser.storage.local.set({ DarkMode: newDarkMode })
updateAllColors(newDarkMode, result.selectedColor)
const darklightText = document.getElementById('darklighttooliptext') const darklightText = document.getElementById('darklighttooliptext')
darklightText!.innerText = GetLightDarkModeString(newDarkMode) darklightText!.innerText = GetLightDarkModeString(!settingsState.DarkMode)
}) })
// Locate the menuToggle element // Locate the menuToggle element
+7 -23
View File
@@ -2,7 +2,7 @@ import browser from 'webextension-polyfill'
import { GetThresholdOfColor } from '../../../SEQTA'; import { GetThresholdOfColor } from '../../../SEQTA';
import { lightenAndPaleColor } from './lightenAndPaleColor'; import { lightenAndPaleColor } from './lightenAndPaleColor';
import ColorLuminance from './ColorLuminance'; import ColorLuminance from './ColorLuminance';
import { SettingsState } from '../../../types/storage'; import { settingsState } from '../../utils/listeners/SettingsState';
import icon48 from '../../../resources/icons/icon-48.png'; 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 getChromeURL = (path: any) => browser.runtime.getURL(path);
const applyProperties = (props: any) => Object.entries(props).forEach(([key, value]) => setCSSVar(key, value)); const applyProperties = (props: any) => Object.entries(props).forEach(([key, value]) => setCSSVar(key, value));
let DarkMode: any = null;
export function updateAllColors(storedSetting: any, newColor = null) { export function updateAllColors(storedSetting: any, newColor = null) {
// Determine the color to use // Determine the color to use
@@ -24,30 +23,24 @@ export function updateAllColors(storedSetting: any, newColor = null) {
document.documentElement.classList.add('transparencyEffects'); document.documentElement.classList.add('transparencyEffects');
} }
DarkMode = (typeof storedSetting?.DarkMode === 'boolean') ? storedSetting.DarkMode : DarkMode;
if (typeof storedSetting === 'boolean') {
DarkMode = storedSetting;
}
// Common properties, always applied // Common properties, always applied
const commonProps = { const commonProps = {
'--better-sub': '#161616', '--better-sub': '#161616',
'--better-alert-highlight': '#c61851', '--better-alert-highlight': '#c61851',
'--better-main': selectedColor '--better-main': settingsState.selectedColor
}; };
// Mode-based properties, applied if storedSetting is provided // Mode-based properties, applied if storedSetting is provided
let modeProps = {}; let modeProps = {};
if (DarkMode !== null) { if (settingsState.DarkMode) {
modeProps = DarkMode ? { modeProps = settingsState.DarkMode ? {
'--betterseqta-logo': `url(${browser.runtime.getURL(darkLogo)})` '--betterseqta-logo': `url(${browser.runtime.getURL(darkLogo)})`
} : { } : {
'--better-pale': lightenAndPaleColor(selectedColor), '--better-pale': lightenAndPaleColor(selectedColor),
'--betterseqta-logo': `url(${browser.runtime.getURL(lightLogo)})` '--betterseqta-logo': `url(${browser.runtime.getURL(lightLogo)})`
}; };
if (DarkMode) { if (settingsState.DarkMode) {
document.documentElement.style.removeProperty('--better-pale'); document.documentElement.style.removeProperty('--better-pale');
document.documentElement.classList.add('dark'); document.documentElement.classList.add('dark');
} else { } else {
@@ -67,7 +60,7 @@ export function updateAllColors(storedSetting: any, newColor = null) {
applyProperties({ ...commonProps, ...modeProps, ...dynamicProps }); applyProperties({ ...commonProps, ...modeProps, ...dynamicProps });
// Set favicon, if storedSetting is provided // Set favicon, if storedSetting is provided
if (DarkMode !== null) { if (settingsState.DarkMode !== null) {
(document.querySelector('link[rel*=\'icon\']')! as HTMLLinkElement).href = getChromeURL(icon48); (document.querySelector('link[rel*=\'icon\']')! as HTMLLinkElement).href = getChromeURL(icon48);
} }
@@ -80,19 +73,10 @@ export function updateAllColors(storedSetting: any, newColor = null) {
continue; continue;
} }
if (DarkMode) { if (settingsState.DarkMode) {
element.contentDocument?.body.classList.add('dark'); element.contentDocument?.body.classList.add('dark');
} else { } else {
element.contentDocument?.body.classList.remove('dark'); 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;
}
} }
@@ -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<StorageManager> = {
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<void> {
const result = await browser.storage.local.get();
this.data = { ...this.data, ...result };
}
private async saveToStorage(): Promise<void> {
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();
+115
View File
@@ -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');
}
}
}
@@ -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);
}
}