Skip to content

Commit

Permalink
Add "Mute/Unmute this Tree" and "Mute/Unmute Descendants" context men…
Browse files Browse the repository at this point in the history
…u commands and shortcut commands #3400
  • Loading branch information
piroor committed Oct 30, 2023
1 parent bce5b75 commit 5b49648
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 6 deletions.
10 changes: 10 additions & 0 deletions webextensions/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,16 @@
"context_reloadDescendants_label": { "message": "R&eload Descendants" },
"context_reloadDescendants_label_multiselected": { "message": "R&eload Descendants of Selected tabs" },
"context_reloadDescendants_command": { "message": "Reload Descendants" },
"context_toggleMuteTree_label_mute": { "message": "&Mute this Tree" },
"context_toggleMuteTree_label_multiselected_mute": { "message": "&Mute Selected Trees" },
"context_toggleMuteTree_label_unmute": { "message": "Un&mute this Tree" },
"context_toggleMuteTree_label_multiselected_unmute": { "message": "Un&mute Selected Trees" },
"context_toggleMuteTree_command": { "message": "Mute/Unmute this Tree" },
"context_toggleMuteDescendants_label_mute": { "message": "Mu&te Descendants" },
"context_toggleMuteDescendants_label_multiselected_mute": { "message": "Mu&te Descendants of Selected tabs" },
"context_toggleMuteDescendants_label_unmute": { "message": "Unmu&te Descendants" },
"context_toggleMuteDescendants_label_multiselected_unmute": { "message": "Unmu&te Descendants of Selected tabs" },
"context_toggleMuteDescendants_command": { "message": "Mute/Unmute Descendants" },
"context_closeTree_label": { "message": "&Close this Tree" },
"context_closeTree_label_multiselected": { "message": "&Close Selected Trees" },
"context_closeTree_command": { "message": "Close this Tree" },
Expand Down
10 changes: 10 additions & 0 deletions webextensions/_locales/ja/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,16 @@

