V2EX Ignore

在 V2EX 的每個帖子標題旁添加一個“忽略”按鈕,單擊即可忽略該主題。

目前為 2023-07-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name         V2EX Ignore
// @license GNU GPLv3
// @namespace    https://www.example.com/
// @version      1.9
// @description  Adds an "Ignore" button next to each post's title on V2EX that allows you to ignore the topic with a single click.
// @description:zh-CN 在 V2EX 的每个帖子标题旁添加一个“忽略”按钮,单击即可忽略该主题。
// @description:zh-TW 在 V2EX 的每個帖子標題旁添加一個“忽略”按鈕,單擊即可忽略該主題。
// @description:ja 在 V2EX の各投稿タイトルの横に、「無視する」ボタンを追加し、クリックするだけでそのトピックを無視できます。
// @author       Arryboom
// @match        https://*.v2ex.com/*
// @match        https://v2ex.com/*
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';
    const debug = true;
    const debugLog = debug ? console.log.bind(console) : function() {};

    debugLog("Ignore plugin running");

    // Function to ignore a topic
    function ignoreTopic(event) {
        event.preventDefault();
        const topicLink = event.target.parentNode.querySelector('.item_title a');
        if (topicLink) {
            const topicUrl = topicLink.href;
            const topicId = getTopicIdFromUrl(topicUrl);
            if (topicId) {
                getOnceValue().then(onceValue => {
                    const ignoreUrl = `https://${window.location.hostname}/ignore/topic/${topicId}?once=${onceValue}`;
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: ignoreUrl,
                        onload: function(response) {
                            debugLog(`Topic ${topicId} ignored.`);
                            const post = event.target.closest('.cell.item');
                            if (post) {
                                post.remove();
                                debugLog(`Post ${topicId} removed.`);
                                showToast(`Topic ${topicId} ignored.`, function() {
                                    window.location.reload();
                                });
                            }
                        },
                        onerror: function(error) {
                            debugLog(`Error ignoring topic ${topicId}: ${error}`);
                        }
                    });
                }).catch(error => {
                    debugLog(`Error fetching once value: ${error}`);
                });
            }
        }
    }

    // Function to extract the topic ID from a topic URL
    function getTopicIdFromUrl(topicUrl) {
        const match = topicUrl.match(/\/t\/(\d+)/);
        if (match) {
            return match[1];
        }
        return null;
    }

    // Function to fetch the current once value from the V2EX server
    function getOnceValue() {
        return new Promise((resolve, reject) => {
            const tops = document.querySelectorAll('a.top');
            const top = Array.from(tops).find(a => a.getAttribute('onclick')?.includes('once='));
            if (top) {
                const onclick = top.getAttribute('onclick');
                const match = onclick.match(/\?once=(\d+)/);
                if (match) {
                    const onceValue = match[1].replace(/\D/g, '');
                    debugLog(`Current once value is ${onceValue}`);
                    resolve(onceValue);
                    return;
                }
            }
            const error = new Error('Not logged in.');
            error.isAuthenticationError = true;
            reject(error);
        });
    }

    // Function to display a toast message
    function showToast(message, callback) {
        const toast = document.createElement('div');
        toast.textContent = message;
        toast.style.position = 'fixed';
        toast.style.bottom = '20px';
        toast.style.right = '20px';
        toast.style.padding = '10px';
        toast.style.backgroundColor = '#333';
        toast.style.color = '#fff';
        toast.style.borderRadius = '5px';
        document.body.appendChild(toast);
        setTimeout(() => {
            toast.remove();
            if (callback) {
                callback();
            }
        }, 300);
    }

    // Find all post titles on the page and add an "Ignore" button next to each one
    const postTitles = document.querySelectorAll('.cell.item .item_title');
    postTitles.forEach(postTitle => {
        const ignoreButton = document.createElement('a');
        ignoreButton.textContent = 'Ignore';
        ignoreButton.href = '#';
        ignoreButton.classList.add('tag');
        ignoreButton.addEventListener('click', ignoreTopic);
        postTitle.parentNode.insertBefore(ignoreButton, postTitle.nextSibling);
    });
})();