Tildes UserScript: Comment Link Fix

blank_dvth
Mon, 12 Jun 2023 17:54:51 +0000

I joined Tildes a couple of days ago, and I'm absolutely loving the interface and community.

In the last few days of using Tildes, I noticed a particular problem that was mildly annoying; if you have the "Collapse old comments when I return to a topic" setting on, and you click on a link that is supposed to lead to a comment in a topic you have already visited, it won't jump to that comment.

Searching around, I found a post about it from a day ago, in which long-time users have mentioned that it's been a known problem for a while now. In those comments, someone mentioned permalinks as a solution, but it appears that's still in the works.

For now, I've made a quick userscript that will address this issue (and adds some slight related functionality). It hasn't been thoroughly tested yet, so if any issues occur, please let me know. This userscript is designed to be used with Tampermonkey (a privacy-friendly alternate that should work is ViolentMonkey), which is available in all popular desktop browsers. Installation instructions for Tampermonkey are available on their site (it's installed like any other extension).

To install the script, you can head to this GitHub Gist which contains the code (click "Raw" to open the TamperMonkey install prompt), or you can copy and paste the code from the following dropdown block into a "New script" on the TamperMonkey dashboard. The dropdown is not guaranteed to contain the latest version.

Code

// ==UserScript==
// @name         Tildes Comment Link Fix
// @namespace    https://gist.github.com/blankdvth/6da89fff580e8cf6e50f88847ddb5729
// @version      1.2.0
// @description  Fixes comment links (anchors) not working as a result of Tildes' comment collapsing feature.
// @author       blank_dvth
// @match        https://tildes.net/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tildes.net
// @grant        none
// ==/UserScript==

/*
    USER SETTINGS
    This script is not big enough to warrant a visual settings menu, so adjust settings here.
    true = enable, false = disable
*/
const alwaysRun_S = false; // If enabled, will always run the script, even if the comment was not collapsed (site works fine in this case). This is useful if you want to make use of the other settings.
const smoothScroll_S = false; // If enabled, will smoothly (animated) scroll to the comment. If disabled, will jump to the comment.
const uncollapseIndividual_S = true; // If enabled will uncollapse parent comments into one line instead of fully uncollapsing them.
const uncollapseChildren_S = true; // If enabled, will uncollapse all children of the comment. If disabled, will leave them collapsed.
const collapseIrrelevant_S = true; // The script uncollapses all parents to ensure the comment is visible. This will collapse irrelevant (not direct parent) comments again.
// END OF USER SETTINGS

/**
 * Uncollapses the comment if it is collapsed.
 * @param {HTMLElement} element Article element of the actual comment
 * @param {boolean} individual If true, will "uncollapse" into one line instead of fully uncollapsing
 * @returns {boolean} True if the comment was collapsed, false if it was not
 */
function uncollapse(element, individual = false) {
    if (element.nodeName !== "ARTICLE") return false;
    var removed = false;
    if (
        !individual &&
        element.classList.contains("is-comment-collapsed-individual")
    ) {
        element.classList.remove("is-comment-collapsed-individual");
        removed = true;
    }
    if (element.classList.contains("is-comment-collapsed")) {
        if (individual)
            element.classList.add("is-comment-collapsed-individual");
        element.classList.remove("is-comment-collapsed");
        removed = true;
    }
    return removed;
}

/**
 * Uncollapses all direct parents of the comment.
 * @param {HTMLElement} element Article element of the actual comment
 * @param {boolean} collapseIrrelevant If true, will collapse irrelevant comments again
 * @param {boolean} individual If true, will "uncollapse" into one line instead of fully uncollapsing
 * @returns {boolean} True if any parent was collapsed, false if none were
 */
function uncollapseParents(element, collapseIrrelevant, individual) {
    const relevant = []; // List of relevant elements (direct parents)
    var wasCollapsed = false; // Whether any parent was collapsed
    while (
        element.parentElement &&
        element.parentElement.nodeName !== "SECTION"
    ) {
        element = element.parentElement;
        relevant.push(element); // Add parent to relevant list
        if (uncollapse(element, individual)) wasCollapsed = true;
        // Collapse all irrelevant sibling comments (if feature enabled)
        if (collapseIrrelevant && element.nodeName === "ARTICLE") {
            element
                .querySelectorAll(
                    `article#${element.id} > ol.comment-tree > li.comment-tree-item > article:not(.is-comment-collapsed)`
                )
                .forEach((child) => {
                    if (!relevant.includes(child))
                        child.classList.add("is-comment-collapsed");
                });
        }
    }
    return wasCollapsed;
}

/**
 * Uncollapses all direct children of the comment.
 * @param {HTMLElement} element Article element of the actual comment
 */
function uncollapseChildren(element) {
    element
        .querySelectorAll("article.is-comment-collapsed article.is-comment-collapsed-individual")
        .forEach(uncollapse);
}

(function () {
    if (!location.hash.startsWith("#comment-")) return; // Not a comment hash
    const comment = document.getElementById(location.hash.substring(1)); // Get comment element
    if (!comment) return; // Comment does not exist
    // Uncollapse the comment itself, and it's parents, then perform other actions if needed/enabled
    if (
        uncollapse(comment) |
            uncollapseParents(
                comment,
                collapseIrrelevant_S,
                uncollapseIndividual_S
            ) ||
        alwaysRun_S
    ) {
        // Uncollapse all children (if feature enabled)
        if (uncollapseChildren_S) uncollapseChildren(comment);
        // Scroll to the comment
        if (smoothScroll_S) comment.scrollIntoView({ behavior: "smooth" });
        else comment.scrollIntoView();
    }
})();

Settings Description

There are comments that already contain short descriptions for each setting in the code, but here are more in-depth descriptions.

alwaysRun: By default, the script does not run if the comment and its parents are already uncollapsed (this means the in-built anchor will work as expected). However, when this setting is enabled, the script will still perform the additional options (such as uncollapsing children and collapsing irrelevant).

smoothScroll: When enabled, will use a smooth animated scroll. When disabled, will jump directly.

uncollapseIndividual: Parent comments need to be uncollapsed in some shape or form in order for the script to work. This allows you to choose what type of uncollapse is used. When enabled, it will uncollapse the parent comments into a single line (shows a short preview). When disabled, it will fully uncollapse the parent comments (everything is visible).

uncollapseChildren: When enabled, will automatically uncollapse all child comments (replies) to the linked comment.

collapseIrrelevant: When enabled, it will automatically collapse all sibling/cousin comments (comments that have a shared parent but are not directly ancestors of the linked comment)

Changelog (Last Updated 2023-06-12 22:55 EST)

v1.2.0:

Prevent entire sibling/cousin chains from being collapsed, only collapse toplevel

Ensure individually collapsed children are uncollapsed properly

Ensure proper exiting if comment does not exist

v1.1.0:

First public release

Comments URL: https://tildes.net/~tildes/1647/tildes_userscript_comment_link_fix

Votes: 33

Comments: 11