"context_reloadTree_label": { "message": "このツリーを再読み込み(&R)" },
"context_reloadTree_label_multiselected": { "message": "選択したツリーを再読み込み(&R)" },
"context_toggleMuteTree_label_mute": { "message": "このツリーをミュート(&M)" },
"context_toggleMuteTree_label_multiselected_mute": { "message": "選択したツリーをミュート(&M)" },
"context_toggleMuteTree_label_unmute": { "message": "このツリーのミュートを解除(&M)" },
"context_toggleMuteTree_label_multiselected_unmute": { "message": "選択したツリーのミュートを解除(&M)" },
"context_toggleMuteTree_command": { "message": "このツリーをミュート/ミュート解除" },
"context_toggleMuteDescendants_label_mute": { "message": "このタブの配下のタブをすべてミュート(&T)" },
"context_toggleMuteDescendants_label_multiselected_mute": { "message": "選択したタブの配下のタブをすべてミュート(&T)" },
"context_toggleMuteDescendants_label_unmute": { "message": "このタブの配下のタブをすべてミュート解除(&T)" },
"context_toggleMuteDescendants_label_multiselected_unmute": { "message": "選択したタブの配下のタブをすべてミュート解除(&T)" },
"context_toggleMuteDescendants_command": { "message": "このタブ派以下のタブをすべてミュート/ミュート解除" },
"context_reloadTree_command": { "message": "このツリーを再読み込み" },
"context_reloadDescendants_label": { "message": "このタブの配下のタブをすべて再読み込み(&E)" },
"context_reloadDescendants_label_multiselected": { "message": "選択したタブの配下のタブをすべて再読み込み(&E)" },
Expand Down
20 changes: 20 additions & 0 deletions webextensions/background/browser-action-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,16 @@ const mItems = [
key: 'context_topLevel_reloadDescendants',
type: 'checkbox'
},
{
title: indent() + browser.i18n.getMessage('context_toggleMuteTree_command'),
key: 'context_topLevel_toggleMuteTree',
type: 'checkbox'
},
{
title: indent() + browser.i18n.getMessage('context_toggleMuteDescendants_command'),
key: 'context_topLevel_toggleMuteDescendants',
type: 'checkbox'
},
{
title: indent() + browser.i18n.getMessage('context_closeTree_command'),
key: 'context_topLevel_closeTree',
Expand Down Expand Up @@ -268,6 +278,16 @@ const mItems = [
key: 'context_reloadDescendants',
type: 'checkbox'
},
{
title: indent() + browser.i18n.getMessage('context_toggleMuteTree_command'),
key: 'context_toggleMuteTree',
type: 'checkbox'
},
{
title: indent() + browser.i18n.getMessage('context_toggleMuteDescendants_command'),
key: 'context_toggleMuteDescendants',
type: 'checkbox'
},
{
title: indent() + browser.i18n.getMessage('context_closeTree_command'),
key: 'context_closeTree',
Expand Down
69 changes: 69 additions & 0 deletions webextensions/background/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,75 @@ export function reloadDescendants(rootTabs) {
}
}

function isUnmuted(tab) {
return !tab.mutedInfo || !tab.mutedInfo.muted;
}

export function toggleMuteTree(tabs) {
const tabsToUpdate = [];
let shouldMute = false;
for (const tab of uniqTabsAndDescendantsSet(tabs)) {
if (!shouldMute && isUnmuted(tab))
shouldMute = true;
tabsToUpdate.push(tab);
}
for (const tab of tabsToUpdate) {
if (shouldMute != isUnmuted(tab))
continue;
browser.tabs.update(tab.id, { muted: shouldMute })
.catch(ApiTabs.createErrorHandler(ApiTabs.handleMissingTabError));
}
}

export function toggleMuteDescendants(rootTabs) {
const rootTabsSet = new Set(rootTabs);
const tabsToUpdate = [];
let shouldMute = false;
for (const tab of uniqTabsAndDescendantsSet(rootTabs)) {
if (rootTabsSet.has(tab))
continue;
if (!shouldMute && isUnmuted(tab))
shouldMute = true;
tabsToUpdate.push(tab);
}
for (const tab of tabsToUpdate) {
if (shouldMute != isUnmuted(tab))
continue;
browser.tabs.update(tab.id, { muted: shouldMute })
.catch(ApiTabs.createErrorHandler(ApiTabs.handleMissingTabError));
}
}

export function getUnmutedState(rootTabs) {
let hasUnmutedTab = false;
let hasUnmutedDescendant = false;
const rootTabsSet = new Set(rootTabs);
for (const tab of uniqTabsAndDescendantsSet(rootTabs)) {
if (!isUnmuted(tab))
continue;
hasUnmutedTab = true;
if (!rootTabsSet.has(tab))
hasUnmutedDescendant = true;
if (hasUnmutedTab && hasUnmutedDescendant)
break;
}
return { hasUnmutedTab, hasUnmutedDescendant };
}

export function getMenuItemTitle(item, { multiselected, hasUnmutedTab, hasUnmutedDescendant } = {}) {
const muteTreeSuffix = hasUnmutedTab ? 'Mute' : 'Unmute';
const muteDescendantSuffix = hasUnmutedDescendant ? 'Mute' : 'Unmute';
return multiselected && (
item[`titleMultiselected${muteTreeSuffix}Tree`] ||
item[`titleMultiselected${muteDescendantSuffix}Descendant`] ||
item.titleMultiselected
) || (
item[`title${muteTreeSuffix}Tree`] ||
item[`title${muteDescendantSuffix}Descendant`] ||
item.title
);
}

export async function closeTree(tabs) {
tabs = uniqTabsAndDescendantsSet(tabs);
const windowId = tabs[0].windowId;
Expand Down
38 changes: 32 additions & 6 deletions webextensions/background/context-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ const mTabItemsById = {
title: browser.i18n.getMessage('context_reloadDescendants_label'),
titleMultiselected: browser.i18n.getMessage('context_reloadDescendants_label_multiselected')
},
'toggleMuteTree': {
titleMuteTree: browser.i18n.getMessage('context_toggleMuteTree_label_mute'),
titleMultiselectedMuteTree: browser.i18n.getMessage('context_toggleMuteTree_label_multiselected_mute'),
titleUnmuteTree: browser.i18n.getMessage('context_toggleMuteTree_label_unmute'),
titleMultiselectedUnmuteTree: browser.i18n.getMessage('context_toggleMuteTree_label_multiselected_unmute')
},
'toggleMuteDescendants': {
titleMuteDescendant: browser.i18n.getMessage('context_toggleMuteDescendants_label_mute'),
titleMultiselectedMuteDescendant: browser.i18n.getMessage('context_toggleMuteDescendants_label_multiselected_mute'),
titleUnmuteDescendant: browser.i18n.getMessage('context_toggleMuteDescendants_label_unmute'),
titleMultiselectedUnmuteDescendant: browser.i18n.getMessage('context_toggleMuteDescendants_label_multiselected_unmute')
},
'separatorAfterReload': {
type: 'separator'
},
Expand Down Expand Up @@ -317,7 +329,7 @@ function updateItem(id, params) {
}, browser.runtime);
}

