diff --git a/extension/icon.svg b/extension/icon_disabled.svg similarity index 100% rename from extension/icon.svg rename to extension/icon_disabled.svg diff --git a/extension/icon_enabled.svg b/extension/icon_enabled.svg new file mode 100644 index 0000000..5e0e2f9 --- /dev/null +++ b/extension/icon_enabled.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/extension/manifest.json b/extension/manifest.json index 368618e..c9ab94f 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -5,7 +5,7 @@ "page_action": { "browser_style": true, "default_title": "Ban banners", - "default_icon": "/icon.svg" + "default_icon": "/icon_enabled.svg" }, "background": { "scripts": ["background_script.js"] diff --git a/src/PageActionButton.js b/src/PageActionButton.js new file mode 100644 index 0000000..c88f07a --- /dev/null +++ b/src/PageActionButton.js @@ -0,0 +1,75 @@ +/* Helper to manage the visibility and icon of a pageAction button. + * Each callback is evaluated whenever the tab has changed. + * Callbacks may be async (i.e. return a Promise). + * + * Arguments: + * - options.visible: Tab => boolean; whether to show the pageAction button + * - options.icon: Tab => either a string (the icon path), or null (to get the + * default icon from the manifest), or an object (with path or imageData, see + * docs of browser.pageAction.setIcon). + * - options.onClicked: Tab => void (or Promise of void); run on click, after + * which the button state is automatically updated on all tabs. + * + * Returns an object with the following methods: + * - update(tab?): update visibility, icons, etcetera of a given tab, or of + * all tabs if the tab argument is omitted. + * + * Example usage: + * const pageActionButton = PageActionButton({ + * visible: tab => tab.url.startsWith('https'), + * icon: tab => isEnabled(tab.url) ? '/icon_enabled.svg' : '/icon_disabled.svg', + * onClicked: tab => toggleEnabled(tab.url), + * }) + * // Manually trigger an update of a tab. + * pageActionButton.update(tab) + */ + +import browser from 'webextension-polyfill' + + +export default function PageActionButton({ visible, icon, onClicked }) { + async function setIcon(tab) { + let iconSetting = await icon(tab) + if (typeof iconSetting === 'string' || iconSetting === null) { + iconSetting = { path: iconSetting } + } + browser.pageAction.setIcon({ + ...iconSetting, + tabId: tab.id, + }) + } + + async function checkTab(tab) { + if (await visible(tab)) { + setIcon(tab) + browser.pageAction.show(tab.id) + } else { + browser.pageAction.hide(tab.id) + } + } + + async function checkAllTabs() { + const tabs = await browser.tabs.query({}) + for (const tab of tabs) { + await checkTab(tab) + } + } + + // Show/hide the button on current tabs (XXX not awaiting it's completion) + checkAllTabs() + + // Show/hide the button whenever a tab changes. + browser.tabs.onUpdated.addListener((id, changeInfo, tab) => { + checkTab(tab) + }); + + // Handle button clicks + browser.pageAction.onClicked.addListener(async tab => { + await onClicked(tab) + await checkAllTabs(tab) + }) + + return { + update: tab => tab ? checkTab(tab) : checkAllTabs() + } +} diff --git a/src/background_script.js b/src/background_script.js index e4a5be7..51ec479 100644 --- a/src/background_script.js +++ b/src/background_script.js @@ -1,8 +1,10 @@ import browser from 'webextension-polyfill' -import pageActionButton from './pageActionButton' -import { toggleEnabled, isEnabled } from './perOrigin' +import PageActionButton from './PageActionButton' +import { toggleEnabled, isEnabled, onEnabledChange } from './perOrigin' - -pageActionButton({ predicate: tab => true }) -browser.pageAction.onClicked.addListener(tab => toggleEnabled(tab.url)) +const pageActionButton = PageActionButton({ + visible: tab => true, + icon: async tab => (await isEnabled(tab.url)) ? '/icon_enabled.svg' : '/icon_disabled.svg', + onClicked: async tab => toggleEnabled(tab.url) +}) diff --git a/src/pageActionButton.js b/src/pageActionButton.js deleted file mode 100644 index 29adb6f..0000000 --- a/src/pageActionButton.js +++ /dev/null @@ -1,31 +0,0 @@ -/* Show a page action button on a tab whenever the given predicate is true. - * The predicate is passed a Tab object, and is evaluated whenever the tab has - * changed. - * - * Example usage: - * pageAction({ predicate: tab => tab.url.startsWith('https') }) - */ - -import browser from 'webextension-polyfill' - - -export default async function pageActionButton({ predicate }) { - async function maybeShowButton(tab) { - if (await predicate(tab)) { - browser.pageAction.show(tab.id) - } else { - browser.pageAction.hide(tab.id) - } - } - - // Show/hide the button on current tabs. - const tabs = await browser.tabs.query({}) - for (const tab of tabs) { - maybeShowButton(tab) - } - - // Show/hide the button whenever a tab changes. - browser.tabs.onUpdated.addListener((id, changeInfo, tab) => { - maybeShowButton(tab) - }); -}