Skip to content

Commit

Permalink
Add commands for focusing audible/muted tabs and for muting/unmuting …
Browse files Browse the repository at this point in the history
…tabs (#311)

* Add command to focus the last tab that emitted sound

* Add command to focus the next tab that is reproducing sound

* Add command to focus the next muted tab

* Add command to focus next tab that is playing audio

* Add several commands for muting or unmuting tabs
  • Loading branch information
david-tejada committed Sep 11, 2024
1 parent abca9f8 commit 2f31ecf
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 0 deletions.
51 changes: 51 additions & 0 deletions src/background/actions/focusTabBySound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import browser from "webextension-polyfill";
import { notify } from "../utils/notify";
import { getNextTabByIndex } from "../utils/tabUtils";

let tabLastSounded: number | undefined;

export function setTabLastSounded(tabId: number) {
tabLastSounded = tabId;
}

export async function focusTabLastSounded() {
if (!tabLastSounded)
return notify("No tab has emitted sound since startup.", {
type: "warning",
});

const tab = await browser.tabs.get(tabLastSounded);
await browser.windows.update(tab.windowId!, { focused: true });
await browser.tabs.update(tabLastSounded, { active: true });
}

export async function focusNextTabWithSound() {
const tabsWithSound = await browser.tabs.query({
audible: true,
muted: false,
});

const nextTabWithSound = await getNextTabByIndex(tabsWithSound);
if (!nextTabWithSound) return;

await browser.windows.update(nextTabWithSound.windowId!, { focused: true });
await browser.tabs.update(nextTabWithSound.id, { active: true });
}

export async function focusNextMutedTab() {
const mutedTabs = await browser.tabs.query({ muted: true });
const nextMutedTab = await getNextTabByIndex(mutedTabs);
if (!nextMutedTab) return;

await browser.windows.update(nextMutedTab.windowId!, { focused: true });
await browser.tabs.update(nextMutedTab.id, { active: true });
}

export async function focusNextAudibleTab() {
const audibleTabs = await browser.tabs.query({ audible: true });
const nextAudibleTab = await getNextTabByIndex(audibleTabs);
if (!nextAudibleTab) return;

await browser.windows.update(nextAudibleTab.windowId!, { focused: true });
await browser.tabs.update(nextAudibleTab.id, { active: true });
}
67 changes: 67 additions & 0 deletions src/background/actions/muteTabs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import browser from "webextension-polyfill";
import { getCurrentTabId } from "../utils/getCurrentTab";
import { getTabIdForMarker } from "../misc/tabMarkers";
import { getNextTabByIndex } from "../utils/tabUtils";
import { notify } from "../utils/notify";

export async function muteTab(tabMarkers?: string[], mute = true) {
if (tabMarkers) {
const tabsToMute = await Promise.all(tabMarkers.map(getTabIdForMarker));

return Promise.all(
tabsToMute.map(async (tabId) =>
browser.tabs.update(tabId, { muted: mute })
)
);
}

const tabToMute = await getCurrentTabId();
return browser.tabs.update(tabToMute, { muted: mute });
}

export async function muteNextTabWithSound() {
const tabsWithSound = await browser.tabs.query({
audible: true,
muted: false,
});

const nextTabWithSound = await getNextTabByIndex(tabsWithSound);
if (!nextTabWithSound)
return notify("There are currently no tabs with sound", {
type: "warning",
});

await browser.tabs.update(nextTabWithSound.id, { muted: true });
}

export async function unmuteNextMutedTab() {
const mutedTabs = await browser.tabs.query({ muted: true });
const nextMutedTab = await getNextTabByIndex(mutedTabs);
if (!nextMutedTab)
return notify("There are currently no muted tabs", {
type: "warning",
});

await browser.tabs.update(nextMutedTab.id, { muted: false });
}

export async function muteAllTabsWithSound() {
const tabsWithSound = await browser.tabs.query({
audible: true,
muted: false,
});

await Promise.all(
tabsWithSound.map(async (tab) =>
browser.tabs.update(tab.id, { muted: true })
)
);
}

export async function unmuteAllMutedTabs() {
const mutedTabs = await browser.tabs.query({ muted: true });

await Promise.all(
mutedTabs.map(async (tab) => browser.tabs.update(tab.id, { muted: false }))
);
}
12 changes: 12 additions & 0 deletions src/background/commands/dispatchCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ const backgroundCommands = new Set<RangoAction["type"]>([
"focusOrCreateTabByUrl",
"focusTabByText",
"cycleTabsByText",
"focusNextTabWithSound",
"focusNextMutedTab",
"focusNextAudibleTab",
"focusTabLastSounded",
"muteCurrentTab",
"unmuteCurrentTab",
"muteTab",
"unmuteTab",
"muteNextTabWithSound",
"unmuteNextMutedTab",
"muteAllTabsWithSound",
"unmuteAllMutedTabs",
]);

export async function dispatchCommand(
Expand Down
61 changes: 61 additions & 0 deletions src/background/commands/runBackgroundCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ import { refreshTabMarkers } from "../misc/tabMarkers";
import { getCurrentTab } from "../utils/getCurrentTab";
import { notifySettingRemoved } from "../utils/notify";
import { closeTab } from "../actions/closeTab";
import {
focusNextAudibleTab,
focusNextMutedTab,
focusNextTabWithSound,
focusTabLastSounded,
} from "../actions/focusTabBySound";
import {
muteAllTabsWithSound,
muteNextTabWithSound,
muteTab,
unmuteAllMutedTabs,
unmuteNextMutedTab,
} from "../actions/muteTabs";

export async function runBackgroundCommand(
command: RangoAction
Expand Down Expand Up @@ -147,6 +160,54 @@ export async function runBackgroundCommand(
await focusPreviousTab();
break;

case "focusNextTabWithSound":
await focusNextTabWithSound();
break;

case "focusNextMutedTab":
await focusNextMutedTab();
break;

case "focusNextAudibleTab":
await focusNextAudibleTab();
break;

case "focusTabLastSounded":
await focusTabLastSounded();
break;

case "muteCurrentTab":
await muteTab();
break;

case "unmuteCurrentTab":
await muteTab(undefined, false);
break;

case "muteTab":
await muteTab(command.target);
break;

case "unmuteTab":
await muteTab(command.target, false);
break;

case "muteNextTabWithSound":
await muteNextTabWithSound();
break;

case "unmuteNextMutedTab":
await unmuteNextMutedTab();
break;

case "muteAllTabsWithSound":
await muteAllTabsWithSound();
break;

case "unmuteAllMutedTabs":
await unmuteAllMutedTabs();
break;

case "copyLocationProperty":
if (currentTab) {
return copyLocationProperty(currentTab, command.arg);
Expand Down
8 changes: 8 additions & 0 deletions src/background/setup/initBackgroundScript.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import browser from "webextension-polyfill";
import { retrieve, store } from "../../common/storage";
import { urls } from "../../common/urls";
import { setTabLastSounded } from "../actions/focusTabBySound";
import { watchNavigation } from "../hints/watchNavigation";
import { sendRequestToContent } from "../messaging/sendRequestToContent";
import { createContextMenus } from "../misc/createContextMenus";
Expand Down Expand Up @@ -117,3 +118,10 @@ browser.bookmarks?.onCreated.addListener(resetBookmarkTitle);
// the title of the bookmark will be changed again to the value of the input
// field of the popup window.
browser.bookmarks?.onChanged.addListener(resetBookmarkTitle);

browser.tabs.onUpdated.addListener(async (tabId, { audible }) => {
if (audible === true) {
const tab = await browser.tabs.get(tabId);
if (!tab.mutedInfo?.muted) setTabLastSounded(tabId);
}
});
21 changes: 21 additions & 0 deletions src/background/utils/tabUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import browser from "webextension-polyfill";
import { getCurrentTab } from "./getCurrentTab";

/**
* Given an array of tabs as a parameter, return the first tab in the array that
* has a greater index than the current tab. If no such tab exists in the
* current window cycle through all existing windows returning to the start of
* the current window if necessary.
*/
export async function getNextTabByIndex(tabs: browser.Tabs.Tab[]) {
const currentTab = await getCurrentTab();

return (
tabs.find(
(tab) =>
(tab.windowId === currentTab.windowId &&
tab.index > currentTab.index) ||
tab.windowId !== currentTab.windowId
) ?? tabs[0]
);
}
12 changes: 12 additions & 0 deletions src/typings/RangoAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ interface RangoActionWithoutTargetWithoutArg {
| "moveCurrentTabToNewWindow"
| "focusPreviousTab"
| "focusFirstInput"
| "focusNextTabWithSound"
| "focusNextMutedTab"
| "focusNextAudibleTab"
| "focusTabLastSounded"
| "muteCurrentTab"
| "unmuteCurrentTab"
| "muteNextTabWithSound"
| "unmuteNextMutedTab"
| "muteAllTabsWithSound"
| "unmuteAllMutedTabs"
| "unhoverAll"
| "copyCurrentTabMarkdownUrl"
| "getBareTitle"
Expand Down Expand Up @@ -103,6 +113,8 @@ interface RangoActionWithoutTargetWithOptionalNumberArg {
export interface RangoActionWithTargets {
type:
| "activateTab"
| "muteTab"
| "unmuteTab"
| "closeTab"
| "openInBackgroundTab"
| "clickElement"
Expand Down

0 comments on commit 2f31ecf

Please sign in to comment.