点击帖子链接时立刻标记为已读,支持搜同和TT1069,解决快速跳转丢记录问题。
Ekde
// ==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();
}
}
})();