diff --git a/ext/src/defaultOptions.ts b/ext/src/defaultOptions.ts index e065569..ae11533 100644 --- a/ext/src/defaultOptions.ts +++ b/ext/src/defaultOptions.ts @@ -10,10 +10,13 @@ export interface Options { mirroringAppId: string; userAgentWhitelistEnabled: boolean; userAgentWhitelist: string[]; + + [key: string]: Options[keyof Options]; } const options: Options = { - mediaEnabled: true + bridgeApplicationName: APPLICATION_NAME + , mediaEnabled: true , mediaSyncElement: false , mediaStopOnUnload: false , localMediaEnabled: true diff --git a/ext/src/lib/options.ts b/ext/src/lib/options.ts new file mode 100644 index 0000000..8ca88e4 --- /dev/null +++ b/ext/src/lib/options.ts @@ -0,0 +1,54 @@ +"use strict"; + +import { Options } from "../defaultOptions"; + + +/** + * Fetches `options` key from storage and returns it as + * Options interface type. + */ +async function getAll (): Promise { + const { options }: { options: Options } = + await browser.storage.sync.get("options"); + + return options; +} + +/** + * Takes Options object and sets to `options` storage key. + * Returns storage promise. + */ +async function setAll (options: Options): Promise { + return browser.storage.sync.set({ options }); +} + +/** + * Gets specific option from storage and returns it as its + * type from Options interface type. + */ +async function get (name: T): Promise { + const options = await getAll(); + + if (options.hasOwnProperty(name)) { + return options[name]; + } +} + +/** + * Sets specific option to storage. Returns storage + * promise. + */ +async function set ( + name: T + , value: Options[T]): Promise { + + const options = await getAll(); + options[name] = value; + return setAll(options); +} + + +export default { + get, getAll + , set, setAll +} diff --git a/ext/src/main.ts b/ext/src/main.ts index b256c2d..93e2764 100755 --- a/ext/src/main.ts +++ b/ext/src/main.ts @@ -5,6 +5,7 @@ import semver from "semver"; import defaultOptions, { Options } from "./defaultOptions"; import getBridgeInfo from "./lib/getBridgeInfo"; import messageRouter from "./lib/messageRouter"; +import options from "./lib/options"; import nativeMessaging from "./lib/nativeMessaging"; import { getChromeUserAgent } from "./lib/userAgents"; @@ -29,17 +30,13 @@ browser.runtime.onInstalled.addListener(async details => { switch (details.reason) { // Set default options case "install": { - await browser.storage.sync.set({ - options: defaultOptions - }); + await options.setAll(defaultOptions); break; } // Set newly added options case "update": { - const { options: existingOptions } - = await browser.storage.sync.get("options"); - + const existingOptions = await options.getAll(); const newOptions: Partial = {}; // Find options not already in storage @@ -50,11 +47,9 @@ browser.runtime.onInstalled.addListener(async details => { } // Update storage with default values of new options - await browser.storage.sync.set({ - options: { - ...existingOptions - , ...newOptions - } + options.setAll({ + ...existingOptions + , ...newOptions }); break; @@ -87,17 +82,17 @@ const mediaCastTargetUrlPatterns = new Set([ const LOCAL_MEDIA_URL_PATTERN = "file://*/*"; async function createMenus () { - const { options } = await browser.storage.sync.get("options"); + const opts = await options.getAll(); /** * If options aren't set or menus have already been * created, return. */ - if (!options || mirrorCastMenuId || mediaCastMenuId) { + if (!opts || mirrorCastMenuId || mediaCastMenuId) { return; } - if (options.localMediaEnabled) { + if (opts.localMediaEnabled) { mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN); } @@ -107,7 +102,7 @@ async function createMenus () { , id: "contextCastMedia" , targetUrlPatterns: Array.from(mediaCastTargetUrlPatterns) , title: _("contextCast") - , visible: options.mediaEnabled + , visible: opts.mediaEnabled }); // Screen/Tab mirroring "Cast..." context menu item @@ -115,7 +110,7 @@ async function createMenus () { contexts: [ "browser_action", "page", "tools_menu" ] , id: "contextCast" , title: _("contextCast") - , visible: options.mirroringEnabled + , visible: opts.mirroringEnabled // Mirroring doesn't work from local files , documentUrlPatterns: [ @@ -317,7 +312,6 @@ let currentUAString: string; async function onBeforeSendHeaders ( details: { requestHeaders?: browser.webRequest.HttpHeaders }) { - const { options } = await browser.storage.sync.get("options"); const { os } = await browser.runtime.getPlatformInfo(); // Create Chrome UA from platform info on first run @@ -353,10 +347,10 @@ async function onBeforeSendHeaders ( * Updates any extension state based on options changes. */ async function onOptionsUpdated (alteredOptions?: Array<(keyof Options)>) { - const { options } = await browser.storage.sync.get("options"); + const opts = await options.getAll(); // If options aren't set yet, return - if (!options) { + if (!opts) { return; } @@ -367,8 +361,8 @@ async function onOptionsUpdated (alteredOptions?: Array<(keyof Options)>) { function register_userAgentWhitelist () { browser.webRequest.onBeforeSendHeaders.addListener( onBeforeSendHeaders - , { urls: options.userAgentWhitelistEnabled - ? options.userAgentWhitelist + , { urls: opts.userAgentWhitelistEnabled + ? opts.userAgentWhitelist : [] } , [ "blocking", "requestHeaders" ]); } @@ -394,18 +388,18 @@ async function onOptionsUpdated (alteredOptions?: Array<(keyof Options)>) { if (alteredOptions.includes("mirroringEnabled")) { browser.menus.update(mirrorCastMenuId, { - visible: options.mirroringEnabled + visible: opts.mirroringEnabled }); } if (alteredOptions.includes("mediaEnabled")) { browser.menus.update(mediaCastMenuId, { - visible: options.mediaEnabled + visible: opts.mediaEnabled }); } if (alteredOptions.includes("localMediaEnabled")) { - if (options.localMediaEnabled) { + if (opts.localMediaEnabled) { mediaCastTargetUrlPatterns.add(LOCAL_MEDIA_URL_PATTERN); } else { mediaCastTargetUrlPatterns.delete(LOCAL_MEDIA_URL_PATTERN); @@ -448,7 +442,7 @@ browser.menus.onClicked.addListener(async (info, tab) => { || info.menuItemId === mediaCastMenuId) { const { frameId } = info; - const { options } = await browser.storage.sync.get("options"); + const mirroringAppId = await options.get("mirroringAppId"); // Load cast setup script await browser.tabs.executeScript(tab.id, { @@ -466,7 +460,7 @@ browser.menus.onClicked.addListener(async (info, tab) => { var selectedMedia = ${info.pageUrl ? ReceiverSelectorMediaType.Tab : ReceiverSelectorMediaType.Screen}; - var FX_CAST_RECEIVER_APP_ID = "${options.mirroringAppId}"; + var FX_CAST_RECEIVER_APP_ID = "${mirroringAppId}"; ` , frameId }); @@ -513,16 +507,13 @@ browser.menus.onClicked.addListener(async (info, tab) => { if (info.parentMenuItemId === whitelistMenuId) { const matchPattern = whitelistMenuMap.get(info.menuItemId); - const options: Options = - (await browser.storage.sync.get("options")).options; + const userAgentWhitelist = await options.get("userAgentWhitelist"); // Add to whitelist - options.userAgentWhitelist.push(matchPattern); + userAgentWhitelist.push(matchPattern); // Update options - await browser.storage.sync.set({ - options - }); + await options.set("userAgentWhitelist", userAgentWhitelist) } }); diff --git a/ext/src/ui/options/index.tsx b/ext/src/ui/options/index.tsx index 6bb67cf..1ad5ba3 100644 --- a/ext/src/ui/options/index.tsx +++ b/ext/src/ui/options/index.tsx @@ -10,6 +10,7 @@ import Bridge from "./Bridge"; import EditableList from "./EditableList"; import getBridgeInfo, { BridgeInfo } from "../../lib/getBridgeInfo"; +import options from "../../lib/options"; import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/utils"; @@ -77,11 +78,9 @@ class OptionsApp extends Component<{}, OptionsAppState> { } public async componentDidMount () { - const { options } = await browser.storage.sync.get("options"); - this.setState({ hasLoaded: true - , options + , options: await options.getAll() }); const bridgeInfo = await getBridgeInfo(); @@ -259,14 +258,6 @@ class OptionsApp extends Component<{}, OptionsAppState> { ); } - /** - * Set stored option values to current state - */ - private setStorage () { - return browser.storage.sync.set({ - options: this.state.options - }); - } private handleReset () { this.setState({ @@ -280,15 +271,12 @@ class OptionsApp extends Component<{}, OptionsAppState> { this.form.reportValidity(); try { - const { options: oldOptions } - = await browser.storage.sync.get("options"); - await this.setStorage(); - const { options } = await browser.storage.sync.get("options"); + const oldOpts = await options.getAll(); + await options.setAll(this.state.options); const alteredOptions = []; - - for (const [ key, val ] of Object.entries(options)) { - const oldVal = oldOptions[key]; + for (const [ key, val ] of Object.entries(this.state.options)) { + const oldVal = oldOpts[key]; if (oldVal !== val) { alteredOptions.push(key); } @@ -325,16 +313,16 @@ class OptionsApp extends Component<{}, OptionsAppState> { private handleInputChange (ev: React.ChangeEvent) { const { target } = ev; - this.setState(({ options }) => { - options[target.name as keyof Options] = getInputValue(target); - return { options }; + this.setState(currentState => { + currentState.options[target.name] = getInputValue(target); + return currentState; }); } private handleWhitelistChange (whitelist: string[]) { - this.setState(({ options }) => { - options.userAgentWhitelist = whitelist; - return { options }; + this.setState(currentState => { + currentState.options.userAgentWhitelist = whitelist; + return currentState; }); }