function updateItemsVisibility(items, { forceVisible = null, multiselected = false } = {}) {
function updateItemsVisibility(items, { forceVisible = null, multiselected = false, hasUnmutedTab = false, hasUnmutedDescendant = false } = {}) {
let updated = false;
let visibleItemsCount = 0;
let visibleNormalItemsCount = 0;
Expand All @@ -334,7 +346,7 @@ function updateItemsVisibility(items, { forceVisible = null, multiselected = fal
lastSeparator = item;
}
else {
const title = multiselected && item.titleMultiselected || item.title;
const title = Commands.getMenuItemTitle(item, { multiselected, hasUnmutedTab, hasUnmutedDescendant });
let visible = !(item.configKey in configs) || configs[item.configKey];
if (forceVisible !== null)
visible = forceVisible;
Expand Down Expand Up @@ -374,10 +386,10 @@ function updateItemsVisibility(items, { forceVisible = null, multiselected = fal
return { updated, visibleItemsCount };
}

async function updateItems({ multiselected } = {}) {
async function updateItems({ multiselected, hasUnmutedTab, hasUnmutedDescendant } = {}) {
let updated = false;

const groupedItems = updateItemsVisibility(mGroupedTabItems, { multiselected });
const groupedItems = updateItemsVisibility(mGroupedTabItems, { multiselected, hasUnmutedTab, hasUnmutedDescendant });
if (groupedItems.updated)
updated = true;

Expand All @@ -395,7 +407,7 @@ async function updateItems({ multiselected } = {}) {
updated = true;
}

const topLevelItems = updateItemsVisibility(mTabItems, { forceVisible: grouped ? false : null, multiselected });
const topLevelItems = updateItemsVisibility(mTabItems, { forceVisible: grouped ? false : null, multiselected, hasUnmutedTab, hasUnmutedDescendant });
if (topLevelItems.updated)
updated = true;

Expand Down Expand Up @@ -446,6 +458,19 @@ function onTabItemClick(info, tab) {
Commands.reloadDescendants(contextTabs);
break;

case 'toggleMuteTree':
if (inverted)
Commands.toggleMuteDescendants(contextTabs);
else
Commands.toggleMuteTree(contextTabs);
break;
case 'toggleMuteDescendants':
if (inverted)
Commands.toggleMuteTree(contextTabs);
else
Commands.toggleMuteDescendants(contextTabs);
break;

case 'closeTree':
if (inverted)
Commands.closeDescendants(contextTabs);
Expand Down Expand Up @@ -553,8 +578,9 @@ async function onTabContextMenuShown(info, tab) {
const hasChild = contextTabs.length > 0 && contextTabs.some(tab => tab.$TST.hasChild);
const subtreeCollapsed = contextTabs.length > 0 && contextTabs.some(tab => tab.$TST.subtreeCollapsed);
const grouped = contextTabs.length > 0 && contextTabs.some(tab => tab.$TST.isGroupTab);
const { hasUnmutedTab, hasUnmutedDescendant } = Commands.getUnmutedState(contextTabs);

let updated = await updateItems({ multiselected });
let updated = await updateItems({ multiselected, hasUnmutedTab, hasUnmutedDescendant });
if (mLastContextTabId != contextTabId)
return; // Skip further operations if the menu was already reopened on a different context tab.

Expand Down
6 changes: 6 additions & 0 deletions webextensions/background/handle-misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ async function onShortcutCommand(command) {
case 'reloadDescendants':
Commands.reloadDescendants(selectedTabs);
return;
case 'toggleMuteTree':
Commands.toggleMuteTree(selectedTabs);
return;
case 'toggleMuteDescendants':
Commands.toggleMuteDescendants(selectedTabs);
return;
case 'closeTree':
Commands.closeTree(selectedTabs);
return;
Expand Down
29 changes: 29 additions & 0 deletions webextensions/background/tab-context-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ const mItemsById = {
title: browser.i18n.getMessage('tabContextMenu_unmute_label'),
titleMultiselected: browser.i18n.getMessage('tabContextMenu_unmute_label_multiselected')
},
'context_topLevel_toggleMuteTree': {
titleMuteTree: browser.i18n.getMessage('context_toggleMuteTree_label_mute'),
titleMultiselectedMuteTree: browser.i18n.getMessage('context_toggleMuteTree_label_multiselected_mute'),
titleUnmuteTree: browser.i18n.getMessage('context_toggleMuteTree_label_unmute'),
titleMultiselectedUnmuteTree: browser.i18n.getMessage('context_toggleMuteTree_label_multiselected_unmute')
},
'context_topLevel_toggleMuteDescendants': {
titleMuteDescendant: browser.i18n.getMessage('context_toggleMuteDescendants_label_mute'),
titleMultiselectedMuteDescendant: browser.i18n.getMessage('context_toggleMuteDescendants_label_multiselected_mute'),
titleUnmuteDescendant: browser.i18n.getMessage('context_toggleMuteDescendants_label_unmute'),
titleMultiselectedUnmuteDescendant: browser.i18n.getMessage('context_toggleMuteDescendants_label_multiselected_unmute')
},
'context_pinTab': {
title: browser.i18n.getMessage('tabContextMenu_pin_label'),
titleMultiselected: browser.i18n.getMessage('tabContextMenu_pin_label_multiselected')
Expand Down Expand Up @@ -552,6 +564,7 @@ async function onShown(info, contextTab) {
[contextTab] :
[];
const hasChild = contextTab && contextTabs.some(tab => tab.$TST.hasChild);
const { hasUnmutedTab, hasUnmutedDescendant } = Commands.getUnmutedState(contextTabs);

if (mOverriddenContext)
return onOverriddenMenuShown(info, contextTab, windowId);
Expand Down Expand Up @@ -593,6 +606,22 @@ async function onShown(info, contextTab) {
visible: emulate && contextTab && contextTab.mutedInfo && contextTab.mutedInfo.muted,
multiselected
}) && modifiedItemsCount++;
updateItem('context_topLevel_toggleMuteTree', {
visible: emulate && contextTab && configs.context_topLevel_toggleMuteTree,
enabled: hasChild,
multiselected,
title: Commands.getMenuItemTitle(mItemsById.context_topLevel_toggleMuteTree, { multiselected, hasUnmutedTab, hasUnmutedDescendant }),
hasUnmutedTab,
hasUnmutedDescendant,
}) && modifiedItemsCount++;
updateItem('context_topLevel_toggleMuteDescendants', {
visible: emulate && contextTab && configs.context_topLevel_toggleMuteDescendants,
enabled: hasChild,
multiselected,
title: Commands.getMenuItemTitle(mItemsById.context_topLevel_toggleMuteDescendants, { multiselected, hasUnmutedTab, hasUnmutedDescendant }),
hasUnmutedTab,
hasUnmutedDescendant,
}) && modifiedItemsCount++;
updateItem('context_pinTab', {
visible: emulate && contextTab && !contextTab.pinned,
multiselected
Expand Down
4 changes: 4 additions & 0 deletions webextensions/common/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ export const configs = new Configs({

context_reloadTree: true,
context_reloadDescendants: false,
context_toggleMuteTree: true,
context_toggleMuteDescendants: false,
context_closeTree: true,
context_closeDescendants: false,
context_closeOthers: false,
Expand All @@ -149,6 +151,8 @@ export const configs = new Configs({

context_topLevel_reloadTree: false,
context_topLevel_reloadDescendants: false,
context_topLevel_toggleMuteTree: false,
context_topLevel_toggleMuteDescendants: false,
context_topLevel_closeTree: false,
context_topLevel_closeDescendants: false,
context_topLevel_closeOthers: false,
Expand Down
6 changes: 6 additions & 0 deletions webextensions/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@
"reloadDescendants": {
"description": "__MSG_context_reloadDescendants_command__"
},
"toggleMuteTree": {
"description": "__MSG_context_toggleMuteTree_command__"
},
"toggleMuteDescendants": {
"description": "__MSG_context_toggleMuteDescendants_command__"
},
"closeTree": {
"description": "__MSG_context_closeTree_command__"
},
Expand Down
16 changes: 16 additions & 0 deletions webextensions/options/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,22 @@ <h1>__MSG_config_context_caption__</h1>
<th><label for="context_reloadDescendants">__MSG_context_reloadDescendants_label__</label></th>
<th><label for="context_reloadDescendants">__MSG_context_reloadTree_label__</label></th>
</tr>
<tr>
<td><label><input id="context_topLevel_toggleMuteTree"
type="checkbox"></label></td>
<td><label><input id="context_toggleMuteTree"
type="checkbox"></label></td>
<th><label for="context_toggleMuteTree">__MSG_context_toggleMuteTree_command__</label></th>
<th><label for="context_toggleMuteTree">__MSG_context_toggleMuteDescendants_command__</label></th>
</tr>
<tr>
<td><label><input id="context_topLevel_toggleMuteDescendants"
type="checkbox"></label></td>
<td><label><input id="context_toggleMuteDescendants"
type="checkbox"></label></td>
<th><label for="context_toggleMuteDescendants">__MSG_context_toggleMuteDescendants_command__</label></th>
<th><label for="context_toggleMuteDescendants">__MSG_context_toggleMuteTree_command__</label></th>
</tr>
<tr>
<td><label><input id="context_topLevel_closeTree"
type="checkbox"></label></td>
Expand Down

0 comments on commit 5b49648

Please sign in to comment.