214 lines
7.3 KiB
TypeScript
214 lines
7.3 KiB
TypeScript
|
import CropModePersistence from '../../../common/enums/CropModePersistence.enum';
|
||
|
import ExtensionMode from '../../../common/enums/ExtensionMode.enum';
|
||
|
import { SiteSettingsInterface } from '../../../common/interfaces/SettingsInterface';
|
||
|
import { _cp } from '../../../common/js/utils';
|
||
|
import Settings from '../Settings';
|
||
|
import { browser } from 'webextension-polyfill-ts';
|
||
|
|
||
|
export class SiteSettings {
|
||
|
private settings: Settings;
|
||
|
private site: string;;
|
||
|
|
||
|
data: SiteSettingsInterface;
|
||
|
temporaryData: SiteSettingsInterface;
|
||
|
sessionData: SiteSettingsInterface;
|
||
|
private defaultSettings: SiteSettingsInterface;
|
||
|
|
||
|
//#region lifecycle
|
||
|
constructor(settings: Settings, site: string) {
|
||
|
this.settings = settings;
|
||
|
this.data = settings.active.sites[site];
|
||
|
this.defaultSettings = settings.default.sites['@global'];
|
||
|
|
||
|
this.compileSettingsObject();
|
||
|
|
||
|
// temporary data starts as a copy of real data. This ensures defaults are correct if nothing
|
||
|
// modifies temporary settings. (Purpose: providing until-reload persistence option)
|
||
|
this.temporaryData = _cp(this.data);
|
||
|
|
||
|
this.sessionData = JSON.parse(sessionStorage.getItem('uw-session-defaults'));
|
||
|
if (!this.sessionData) {
|
||
|
this.sessionData = _cp(this.data);
|
||
|
sessionStorage.setItem('uw-session-defaults', JSON.stringify(this.sessionData));
|
||
|
}
|
||
|
|
||
|
browser.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)})
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Merges defaultSettings into site settings where appropriate.
|
||
|
* Alan pls ensure default settings object follows the correct structure
|
||
|
*/
|
||
|
private compileSettingsObject() {
|
||
|
if (!this.data) {
|
||
|
this.data = _cp(this.defaultSettings);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.data.defaultCrop = this.data.defaultCrop ?? _cp(this.defaultSettings.defaultCrop);
|
||
|
this.data.defaultStretch = this.data.defaultStretch ?? _cp(this.defaultSettings.defaultStretch);
|
||
|
|
||
|
|
||
|
for (const enableSegment of ['enable', 'enableAard', 'enableKeyboard']) {
|
||
|
if (!this.data[enableSegment]) {
|
||
|
this.data[enableSegment] = {};
|
||
|
}
|
||
|
for (const environment of ['normal', 'theater', 'fullscreen']) {
|
||
|
if (!this.data[enableSegment][environment] || this.data[enableSegment][environment] === ExtensionMode.Default) {
|
||
|
this.data[enableSegment][environment] = _cp(this.defaultSettings[enableSegment][environment]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ensure data.persistOption exists:
|
||
|
if (!this.data.persistOption) {
|
||
|
this.data.persistOption = {} as any; // this will get populated correctly soon
|
||
|
}
|
||
|
for (const persistOption of ['crop', 'stretch', 'alignment']) {
|
||
|
if ( (this.data.persistOption[persistOption] ?? CropModePersistence.Default) === CropModePersistence.Default ) {
|
||
|
this.data.persistOption[persistOption] = this.defaultSettings.persistOption[persistOption];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (this.data.activeDOMConfig && this.data.DOMConfig) {
|
||
|
this.data.currentDOMConfig = this.data.DOMConfig[this.data.activeDOMConfig];
|
||
|
}
|
||
|
}
|
||
|
//#endregion
|
||
|
|
||
|
//#region events
|
||
|
/**
|
||
|
* Updates setting values for current and default site
|
||
|
* @param changes
|
||
|
* @param area
|
||
|
* @returns
|
||
|
*/
|
||
|
private storageChangeListener(changes, area) {
|
||
|
if (!changes.uwSettings) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const parsedSettings = JSON.parse(changes.uwSettings.newValue);
|
||
|
this.data = parsedSettings.active.sites[this.site];
|
||
|
this.defaultSettings = parsedSettings.active.sites['@global'];
|
||
|
|
||
|
this.compileSettingsObject();
|
||
|
}
|
||
|
//#endregion
|
||
|
|
||
|
/**
|
||
|
* Gets custom query selector for player or video, if element exists, is manually defined, and has querySelectors property.
|
||
|
* @param element player or video
|
||
|
* @returns querySelector if possible, undefined otherwise
|
||
|
*/
|
||
|
getCustomDOMQuerySelector(element: 'video' | 'player'): string | undefined {
|
||
|
return this.data.currentDOMConfig?.elements?.[element]?.manual && this.data.currentDOMConfig?.elements?.[element]?.querySelectors || undefined;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets option value.
|
||
|
* @param optionPath path to value in object notation (dot separated)
|
||
|
* @param optionValue new value of option
|
||
|
* @param reload whether we should trigger a reload in components that require it
|
||
|
*/
|
||
|
async set(optionPath: string, optionValue: any, reload: boolean = false) {
|
||
|
// if no settings exist for this site, create an empty object
|
||
|
if (!this.settings.active.sites[this.site]) {
|
||
|
this.settings.active.sites[this.site] = _cp(this.settings.active.sites['@empty']);
|
||
|
}
|
||
|
|
||
|
const pathParts = optionPath.split('.');
|
||
|
|
||
|
let iterator = this.settings.active.sites[this.site];
|
||
|
let i;
|
||
|
for (i = 0; i < pathParts.length - 1; i++) {
|
||
|
iterator = iterator[pathParts[i]];
|
||
|
}
|
||
|
iterator[pathParts[i]] = optionValue;
|
||
|
|
||
|
if (reload) {
|
||
|
this.settings.save();
|
||
|
} else {
|
||
|
this.settings.saveWithoutReload();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets temporary option value (for Persistence.UntilReload)
|
||
|
* @param optionPath path to value in object notation (dot separated)
|
||
|
* @param optionValue new value of option
|
||
|
*/
|
||
|
setTemporary(optionPath: string, optionValue: any) {
|
||
|
const pathParts = optionPath.split('.');
|
||
|
|
||
|
let iterator = this.temporaryData;
|
||
|
let i;
|
||
|
for (i = 0; i < pathParts.length - 1; i++) {
|
||
|
iterator = iterator[pathParts[i]];
|
||
|
}
|
||
|
iterator[pathParts[i]] = optionValue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets option value to sessionStorage (for Persistence.CurrentSession)
|
||
|
* @param optionPath path to value in object notation (dot separated)
|
||
|
* @param optionValue new value of option
|
||
|
*/
|
||
|
setSession(optionPath: string, optionValue: any) {
|
||
|
const pathParts = optionPath.split('.');
|
||
|
|
||
|
let iterator = this.sessionData;
|
||
|
let i;
|
||
|
for (i = 0; i < pathParts.length - 1; i++) {
|
||
|
iterator = iterator[pathParts[i]];
|
||
|
}
|
||
|
iterator[pathParts[i]] = optionValue;
|
||
|
|
||
|
sessionStorage.setItem('uw-session-defaults', JSON.stringify(this.sessionData));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets default crop mode for extension, while taking persistence settings into account
|
||
|
*/
|
||
|
getDefaultOption(option: 'crop' | 'stretch' | 'alignment') {
|
||
|
const persistenceLevel = this.data.persistOption[option];
|
||
|
|
||
|
switch (persistenceLevel) {
|
||
|
case CropModePersistence.UntilPageReload:
|
||
|
return this.temporaryData.defaults[option];
|
||
|
case CropModePersistence.CurrentSession:
|
||
|
return this.sessionData.defaults[option];
|
||
|
case CropModePersistence.Disabled:
|
||
|
case CropModePersistence.Default:
|
||
|
case CropModePersistence.Forever:
|
||
|
default:
|
||
|
return this.data.defaults[option];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates options while accounting for persistence settings
|
||
|
* @param option
|
||
|
* @param value
|
||
|
* @returns
|
||
|
*/
|
||
|
async updatePersistentOption(option: 'crop' | 'stretch' | 'alignment', value) {
|
||
|
const persistenceLevel = this.data.persistOption[option];
|
||
|
switch (persistenceLevel) {
|
||
|
case CropModePersistence.Disabled:
|
||
|
return;
|
||
|
case CropModePersistence.UntilPageReload:
|
||
|
return this.temporaryData.defaults[option] = value;
|
||
|
case CropModePersistence.CurrentSession:
|
||
|
this.sessionData.defaults[option] = value;
|
||
|
return sessionStorage.setItem('uw-session-defaults', JSON.stringify(this.sessionData));
|
||
|
case CropModePersistence.Forever:
|
||
|
return this.set(`defaults.${option}`, value); // async, but we don't care in this case IIRC
|
||
|
default:
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|