diff --git a/src/config.js b/src/config.js index a93ffe28..f271ace5 100644 --- a/src/config.js +++ b/src/config.js @@ -1,25 +1,81 @@ const CONFIG_KEY = 'ytaf-configuration'; -const defaultConfig = { - enableAdBlock: true, - enableSponsorBlock: true, - enableSponsorBlockSponsor: true, - enableSponsorBlockIntro: true, - enableSponsorBlockOutro: true, - enableSponsorBlockInteraction: true, - enableSponsorBlockSelfPromo: true, - enableSponsorBlockMusicOfftopic: true -}; - -let localConfig; - -try { - localConfig = JSON.parse(window.localStorage[CONFIG_KEY]); -} catch (err) { - console.warn('Config read failed:', err); - localConfig = defaultConfig; + +export const configOptions = new Map([ + ['enableAdBlock', { default: true, desc: 'Enable ad blocking' }], + ['enableSponsorBlock', { default: true, desc: 'Enable SponsorBlock' }], + [ + 'enableSponsorBlockSponsor', + { default: true, desc: 'Skip sponsor segments' } + ], + ['enableSponsorBlockIntro', { default: true, desc: 'Skip intro segments' }], + ['enableSponsorBlockOutro', { default: true, desc: 'Skip outro segments' }], + [ + 'enableSponsorBlockInteraction', + { + default: true, + desc: 'Skip interaction reminder segments' + } + ], + [ + 'enableSponsorBlockSelfPromo', + { + default: true, + desc: 'Skip self promotion segments' + } + ], + [ + 'enableSponsorBlockMusicOfftopic', + { + default: true, + desc: 'Skip music and off-topic segments' + } + ] +]); + +const defaultConfig = (() => { + let ret = {}; + for (const [k, v] of configOptions) { + ret[k] = v.default; + } + return ret; +})(); + +function loadStoredConfig() { + const storage = window.localStorage.getItem(CONFIG_KEY); + + if (storage === null) { + console.info('Config not set; using defaults.'); + return null; + } + + try { + return JSON.parse(storage); + } catch (err) { + console.warn('Error parsing stored config:', err); + return null; + } +} + +// Use defaultConfig as a prototype so writes to localConfig don't change it. +let localConfig = loadStoredConfig() ?? Object.create(defaultConfig); + +function configExists(key) { + return configOptions.has(key); +} + +export function getConfigDesc(key) { + if (!configExists(key)) { + throw new Error('tried to get desc for unknown config key:', key); + } + + return configOptions.get(key).desc; } export function configRead(key) { + if (!configExists(key)) { + throw new Error('tried to read unknown config key:', key); + } + if (localConfig[key] === undefined) { console.warn( 'Populating key', @@ -27,6 +83,7 @@ export function configRead(key) { 'with default value', defaultConfig[key] ); + localConfig[key] = defaultConfig[key]; } @@ -34,6 +91,10 @@ export function configRead(key) { } export function configWrite(key, value) { + if (!configExists(key)) { + throw new Error('tried to write unknown config key:', key); + } + console.info('Setting key', key, 'to', value); localConfig[key] = value; window.localStorage[CONFIG_KEY] = JSON.stringify(localConfig); diff --git a/src/ui.js b/src/ui.js index 4bce127e..06b7788f 100644 --- a/src/ui.js +++ b/src/ui.js @@ -1,33 +1,85 @@ /*global navigate*/ import './spatial-navigation-polyfill.js'; import './ui.css'; -import { configRead, configWrite } from './config.js'; +import { configRead, configWrite, getConfigDesc } from './config.js'; // We handle key events ourselves. window.__spatialNavigation__.keyMode = 'NONE'; const ARROW_KEY_CODE = { 37: 'left', 38: 'up', 39: 'right', 40: 'down' }; -const uiContainer = document.createElement('div'); -uiContainer.classList.add('ytaf-ui-container'); -uiContainer.style['display'] = 'none'; -uiContainer.setAttribute('tabindex', 0); -uiContainer.addEventListener( - 'focus', - () => console.info('uiContainer focused!'), - true -); -uiContainer.addEventListener( - 'blur', - () => console.info('uiContainer blured!'), - true -); - -uiContainer.addEventListener( - 'keydown', - (evt) => { - console.info('uiContainer key event:', evt.type, evt.charCode); - if (evt.charCode !== 404 && evt.charCode !== 172) { +// Red, Green, Yellow, Blue +// 403, 404, 405, 406 +// ---, 172, 170, 191 +const colorCodeMap = new Map([ + [403, 'red'], + + [404, 'green'], + [172, 'green'], + + [405, 'yellow'], + [170, 'yellow'], + + [406, 'blue'], + [191, 'blue'] +]); + +/** + * Returns the name of the color button associated with a code or null if not a color button. + * @param {number} charCode KeyboardEvent.charCode property from event + * @returns {string | null} Color name or null + */ +function getKeyColor(charCode) { + if (colorCodeMap.has(charCode)) { + return colorCodeMap.get(charCode); + } + + return null; +} + +function createConfigCheckbox(key) { + const elmInput = document.createElement('input'); + elmInput.type = 'checkbox'; + elmInput.checked = configRead(key); + elmInput.addEventListener('change', (evt) => { + configWrite(key, evt.target.checked); + }); + + const elmLabel = document.createElement('label'); + elmLabel.appendChild(elmInput); + // Use non-breaking space (U+00A0) + elmLabel.appendChild(document.createTextNode('\u00A0' + getConfigDesc(key))); + + return elmLabel; +} + +function createOptionsPanel() { + const elmContainer = document.createElement('div'); + + elmContainer.classList.add('ytaf-ui-container'); + elmContainer.style['display'] = 'none'; + elmContainer.setAttribute('tabindex', 0); + + elmContainer.addEventListener( + 'focus', + () => console.info('Options panel focused!'), + true + ); + elmContainer.addEventListener( + 'blur', + () => console.info('Options panel blurred!'), + true + ); + + elmContainer.addEventListener( + 'keydown', + (evt) => { + console.info('Options panel key event:', evt.type, evt.charCode); + + if (getKeyColor(evt.charCode) === 'green') { + return; + } + if (evt.keyCode in ARROW_KEY_CODE) { navigate(ARROW_KEY_CODE[evt.keyCode]); } else if (evt.keyCode === 13) { @@ -35,98 +87,67 @@ uiContainer.addEventListener( document.querySelector(':focus').click(); } else if (evt.keyCode === 27) { // Back button - uiContainer.style.display = 'none'; - uiContainer.blur(); + showOptionsPanel(false); } + evt.preventDefault(); evt.stopPropagation(); - } - }, - true -); - -uiContainer.innerHTML = ` -
- - - - - - --