您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hide topics you're not interested in
// ==UserScript== // @name Rllmuk Topic Ignore List (Invision 4) // @description Hide topics you're not interested in // @namespace https://github.com/insin/greasemonkey/ // @version 13 // @match https://rllmukforum.com/index.php* // @match https://www.rllmukforum.com/index.php* // @grant GM.registerMenuCommand // ==/UserScript== /** * @typedef {{updateClassNames(): void}} Topic */ /** * @typedef {{id: string}} IgnoredItem */ const IGNORED_TOPICS_STORAGE = 'rit_ignoredTopics' const IGNORED_FORUMS_STORAGE = 'rit_ignoredForums' /** @type {Topic[]} */ let topics = [] /** @type {IgnoredItem[]} */ let ignoredTopics /** @type {string[]} */ let ignoredTopicIds /** @type {IgnoredItem[]} */ let ignoredForums /** @type {string[]} */ let ignoredForumIds /** @type {import("./types").Config} */ let config = { hideFluidSidebar: false, showIgnoredTopics: false, } function isFluidForumPage() { return ( location.href.includes('index.php?forumId=') || (location.href.endsWith('index.php') && document.querySelector('a.ipsButton_primary[href*="setMethod&method=fluid"]') != null) ) } function loadIgnoreConfig() { ignoredTopics = JSON.parse(localStorage[IGNORED_TOPICS_STORAGE] || '[]') ignoredTopicIds = ignoredTopics.map(topic => topic.id) ignoredForums = JSON.parse(localStorage[IGNORED_FORUMS_STORAGE] || '[]') ignoredForumIds = ignoredForums.map(forum => forum.id) } /** * @param {string} id * @param {Topic} topic */ function toggleIgnoreTopic(id, topic) { if (!ignoredTopicIds.includes(id)) { ignoredTopicIds.unshift(id) ignoredTopics.unshift({id}) } else { let index = ignoredTopicIds.indexOf(id) ignoredTopicIds.splice(index, 1) ignoredTopics.splice(index, 1) } localStorage[IGNORED_TOPICS_STORAGE] = JSON.stringify(ignoredTopics) topic.updateClassNames() } /** * @param {string} id */ function toggleIgnoreForum(id) { if (!ignoredForumIds.includes(id)) { ignoredForumIds.unshift(id) ignoredForums.unshift({id}) } else { let index = ignoredForumIds.indexOf(id) ignoredForumIds.splice(index, 1) ignoredForums.splice(index, 1) } localStorage[IGNORED_FORUMS_STORAGE] = JSON.stringify(ignoredForums) topics.forEach(topic => topic.updateClassNames()) } /** * @param {boolean} showIgnoredTopics */ function toggleShowIgnoredTopics(showIgnoredTopics) { config.showIgnoredTopics = showIgnoredTopics topics.forEach(topic => topic.updateClassNames()) } /** * @param {string} css */ function addStyle(css) { let $style = document.createElement('style') $style.appendChild(document.createTextNode(css)) document.querySelector('head').appendChild($style) } function UnreadContentPage() { const TOPIC_LINK_ID_RE = /index\.php\?\/topic\/(\d+)/ const FORUM_LINK_ID_RE = /index\.php\?(?:\/forum\/|forumId=)(\d+)/ /** @type {string} */ let view addStyle(` .rit_ignoreControl { visibility: hidden; } .rit_ignored { display: none; } .rit_ignored.rit_show { display: block; background-color: #fee; } .rit_ignored.rit_show::after { border-color: transparent #fee transparent transparent !important; } li.ipsStreamItem:hover .rit_ignoreControl { visibility: visible; } .rit_ignoreForumControl { opacity: 0.5; } .rit_ignoreForumControl:hover { opacity: 1; } .rit_ignoredForum .rit_ignoreTopicControl { display: none; } .rit_ignoredTopic .rit_ignoreForumControl { display: none; } .rit_ignoredTopic.rit_ignoredForum .rit_ignoreForumControl { display: inline; } `) function getView() { let $activeViewButton = document.querySelector('a.ipsButton_primary[data-action="switchView"]') return $activeViewButton ? $activeViewButton.textContent.trim() : null } /** * @param {HTMLElement} $topic * @returns {Topic} */ function Topic($topic) { let $topicLink = /** @type {HTMLAnchorElement} */ ($topic.querySelector('a[href*="index.php?/topic/"][data-linktype="link"]')) let $forumLink = /** @type {HTMLAnchorElement} */ ($topic.querySelector('a[href*="index.php?/forum/"], a[href*="index.php?forumId"]')) if (!$topicLink) { return null } let topicId = TOPIC_LINK_ID_RE.exec($topicLink.href)[1] let forumId = FORUM_LINK_ID_RE.exec($forumLink.href)[1] let api = { updateClassNames() { let isTopicIgnored = ignoredTopicIds.includes(topicId) let isForumIgnored = ignoredForumIds.includes(forumId) $topic.classList.toggle('rit_ignoredTopic', isTopicIgnored) $topic.classList.toggle('rit_ignoredForum', isForumIgnored) $topic.classList.toggle('rit_ignored', isTopicIgnored || isForumIgnored) $topic.classList.toggle('rit_show', config.showIgnoredTopics && (isTopicIgnored || isForumIgnored)) } } let $ignoreTopicContainer if (view == 'Condensed') { $ignoreTopicContainer = $topic.querySelector('ul.ipsStreamItem_stats') $ignoreTopicContainer.insertAdjacentHTML('beforeend', ` <li class="rit_ignoreControl rit_ignoreTopicControl"> <a style="cursor: pointer"><i class="fa fa-trash"></i></a> </li> `) } else { $ignoreTopicContainer = $topicLink.parentElement $ignoreTopicContainer.insertAdjacentHTML('beforeend', ` <a style="cursor: pointer"class="rit_ignoreControl rit_ignoreTopicControl"> <i class="fa fa-trash"></i> </a> `) } $ignoreTopicContainer.querySelector('i.fa-trash').addEventListener('click', () => { toggleIgnoreTopic(topicId, api) }) $forumLink.parentElement.insertAdjacentHTML('beforeend', ` <a style="cursor: pointer" class="rit_ignoreControl rit_ignoreForumControl"><i class="fa fa-trash"></i></a> `) $forumLink.parentElement.querySelector('i.fa-trash').addEventListener('click', () => { toggleIgnoreForum(forumId) }) if (config.topicLinksLatestPost && !$topicLink.href.endsWith('&do=getNewComment')) { $topicLink.href += '&do=getNewComment' } return api } /** * Add ignore controls to a topic and hide it if it's in the ignored list. * @param {HTMLElement} $topic */ function processTopic($topic) { let topic = Topic($topic) if (topic == null) { return } topics.push(topic) topic.updateClassNames() } /** * Process topics within a topic container and watch for a new topic container being added. * When you click "Load more activity", a new <div> is added to the end of the topic container. * @param {HTMLElement} $el */ function processTopicContainer($el) { Array.from($el.querySelectorAll(':scope > li.ipsStreamItem'), processTopic) new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (view != getView()) { processView() } else if (mutation.addedNodes[0] instanceof HTMLElement && mutation.addedNodes[0].tagName === 'DIV') { processTopicContainer(mutation.addedNodes[0]) } }) }).observe($el, {childList: true}) } /** * Reset handling of topics when the view changes between Condensed and Expanded. */ function processView() { topics = [] view = getView() processTopicContainer(document.querySelector('ol.ipsStream')) } processView() } function ForumPage() { let isFluid = isFluidForumPage() addStyle(` .rit_ignoreControl { display: table-cell; min-width: 24px; vertical-align: middle; visibility: hidden; } .rit_ignored { display: none; } body.rit_hideFluidSidebar #ipsLayout_sidebar { display: none; } .rit_ignored.rit_show { display: block; background-color: #fee !important; } @media screen and (max-width:979px) { .rit_ignoreControl { position: absolute; ${isFluid ? 'right' : 'left'}: 12px; ${isFluid ? 'top: 50%;' : 'bottom: 16px;'} } .rit_toggleFluidToolItem { display: none; } } li.ipsDataItem:hover .rit_ignoreControl { visibility: visible; } `) /** * @param {HTMLElement} $topic * @returns {Topic} */ function Topic($topic) { let topicId = $topic.dataset.rowid if (!topicId) { return null } let api = { updateClassNames() { let isTopicIgnored = ignoredTopicIds.includes(topicId) $topic.classList.toggle('rit_ignored', isTopicIgnored) $topic.classList.toggle('rit_show', config.showIgnoredTopics && isTopicIgnored) } } $topic.insertAdjacentHTML('beforeend', ` <div class="rit_ignoreControl ipsType_light ipsType_blendLinks"> <a style="cursor: pointer"><i class="fa fa-trash"></i></a> <div> `) $topic.querySelector('i.fa-trash').addEventListener('click', () => { toggleIgnoreTopic(topicId, api) }) return api } /** * Add ignore controls to a topic and hide it if it's in the ignored list. * @param {HTMLElement} $topic */ function processTopic($topic) { let topic = Topic($topic) if (topic == null) { return } topics.push(topic) topic.updateClassNames() } if (isFluid) { let $toolList = document.querySelector('.ipsPageHeader .ipsToolList') $toolList.insertAdjacentHTML('afterbegin', ` <li class="rit_toggleFluidToolItem"> <ul class="ipsButton_split"> <li> <a class="rit_toggleFluidButton ipsButton ipsButton_narrow ipsButton_medium" href="#toggleFluidSidebar"> <i class="fa fa-chevron-down"></i> </a> </li> </ul> </li> `) let $toggleFluidControl = /** @type {HTMLAnchorElement} */ ($toolList.querySelector('.rit_toggleFluidButton')) let $toggleFluidIcon = $toggleFluidControl.firstElementChild function applyHideFluidSidebarConfig() { document.body.classList.toggle('rit_hideFluidSidebar', config.hideFluidSidebar) $toggleFluidIcon.classList.toggle('fa-chevron-down', !config.hideFluidSidebar) $toggleFluidIcon.classList.toggle('fa-chevron-left', config.hideFluidSidebar) $toggleFluidControl.title = `${config.hideFluidSidebar ? 'Show' : 'Hide'} sidebar` } $toggleFluidControl.addEventListener('click', (e) => { e.preventDefault() config.hideFluidSidebar = !config.hideFluidSidebar applyHideFluidSidebarConfig() if (typeof GM != 'undefined') { localStorage.rit_config = JSON.stringify(config) } else { chrome.storage.local.set({hideFluidSidebar: config.hideFluidSidebar}) } }) applyHideFluidSidebarConfig() } // Initial list of topics Array.from(document.querySelectorAll('ol.cTopicList > li.ipsDataItem[data-rowid]'), processTopic) // Watch for topics being replaced when paging new MutationObserver(mutations => mutations.forEach(mutation => Array.from(mutation.addedNodes).filter(node => node.nodeType === Node.ELEMENT_NODE).map(processTopic) ) ).observe(document.querySelector('ol.cTopicList'), {childList: true}) } let page if (location.href.includes('index.php?/discover/unread')) { page = UnreadContentPage } else if (location.href.includes('index.php?/forum/') || isFluidForumPage()) { page = ForumPage } if (page) { if (typeof GM != 'undefined') { Object.assign(config, JSON.parse(localStorage.rit_config || '{}')) loadIgnoreConfig() page() GM.registerMenuCommand('Toggle Ignored Topic Display', () => { toggleShowIgnoredTopics(!config.showIgnoredTopics) localStorage.rit_config = JSON.stringify(config) }) } else { chrome.storage.local.get((storedConfig) => { Object.assign(config, storedConfig) loadIgnoreConfig() page() }) chrome.storage.onChanged.addListener((changes) => { if ('showIgnoredTopics' in changes) { toggleShowIgnoredTopics(changes['showIgnoredTopics'].newValue) } }) } }