RED Editing Marker

Improve workflow in RED Editing forum by marking threads with status icons.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

You will need to install an extension such as Tampermonkey to install this script.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         RED Editing Marker
// @namespace    http://tampermonkey.net/
// @version      1.0.3
// @description  Improve workflow in RED Editing forum by marking threads with status icons.
// @description:en  Improve workflow in RED Editing forum by marking threads with status icons.
// @description:zh-CN 在 RED 的 Editing 版块中给帖子做标记,提高处理效率。
// @license      MIT
// @match        https://redacted.sh/forums.php*forumid=10*
// @match        https://redacted.sh/forums.php?action=viewthread&threadid=*
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==


(function() {
    'use strict';

    const url = new URL(location.href);

    /** 小提示(自动淡出) **/
    function toast(msg) {
        const existing = document.querySelector("#edit-toast");
        if (existing) existing.remove();

        const box = document.createElement("div");
        box.id = "edit-toast";
        box.textContent = msg;
        box.style.marginTop = "6px";
        box.style.padding = "4px 8px";
        box.style.display = "inline-block";
        box.style.background = "rgba(0,0,0,0.75)";
        box.style.color = "#fff";
        box.style.borderRadius = "4px";
        box.style.fontSize = "13px";
        box.style.transition = "opacity 0.4s";

        // 显示在按钮下方(container 是按钮所在的 div)
        const container = document.querySelector("body#forums div#wrapper div#content div.thin h2 div");
        if (container) container.appendChild(box);

        setTimeout(() => {
            box.style.opacity = "0";
            setTimeout(() => box.remove(), 400);
        }, 900);
    }


    // ===============================
    // 1) Detail page: buttons + save
    // ===============================
    if (url.searchParams.get("action") === "viewthread") {

        const editingAF = document.querySelector('a[href="forums.php?action=viewforum&forumid=10"]');
        if (!editingAF) return;

        const id = url.searchParams.get("threadid");
        if (!id) return;

        const savedState = GM_getValue("editStatus", {});

        const h2 = document.querySelector("body#forums div#wrapper div#content div.thin h2");
        if (!h2) return;

        const container = document.createElement("div");
        container.style.margin = "12px 0";


        function createButton(text, status, activeColor) {
            const btn = document.createElement("button");
            btn.textContent = text;
            btn.style.marginRight = "6px";
            btn.style.padding = "3px 6px";
            btn.style.borderRadius = "4px";
            btn.style.cursor = "pointer";
            btn.style.border = "none";
            btn.style.background = "#555";
            btn.style.color = "#eee";

            if (savedState[id] === status) {
                btn.style.background = activeColor;
                btn.style.color = "#fff";
            }

            btn.onclick = () => {
                savedState[id] = status;
                GM_setValue("editStatus", savedState);

                container.querySelectorAll("button").forEach(b => {
                    b.style.background = "#555";
                    b.style.color = "#eee";
                });

                btn.style.background = activeColor;
                btn.style.color = "#fff";
                toast(`Marked as: ${text}`);
            };

            return btn;
        }


        // 标记按钮
        container.appendChild(createButton("✅ Solved", "solved", "#4caf50"));
        container.appendChild(createButton("⭐ Follow", "star", "#2196f3"));
        container.appendChild(createButton("❌ Passed", "unknown", "#ff9800"));

        // Reset → 状态变成 "reset",主页显示 ✎
        const resetBtn = document.createElement("button");
        resetBtn.textContent = "✎ Reset";
        resetBtn.style.padding = "3px 6px";
        resetBtn.style.borderRadius = "4px";
        resetBtn.style.cursor = "pointer";
        resetBtn.style.border = "none";
        resetBtn.style.background = "#777";
        resetBtn.style.color = "#fff";

        resetBtn.onclick = () => {
            savedState[id] = "reset"; // ✅ 不删除,而是修改成 reset
            GM_setValue("editStatus", savedState);

            container.querySelectorAll("button").forEach(b => {
                b.style.background = "#555";
                b.style.color = "#eee";
            });

            toast("Reset done");
        };

        container.appendChild(resetBtn);

        h2.appendChild(container);
        return;
    }


    // ===============================
    // 2) Forum list page: show icons
    // ===============================
    if (url.searchParams.get("action") === "viewforum" && url.searchParams.get("forumid") === "10") {

        const savedState = GM_getValue("editStatus", {});
        const rows = document.querySelectorAll("tr.rowa, tr.rowb");

        rows.forEach(row => {

            const isSticky =
                row.querySelector("td.unread_sticky") ||
                row.querySelector("td.read_sticky") ||
                row.querySelector("td.unread_locked_sticky") ||
                row.querySelector("td.read_locked_sticky");

            const titleLink = row.querySelector('td:nth-child(2) a[href*="viewthread"]');
            if (!titleLink) return;

            const threadURL = new URL(titleLink.href, location.origin);
            const id = threadURL.searchParams.get("threadid");

            // 在列表页显示保存状态的图标
            if (id && savedState[id]) {
                const icon = document.createElement("span");

                if (savedState[id] === "solved")      icon.textContent = "✅ ";
                else if (savedState[id] === "unknown") icon.textContent = "❌ ";
                else if (savedState[id] === "star")    icon.textContent = "⭐ ";
                else if (savedState[id] === "reset")   icon.textContent = "✎ ";

                titleLink.parentNode.insertBefore(icon, titleLink);
            }

            // Zero comments:仅在**没有任何 savedState** 才显示 ⚪
            if (!isSticky && (!id || !savedState[id])) {
                const countCell = row.querySelector("td.number_column");
                if (countCell && countCell.textContent.trim() === "0") {

                    if (!titleLink.dataset.zeroFlagged) {
                        const icon = document.createElement("span");
                        icon.textContent = "⚪ ";
                        icon.style.fontWeight = "bold";
                        titleLink.parentNode.insertBefore(icon, titleLink);
                        titleLink.dataset.zeroFlagged = "true";
                    }
                }
            }
        });
    }

})();