Multi Forum Read Marker (Soutong + TT1069)

点击帖子链接时立刻标记为已读,支持搜同和TT1069,解决快速跳转丢记录问题。

Verzia zo dňa 08.06.2025. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         Multi Forum Read Marker (Soutong + TT1069)
// @namespace    https://felixchristian.dev/userscripts/multi-forum-read-marker
// @version      1.4.0
// @description  点击帖子链接时立刻标记为已读,支持搜同和TT1069,解决快速跳转丢记录问题。
// @author       FelixChristian
// @license      MIT
// @match        https://soutong.men/forum.php?mod=forumdisplay&fid=*
// @match        https://soutong.men/forum.php?mod=viewthread&tid=*
// @match        https://www.tt1069.com/bbs/thread-*-*-*.html
// @match        https://www.tt1069.com/bbs/forum-*-*.html
// @match        https://www.tt1069.com/bbs/forum.php?mod=forumdisplay&fid=*
// @match        https://www.tt1069.com/bbs/forum.php?mod=forumdisplay&fid=*&*
// @match        https://www.tt1069.com/bbs/forum.php?mod=viewthread&tid=*
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

(function () {
    'use strict';

    const STORAGE_KEY = 'visitedTids';
    let visitedTids = {};

    // 初始化访问记录,合并localStorage和GM存储
    function loadVisited() {
        try {
            visitedTids = GM_getValue(STORAGE_KEY, {});
            const localData = localStorage.getItem(STORAGE_KEY + '_temp');
            if (localData) {
                const localObj = JSON.parse(localData);
                visitedTids = {...visitedTids, ...localObj};
            }
        } catch (e) {
            visitedTids = {};
        }
    }
    loadVisited();

    function saveVisited() {
        try {
            GM_setValue(STORAGE_KEY, visitedTids);
            localStorage.setItem(STORAGE_KEY + '_temp', JSON.stringify(visitedTids));
        } catch (e) {}
    }

    function getTidFromUrl(url) {
        try {
            const u = new URL(url, location.origin);
            let tid = u.searchParams.get('tid');
            if (!tid) {
                const match = url.match(/thread-(\d+)-/);
                tid = match ? match[1] : null;
            }
            return tid;
        } catch (e) {
            return null;
        }
    }

    // 给帖子链接加已读标记
    function markReadThreads() {
        const threadLinks = document.querySelectorAll('a.s.xst');
        threadLinks.forEach(link => {
            if (link.dataset.markedVisited) return; // 已标记跳过
            const tid = getTidFromUrl(link.href);
            if (tid && visitedTids[tid]) {
                const tag = document.createElement('span');
                tag.textContent = '[已读] ';
                tag.style.color = 'red';
                tag.style.fontWeight = 'bold';
                tag.style.marginRight = '4px';
                link.insertBefore(tag, link.firstChild);
                link.dataset.markedVisited = 'true';
            }
        });
    }

    // 点击帖子链接时立即标记并保存
    function attachClickListeners() {
        const threadLinks = document.querySelectorAll('a.s.xst');
        threadLinks.forEach(link => {
            if (link.dataset.clickListenerAdded) return;
            link.addEventListener('click', () => {
                const tid = getTidFromUrl(link.href);
                if (!tid) return;
                if (!visitedTids[tid]) {
                    visitedTids[tid] = Date.now();
                    // 立即存储
                    saveVisited();
                    // 立刻显示标记
                    if (!link.dataset.markedVisited) {
                        const tag = document.createElement('span');
                        tag.textContent = '[已读] ';
                        tag.style.color = 'red';
                        tag.style.fontWeight = 'bold';
                        tag.style.marginRight = '4px';
                        link.insertBefore(tag, link.firstChild);
                        link.dataset.markedVisited = 'true';
                    }
                }
            });
            link.dataset.clickListenerAdded = 'true';
        });
    }

    // 页面列表页加载时标记和绑定点击事件
    if (location.href.includes('mod=forumdisplay') || /forum-\d+-\d+\.html/.test(location.pathname)) {
        window.addEventListener('load', () => {
            markReadThreads();
            attachClickListeners();
        });
        const observer = new MutationObserver(() => {
            markReadThreads();
            attachClickListeners();
        });
        observer.observe(document.body, {childList: true, subtree: true});
    }

    // 帖子详情页访问时也保存记录,防止跳转直接访问帖子时丢记录
    if (location.href.includes('mod=viewthread') || /thread-\d+-/.test(location.pathname)) {
        const tid = getTidFromUrl(location.href);
        if (tid && !visitedTids[tid]) {
            visitedTids[tid] = Date.now();
            saveVisited();
        }
    }
})();