diff --git a/source/background-scripts/context-menus.ts b/source/background-scripts/context-menus.ts new file mode 100644 index 0000000..83bd7ee --- /dev/null +++ b/source/background-scripts/context-menus.ts @@ -0,0 +1,56 @@ +import browser from 'webextension-polyfill'; + +import {updateBadge} from '../utilities/badge.js'; + +type ContextMenu = browser.Menus.CreateCreatePropertiesType; + +export function getContextMenus(): ContextMenu[] { + const actionContext = + import.meta.env.VITE_BROWSER === 'chromium' ? 'action' : 'browser_action'; + + const contextMenus: ContextMenu[] = [ + { + id: 're-nav-toggle-redirects', + title: 'Toggle all redirects', + contexts: [actionContext], + }, + ]; + + return contextMenus; +} + +export async function initializeContextMenus(): Promise { + const contextMenus = getContextMenus(); + + await browser.contextMenus.removeAll(); + + for (const contextMenu of contextMenus) { + browser.contextMenus.create(contextMenu, contextCreated); + } +} + +function contextCreated(): void { + const error = browser.runtime.lastError; + + if (error !== null && error !== undefined) { + console.error('Re-Nav', error.message); + } +} + +export async function contextClicked( + contextMenuIds: Set, + info: browser.Menus.OnClickData, + tab?: browser.Tabs.Tab, +): Promise { + const id = info.menuItemId.toString(); + if (!contextMenuIds.has(id)) { + return; + } + + if (id === 're-nav-toggle-redirects') { + const state = await browser.storage.local.get({redirectsEnabled: true}); + const redirectsEnabled = !(state.redirectsEnabled as boolean); + await browser.storage.local.set({redirectsEnabled}); + await updateBadge(redirectsEnabled); + } +} diff --git a/source/background-scripts/initialize.ts b/source/background-scripts/initialize.ts index 6899335..c79d0d1 100644 --- a/source/background-scripts/initialize.ts +++ b/source/background-scripts/initialize.ts @@ -1,6 +1,12 @@ import browser from 'webextension-polyfill'; import storage from '../redirect/storage.js'; +import {updateBadge} from '../utilities/badge.js'; +import { + contextClicked, + getContextMenus, + initializeContextMenus, +} from './context-menus.js'; async function browserActionClicked() { await browser.runtime.openOptionsPage(); @@ -10,14 +16,22 @@ if (import.meta.env.VITE_BROWSER === 'chromium') { browser.action.onClicked.addListener(browserActionClicked); } else { browser.browserAction.onClicked.addListener(browserActionClicked); + void initializeContextMenus(); } browser.runtime.onInstalled.addListener(async () => { + await initializeContextMenus(); + await updateBadge(); + if (import.meta.env.DEV) { await browser.runtime.openOptionsPage(); } }); +browser.runtime.onStartup.addListener(async () => { + await updateBadge(); +}); + browser.webNavigation.onBeforeNavigate.addListener(async (details) => { if (!details.url.startsWith('http') || details.frameId > 0) { return; @@ -64,3 +78,12 @@ browser.webNavigation.onBeforeNavigate.addListener(async (details) => { } } }); + +browser.contextMenus.onClicked.addListener(async (info, tab) => { + const contextMenus = getContextMenus(); + const contextMenuIds = new Set( + contextMenus.map(({id}) => id ?? 're-nav-unknown'), + ); + + await contextClicked(contextMenuIds, info, tab); +}); diff --git a/source/manifest.ts b/source/manifest.ts index 677016b..cbaa466 100644 --- a/source/manifest.ts +++ b/source/manifest.ts @@ -7,7 +7,7 @@ export default function createManifest( name: 'Re-Nav', description: 'Navigation redirects for the masses.', version: '0.1.1', - permissions: ['storage', 'tabs', 'webNavigation'], + permissions: ['contextMenus', 'storage', 'tabs', 'webNavigation'], options_ui: { page: 'options/index.html', open_in_tab: true, diff --git a/source/utilities/badge.ts b/source/utilities/badge.ts new file mode 100644 index 0000000..5a8152d --- /dev/null +++ b/source/utilities/badge.ts @@ -0,0 +1,25 @@ +import browser from 'webextension-polyfill'; + +export async function updateBadge(redirectsEnabled?: boolean): Promise { + if (redirectsEnabled === undefined) { + const state = await browser.storage.local.get({redirectsEnabled: true}); + redirectsEnabled = state.redirectsEnabled as boolean; + } + + let action: browser.Action.Static = browser.browserAction; + if (import.meta.env.VITE_BROWSER === 'chromium') { + action = browser.action; + } + + await action.setBadgeText({ + text: redirectsEnabled ? '' : '✗', + }); + + await action.setBadgeBackgroundColor({ + color: '#f99fb1', + }); + + if (import.meta.env.VITE_BROWSER === 'firefox') { + action.setBadgeTextColor({color: '#2a2041'}); + } +}