diff --git a/content.js b/content.js
index e8a566f..d6164ba 100644
--- a/content.js
+++ b/content.js
@@ -95,6 +95,7 @@ const locales = {
STREAMED_TITLE: 'views Streamed',
TELL_US_WHY: 'Tell us why',
THANKS: 'Thanks',
+ UNHIDE_CHANNEL: 'Unhide channel',
},
'ja-JP': {
CLIP: 'クリップ',
@@ -110,6 +111,7 @@ const locales = {
SHORTS: 'ショート',
STREAMED_TITLE: '前 に配信済み',
TELL_US_WHY: '理由を教えてください',
+ UNHIDE_CHANNEL: 'チャンネルの再表示',
}
}
@@ -135,6 +137,7 @@ const Classes = {
const Svgs = {
DELETE: '',
+ RESTORE: '',
}
//#region State
@@ -255,6 +258,13 @@ function getElement(selector, {
})
}
+/** @param {import("./types").Channel} channel */
+function isChannelHidden(channel) {
+ return config.hiddenChannels.some((hiddenChannel) =>
+ channel.url && hiddenChannel.url ? channel.url == hiddenChannel.url : hiddenChannel.name == channel.name
+ )
+}
+
let logObserverDisconnects = true
/**
@@ -1201,6 +1211,7 @@ function handleCurrentUrl() {
}
}
+/** @param {HTMLElement} $menu */
function addDownloadTranscriptToDesktopMenu($menu) {
if (!isVideoPage()) return
@@ -1225,7 +1236,7 @@ function addDownloadTranscriptToDesktopMenu($menu) {
document.querySelector('#content')?.click()
}
$item.addEventListener('click', download)
- $item.addEventListener('keydown', (e) => {
+ $item.addEventListener('keydown', /** @param {KeyboardEvent} e */ (e) => {
if (e.key == ' ' || e.key == 'Enter') {
e.preventDefault()
download()
@@ -1233,24 +1244,103 @@ function addDownloadTranscriptToDesktopMenu($menu) {
})
}
-function handleDesktopChannelMenu($menu) {
+/** @param {HTMLElement} $menu */
+function handleDesktopWatchChannelMenu($menu) {
if (!isVideoPage()) return
let $channelMenuRenderer = $lastClickedElement.closest('ytd-menu-renderer.ytd-watch-metadata')
if (!$channelMenuRenderer) return
- let $menuItems = /** @type {NodeListOf} */ ($menu.querySelectorAll('ytd-menu-service-item-renderer'))
- let testLabels = new Set([getString('SHARE'), getString('THANKS'), getString('CLIP')])
- for (let $menuItem of $menuItems) {
- if (testLabels.has($menuItem.querySelector('yt-formatted-string')?.textContent)) {
- log('tagging Share/Thanks/Clip menu item')
- $menuItem.classList.add(Classes.HIDE_SHARE_THANKS_CLIP)
+ if (config.hideShareThanksClip) {
+ let $menuItems = /** @type {NodeListOf} */ ($menu.querySelectorAll('ytd-menu-service-item-renderer'))
+ let testLabels = new Set([getString('SHARE'), getString('THANKS'), getString('CLIP')])
+ for (let $menuItem of $menuItems) {
+ if (testLabels.has($menuItem.querySelector('yt-formatted-string')?.textContent)) {
+ log('tagging Share/Thanks/Clip menu item')
+ $menuItem.classList.add(Classes.HIDE_SHARE_THANKS_CLIP)
+ }
+ }
+ }
+
+ if (config.hideChannels) {
+ let $channelLink = /** @type {HTMLAnchorElement} */ (document.querySelector('#channel-name a'))
+ if (!$channelLink) {
+ warn('channel link not found in video page')
+ return
+ }
+
+ let channel = {
+ name: $channelLink.textContent,
+ url: $channelLink.pathname,
+ }
+ lastClickedChannel = channel
+
+ let $item = $menu.querySelector('#cpfyt-hide-channel-menu-item')
+
+ function configureMenuItem(channel) {
+ let hidden = isChannelHidden(channel)
+ $item.querySelector('.cpfyt-menu-icon').innerHTML = hidden ? Svgs.RESTORE : Svgs.DELETE
+ $item.querySelector('.cpfyt-menu-text').textContent = getString(hidden ? 'UNHIDE_CHANNEL' : 'HIDE_CHANNEL')
+ }
+
+ // The same menu can be reused, so we reconfigure it if it exists. If the
+ // menu item is reused, we're just changing [lastClickedChannel], which is
+ // why [toggleHideChannel] uses it.
+ if (!$item) {
+ let hidden = isChannelHidden(channel)
+
+ function toggleHideChannel() {
+ let hidden = isChannelHidden(lastClickedChannel)
+ if (hidden) {
+ log('unhiding channel', lastClickedChannel)
+ config.hiddenChannels = config.hiddenChannels.filter((hiddenChannel) =>
+ hiddenChannel.url ? lastClickedChannel.url != hiddenChannel.url : hiddenChannel.name != lastClickedChannel.name
+ )
+ } else {
+ log('hiding channel', lastClickedChannel)
+ config.hiddenChannels.unshift(lastClickedChannel)
+ }
+ configureMenuItem(lastClickedChannel)
+ storeConfigChanges({hiddenChannels: config.hiddenChannels})
+ configureCss()
+ handleCurrentUrl()
+ // Dismiss the menu
+ let $popupContainer = /** @type {HTMLElement} */ ($menu.closest('ytd-popup-container'))
+ $popupContainer.click()
+ // XXX Menu isn't dismissing on iPad Safari
+ if ($menu.style.display != 'none') {
+ $menu.style.display = 'none'
+ $menu.setAttribute('aria-hidden', 'true')
+ }
+ }
+
+ let $menuItems = $menu.querySelector('#items')
+ $menuItems.insertAdjacentHTML('beforeend', `
+
+ `.trim())
+ $item = $menuItems.lastElementChild
+ $item.addEventListener('click', toggleHideChannel)
+ $item.addEventListener('keydown', /** @param {KeyboardEvent} e */ (e) => {
+ if (e.key == ' ' || e.key == 'Enter') {
+ e.preventDefault()
+ toggleHideChannel()
+ }
+ })
+ } else {
+ configureMenuItem(channel)
}
}
}
/** @param {HTMLElement} $menu */
-function addHideChannelToDesktopMenu($menu) {
+function addHideChannelToDesktopVideoMenu($menu) {
let videoContainerElement
if (isSearchPage()) {
videoContainerElement = 'ytd-video-renderer'
@@ -1272,7 +1362,7 @@ function addHideChannelToDesktopMenu($menu) {
if (!channel) return
lastClickedChannel = channel
- if ($menu.querySelector('.cpfyt-menu-item')) return
+ if ($menu.querySelector('#cpfyt-hide-channel-menu-item')) return
let $menuItems = $menu.querySelector('#items')
$menuItems.insertAdjacentHTML('beforeend', `
@@ -1310,10 +1400,8 @@ function addHideChannelToDesktopMenu($menu) {
})
}
-/**
- * @param {HTMLElement} $menu
- */
-async function addHideChannelToMobileMenu($menu) {
+/** @param {HTMLElement} $menu */
+async function addHideChannelToMobileVideoMenu($menu) {
if (!(isHomePage() || isSearchPage() || isVideoPage())) return
/** @type {HTMLElement} */
@@ -1532,13 +1620,13 @@ function onDesktopMenuAppeared($menu) {
addDownloadTranscriptToDesktopMenu($menu)
}
if (config.hideChannels) {
- addHideChannelToDesktopMenu($menu)
+ addHideChannelToDesktopVideoMenu($menu)
}
if (config.hideHiddenVideos) {
observeVideoHiddenState()
}
- if (config.hideShareThanksClip) {
- handleDesktopChannelMenu($menu)
+ if (config.hideChannels || config.hideShareThanksClip) {
+ handleDesktopWatchChannelMenu($menu)
}
}
@@ -2176,7 +2264,7 @@ function onMobileMenuAppeared($menu) {
}
if (config.hideChannels) {
- addHideChannelToMobileMenu($menu)
+ addHideChannelToMobileVideoMenu($menu)
}
if (config.hideHiddenVideos) {
observeVideoHiddenState()
@@ -2231,9 +2319,7 @@ function manuallyHideVideo($video) {
let channel = getChannelDetailsFromVideo($video)
let hide = false
if (channel) {
- hide = config.hiddenChannels.some((hiddenChannel) =>
- channel.url && hiddenChannel.url ? channel.url == hiddenChannel.url : hiddenChannel.name == channel.name
- )
+ hide = isChannelHidden(channel)
}
$video.classList.toggle(Classes.HIDE_CHANNEL, hide)
}
@@ -2596,22 +2682,24 @@ function onConfigChange(storageChanges) {
.filter(([key]) => config.hasOwnProperty(key) && key != 'version')
.map(([key, {newValue}]) => [key, newValue])
)
- if (Object.keys(configChanges).length > 0) {
- if ('debug' in configChanges) {
- log('disabling debug mode')
- debug = configChanges.debug
- log('enabled debug mode')
- return
- }
- if ('debugManualHiding' in configChanges) {
- debugManualHiding = configChanges.debugManualHiding
- log(`${debugManualHiding ? 'en' : 'dis'}abled debugging manual hiding`)
- configureCss()
- return
- }
- Object.assign(config, configChanges)
- configChanged(configChanges)
+ if (Object.keys(configChanges).length == 0) return
+
+ if ('debug' in configChanges) {
+ log('disabling debug mode')
+ debug = configChanges.debug
+ log('enabled debug mode')
+ return
+ }
+
+ if ('debugManualHiding' in configChanges) {
+ debugManualHiding = configChanges.debugManualHiding
+ log(`${debugManualHiding ? 'en' : 'dis'}abled debugging manual hiding`)
+ configureCss()
+ return
}
+
+ Object.assign(config, configChanges)
+ configChanged(configChanges)
}
/** @param {Partial} configChanges */
diff --git a/types.d.ts b/types.d.ts
index 400b3cf..dfd6bf8 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -33,6 +33,7 @@ export type LocaleKey =
| 'STREAMED_TITLE'
| 'TELL_US_WHY'
| 'THANKS'
+ | 'UNHIDE_CHANNEL'
export type OptionsConfig = EmbedConfig & SiteConfig