mirror of
https://github.com/tridactyl/tridactyl.git
synced 2026-09-10 07:16:33 -04:00
Fix #822: add browser button to toggle superignore
Ideally this would inherit the user's theme but I don't think I care enough to do that
This commit is contained in:
parent
d0739c51d9
commit
3387a4f864
|
|
@ -1,6 +1,6 @@
|
|||
const esbuild = require('esbuild')
|
||||
|
||||
for (let f of ["content", "background", "help", "newtab", "reader", "commandline_frame", "qrCodeGenerator"]) {
|
||||
for (let f of ["content", "background", "help", "newtab", "reader", "commandline_frame", "qrCodeGenerator", "browser_action_popup"]) {
|
||||
esbuild.build({
|
||||
entryPoints: [`src/${f}.ts`],
|
||||
bundle: true,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import * as omnibox from "@src/background/omnibox"
|
|||
import * as R from "ramda"
|
||||
import * as webrequests from "@src/background/webrequests"
|
||||
import * as commands from "@src/background/commands"
|
||||
import * as browser_action from "@src/background/browser_action"
|
||||
import * as meta from "@src/background/meta"
|
||||
import * as Logging from "@src/lib/logging"
|
||||
import * as Proxy from "@src/lib/proxy"
|
||||
|
|
@ -244,6 +245,10 @@ browser.tabs.onCreated.addListener(aucon.tabCreatedListener)
|
|||
// An object to collect all of our statistics in one place.
|
||||
const statsLogger: perf.StatsLogger = new perf.StatsLogger()
|
||||
const messages = {
|
||||
browser_action_background: {
|
||||
getState: browser_action.getState,
|
||||
toggle: browser_action.toggle,
|
||||
},
|
||||
config_background: {
|
||||
clear: config.clear,
|
||||
pull: config.pull,
|
||||
|
|
@ -288,6 +293,7 @@ omnibox.init()
|
|||
// }}}
|
||||
|
||||
commands.updateListener()
|
||||
browser_action.init()
|
||||
|
||||
// {{{ Obey Mozilla's orders https://github.com/tridactyl/tridactyl/issues/1800
|
||||
|
||||
|
|
|
|||
43
src/background/browser_action.test.ts
Normal file
43
src/background/browser_action.test.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import * as config from "@src/lib/config"
|
||||
import { getState, init, toggle } from "@src/background/browser_action"
|
||||
|
||||
jest.mock("@src/lib/config", () => {
|
||||
const userconfig = { superignore: "false" }
|
||||
return {
|
||||
DEFAULTS: { superignore: "false" },
|
||||
USERCONFIG: userconfig,
|
||||
getAsync: jest.fn().mockResolvedValue(undefined),
|
||||
set: jest.fn((_key, value) => {
|
||||
userconfig.superignore = value
|
||||
return Promise.resolve()
|
||||
}),
|
||||
addChangeListener: jest.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
test("browser action toggles superignore without reloading tabs", async () => {
|
||||
await config.set("superignore", "false")
|
||||
|
||||
init()
|
||||
await expect(getState()).resolves.toBe("false")
|
||||
await expect(Promise.all([toggle(), toggle()])).resolves.toEqual([
|
||||
"true",
|
||||
"false",
|
||||
])
|
||||
expect(config.USERCONFIG.superignore).toBe("false")
|
||||
expect(browser.browserAction.setBadgeText).toHaveBeenCalledWith({
|
||||
text: "OFF",
|
||||
})
|
||||
expect(browser.tabs.reload).not.toHaveBeenCalled()
|
||||
expect(browser.browserAction.onClicked.addListener).not.toHaveBeenCalled()
|
||||
expect(browser.browserAction.setTitle).toHaveBeenLastCalledWith({
|
||||
title: "Tridactyl enabled",
|
||||
})
|
||||
|
||||
jest.mocked(config.set).mockImplementationOnce(async (_key, value) => {
|
||||
config.USERCONFIG.superignore = value as "true" | "false"
|
||||
throw new Error("write failed")
|
||||
})
|
||||
await expect(toggle()).rejects.toThrow("write failed")
|
||||
expect(config.USERCONFIG.superignore).toBe("false")
|
||||
})
|
||||
48
src/background/browser_action.ts
Normal file
48
src/background/browser_action.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import * as config from "@src/lib/config"
|
||||
|
||||
const ready = config.getAsync()
|
||||
const superignore = () =>
|
||||
config.USERCONFIG.superignore ?? config.DEFAULTS.superignore
|
||||
|
||||
function updateButton(value) {
|
||||
const disabled = value === "true"
|
||||
return Promise.all([
|
||||
browser.browserAction.setBadgeText({ text: disabled ? "OFF" : "" }),
|
||||
browser.browserAction.setTitle({
|
||||
title: disabled ? "Tridactyl disabled" : "Tridactyl enabled",
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
export async function getState() {
|
||||
await ready
|
||||
return superignore()
|
||||
}
|
||||
|
||||
let toggleQueue: Promise<unknown> = Promise.resolve()
|
||||
export function toggle() {
|
||||
const pending = toggleQueue
|
||||
.catch(() => undefined)
|
||||
.then(async () => {
|
||||
const previous = config.USERCONFIG.superignore
|
||||
const value = (await getState()) === "true" ? "false" : "true"
|
||||
try {
|
||||
await config.set("superignore", value)
|
||||
} catch (error) {
|
||||
if (previous === undefined) delete config.USERCONFIG.superignore
|
||||
else config.USERCONFIG.superignore = previous
|
||||
throw error
|
||||
}
|
||||
updateButton(value).catch(console.error)
|
||||
return value
|
||||
})
|
||||
toggleQueue = pending
|
||||
return pending
|
||||
}
|
||||
|
||||
export function init() {
|
||||
config.addChangeListener("superignore", (_, value) =>
|
||||
updateButton(value).catch(console.error),
|
||||
)
|
||||
getState().then(updateButton).catch(console.error)
|
||||
}
|
||||
55
src/browser_action_popup.test.ts
Normal file
55
src/browser_action_popup.test.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
jest.mocked(browser.runtime.sendMessage)
|
||||
.mockResolvedValueOnce("false")
|
||||
.mockResolvedValueOnce("true")
|
||||
|
||||
const flush = () => new Promise(resolve => setTimeout(resolve))
|
||||
|
||||
test("popup separates toggling from reloading the active tab", async () => {
|
||||
document.body.innerHTML = `
|
||||
<p id="state"></p>
|
||||
<button id="toggle" disabled></button>
|
||||
<button id="reload" disabled></button>`
|
||||
jest.mocked(browser.tabs.query).mockResolvedValue([
|
||||
{ id: 7 } as browser.tabs.Tab,
|
||||
])
|
||||
jest.mocked(browser.tabs.reload).mockResolvedValue(undefined)
|
||||
const close = jest.spyOn(window, "close").mockImplementation()
|
||||
|
||||
await import("@src/browser_action_popup")
|
||||
await flush()
|
||||
expect(document.querySelector("#state").textContent).toBe(
|
||||
"Enabled globally",
|
||||
)
|
||||
expect(browser.runtime.sendMessage).toHaveBeenCalledWith({
|
||||
type: "browser_action_background",
|
||||
command: "getState",
|
||||
args: [],
|
||||
})
|
||||
|
||||
document.querySelector<HTMLElement>("#toggle").click()
|
||||
expect(document.querySelector<HTMLButtonElement>("#reload").disabled).toBe(
|
||||
true,
|
||||
)
|
||||
await flush()
|
||||
expect(browser.runtime.sendMessage).toHaveBeenCalledWith({
|
||||
type: "browser_action_background",
|
||||
command: "toggle",
|
||||
args: [],
|
||||
})
|
||||
expect(browser.tabs.reload).not.toHaveBeenCalled()
|
||||
expect(document.querySelector("#state").textContent).toBe(
|
||||
"Disabled globally",
|
||||
)
|
||||
|
||||
document.querySelector<HTMLElement>("#reload").click()
|
||||
expect(document.querySelector<HTMLButtonElement>("#toggle").disabled).toBe(
|
||||
true,
|
||||
)
|
||||
await flush()
|
||||
expect(browser.tabs.query).toHaveBeenCalledWith({
|
||||
active: true,
|
||||
currentWindow: true,
|
||||
})
|
||||
expect(browser.tabs.reload).toHaveBeenCalledWith(7)
|
||||
expect(close).toHaveBeenCalled()
|
||||
})
|
||||
63
src/browser_action_popup.ts
Normal file
63
src/browser_action_popup.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
export {}
|
||||
|
||||
function message(command: "getState" | "toggle") {
|
||||
return browser.runtime.sendMessage({
|
||||
type: "browser_action_background",
|
||||
command,
|
||||
args: [],
|
||||
})
|
||||
}
|
||||
|
||||
const state = document.querySelector<HTMLElement>("#state")
|
||||
const toggleButton = document.querySelector<HTMLButtonElement>("#toggle")
|
||||
const reloadButton = document.querySelector<HTMLButtonElement>("#reload")
|
||||
|
||||
function showState(value) {
|
||||
const disabled = value === "true"
|
||||
state.textContent = disabled ? "Disabled globally" : "Enabled globally"
|
||||
toggleButton.textContent = disabled
|
||||
? "Enable Tridactyl"
|
||||
: "Disable Tridactyl"
|
||||
}
|
||||
|
||||
function showError(error) {
|
||||
state.textContent = `Error: ${error instanceof Error ? error.message : error}`
|
||||
}
|
||||
|
||||
function setBusy(busy) {
|
||||
toggleButton.disabled = busy
|
||||
reloadButton.disabled = busy
|
||||
}
|
||||
|
||||
async function run(action: () => Promise<void>) {
|
||||
setBusy(true)
|
||||
try {
|
||||
await action()
|
||||
} catch (error) {
|
||||
showError(error)
|
||||
} finally {
|
||||
setBusy(false)
|
||||
}
|
||||
}
|
||||
|
||||
message("getState")
|
||||
.then(showState)
|
||||
.then(() => setBusy(false), showError)
|
||||
|
||||
toggleButton.addEventListener("click", () =>
|
||||
run(async () => {
|
||||
showState(await message("toggle"))
|
||||
}),
|
||||
)
|
||||
|
||||
reloadButton.addEventListener("click", () =>
|
||||
run(async () => {
|
||||
const [tab] = await browser.tabs.query({
|
||||
active: true,
|
||||
currentWindow: true,
|
||||
})
|
||||
if (tab?.id === undefined) throw new Error("No active tab found")
|
||||
await browser.tabs.reload(tab.id)
|
||||
window.close()
|
||||
}),
|
||||
)
|
||||
|
|
@ -248,7 +248,7 @@ export class default_config {
|
|||
*
|
||||
* You are usually better off using `blacklistadd` and `seturl [url] noiframe true` as you can then still use some Tridactyl binds, e.g. `shift-insert` for exiting ignore mode.
|
||||
*
|
||||
* NB: you should only use this with `seturl`. If you get trapped with Tridactyl disabled everywhere just run `tri unset superignore` in the Firefox address bar. If that still doesn't fix things, you can totally reset Tridactyl by running `tri help superignore` in the Firefox address bar, scrolling to the bottom of that page and then clicking "Reset Tridactyl config".
|
||||
* Use the toolbar popup to toggle this globally. If you get trapped with Tridactyl disabled everywhere just run `tri unset superignore` in the Firefox address bar. If that still doesn't fix things, you can totally reset Tridactyl by running `tri help superignore` in the Firefox address bar, scrolling to the bottom of that page and then clicking "Reset Tridactyl config".
|
||||
*/
|
||||
superignore: "true" | "false" = "false"
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export type TabMessageType =
|
|||
| "commandline_frame_ready_to_receive_messages"
|
||||
|
||||
export type NonTabMessageType =
|
||||
| "browser_action_background"
|
||||
| "owntab_background"
|
||||
| "excmd_background"
|
||||
| "controller_background"
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@
|
|||
"background.js"
|
||||
]
|
||||
},
|
||||
"browser_action": {
|
||||
"default_area": "navbar",
|
||||
"default_icon": "static/logo/Tridactyl_32px.png",
|
||||
"default_popup": "static/browser_action_popup.html",
|
||||
"default_title": "Tridactyl controls"
|
||||
},
|
||||
"chrome_url_overrides": {
|
||||
"newtab": "static/newtab.html"
|
||||
},
|
||||
|
|
|
|||
21
src/static/browser_action_popup.html
Normal file
21
src/static/browser_action_popup.html
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Tridactyl controls</title>
|
||||
<link rel="stylesheet" href="css/browser_action_popup.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Tridactyl</h1>
|
||||
<p id="state" class="state" aria-live="polite">Loading...</p>
|
||||
<button id="toggle" type="button" disabled>Loading...</button>
|
||||
<p class="help">
|
||||
This setting applies globally. Reload pages to apply it.
|
||||
</p>
|
||||
<button id="reload" class="secondary" type="button" disabled>
|
||||
Reload this tab
|
||||
</button>
|
||||
<script src="../browser_action_popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
56
src/static/css/browser_action_popup.css
Normal file
56
src/static/css/browser_action_popup.css
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
@import url("../themes/auto/auto.css");
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
width: 18rem;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
border-top: 0.25rem solid var(--tridactyl-url-fg);
|
||||
color: var(--tridactyl-fg);
|
||||
background: var(--tridactyl-bg);
|
||||
font-family: var(--tridactyl-font-family-sans);
|
||||
font-size: var(--tridactyl-font-size);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0 0 0.75rem;
|
||||
font-family: var(--tridactyl-font-family);
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.state {
|
||||
margin: 0 0 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 0.65rem;
|
||||
border: 1px solid var(--tridactyl-fg);
|
||||
border-radius: var(--tridactyl-status-border-radius);
|
||||
color: var(--tridactyl-bg);
|
||||
background: var(--tridactyl-fg);
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:focus-visible {
|
||||
outline: 2px solid var(--tridactyl-url-fg);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
cursor: wait;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
margin-top: 0.25rem;
|
||||
color: var(--tridactyl-fg);
|
||||
background: var(--tridactyl-bg);
|
||||
}
|
||||
|
||||
.help {
|
||||
font-size: var(--tridactyl-small-font-size);
|
||||
}
|
||||
Loading…
Reference in a new issue