13 votes

Lazy userscript

Just a hack job, but I'd seen some comment about it being inconvenient to ignore posts so I made a quick userscript (tested with Tampermonkey on Firefox) that adds hotkeys for bookmarking/ignoring/voting on a post. It can also navigate to the link or comments or prev/next pages (/). Only implemented for posts 1-9 at the moment.

Ex:

i+2 ignores and hides the 2nd post (or restores, if ignored)

? shows a summary of hotkeys

// ==UserScript==
// @name         Tildes
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Quickie convenience hotkeys for tildes.net
// @author       TT
// @match        *://tildes.net/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tildes.net
// @grant        none
// @require      https://unpkg.com/hotkeys-js/dist/hotkeys.min.js
// ==/UserScript==
(function () {
    "use strict";
    let Action;
    (function (Action) {
        Action[Action["Invalid"] = 0] = "Invalid";
        Action[Action["Bookmark"] = 1] = "Bookmark";
        Action[Action["Comments"] = 2] = "Comments";
        Action[Action["Ignore"] = 3] = "Ignore";
        Action[Action["Open"] = 4] = "Open";
        Action[Action["Vote"] = 5] = "Vote";
    })(Action || (Action = {}));
    const regex = /tildes\.net(\/~(?<group>\w+))?/gi;
    let match = regex.exec(document.location);
    //In a group if I wanted to support hotkeys there?
    if (match.groups.group) {
        //alert(match.groups.group);
    }
    else {
        addArticleNumbers();
        addMainHotkeys();
    }
    //Route
    function routeAction(action, event, handler) {
        event.preventDefault();
        //Grab index.  Zero-index?
        let indexText = handler.key.substring(2);
        let index = parseInt(indexText) - 1;
        if (isNaN(index))
            return;
        actOnArticle(action, index);
    }
    function actOnArticle(action, index) {
        //Get article for index
        let article = document.querySelector(".topic-listing").children[index].children[0];
        //Do the thing
        switch (action) {
            case Action.Bookmark:
                if (article.style.borderStyle === 'solid')
                    article.style.borderStyle = 'none';
                else
                    article.style.borderStyle = 'solid';
                article.querySelector('button[data-ic-put-to$="bookmark"]').click();
                break;
            case Action.Comments:
                article.querySelector(".topic-info-comments a").click();
                break;
            case Action.Ignore:
                //Hide vs blank?
                if (article.style.visibility === "hidden")
                    article.style.visibility = "visible";
                else
                    article.style.visibility = "hidden";
                // article.style.display = 'none';
                article.querySelector('button[data-ic-put-to$="ignore"]').click();
                break;
            case Action.Open:
                article.querySelector(".topic-title a").click();
                break;
            case Action.Vote:
                article.querySelector(".topic-voting").click();
                break;
        }
    }
    function addArticleNumbers() {
        let titles = Array.from(document.querySelectorAll(".topic-title a"));
        for (let i = 1; i <= titles.length; i++) {
            let title = titles[i - 1];
            title.text = i + " - " + title.text;
        }
    }
    function addMainHotkeys() {
        //Set up handlers
        const handleBookmark = (event, handler) => routeAction(Action.Bookmark, event, handler);
        const handleComments = (event, handler) => routeAction(Action.Comments, event, handler);
        const handleIgnore = (event, handler) => routeAction(Action.Ignore, event, handler);
        const handleOpen = (event, handler) => routeAction(Action.Open, event, handler);
        const handleVote = (event, handler) => routeAction(Action.Vote, event, handler);
        hotkeys("shift+/", (e, h) => alert(getHelpText()));
        //Page nav
        hotkeys("left", (e, h) => Array.from(document.querySelectorAll(".pagination a")).find((e) => e.textContent == "Prev").click());
        hotkeys("right", (e, h) => Array.from(document.querySelectorAll(".pagination a")).find((e) => e.textContent == "Next").click());
        for (let i = 1; i <= 9; i++) {
            hotkeys("b+" + i, handleBookmark);
            hotkeys("c+" + i, handleComments);
            hotkeys("i+" + i, handleIgnore);
            hotkeys("o+" + i, handleOpen);
            hotkeys("v+" + i, handleVote);
        }
    }
    function getHelpText() {
        return `
    ←/→ = navigation
    b = Bookmark, i = Ignore, v = Vote,
    c = Open comments, o = Open link,

    Action+[1-9] calls that action on the corresponding article`;
    }
})();

9 comments

  1. [5]
    Zealotte
    Link
    With my very limited knowledge of programming in general and scripting in particular, I'm curious how hard (if even possible) it would be to just break the "ignore" link out of the "Actions" cage...

    With my very limited knowledge of programming in general and scripting in particular, I'm curious how hard (if even possible) it would be to just break the "ignore" link out of the "Actions" cage it currently resides in.

    My only (admittedly very small) annoyance with the current setup is that it takes two clicks and extra mouse or finger movement to ignore each post.

    I've always been a heavy-handed hider of threads that I have no interest in.

    3 votes
    1. [4]
      TemulentTeatotaler
      Link Parent
      It isn't hard to move (or recreate) the ignore button, you just need to add a bit of css to make it look the way you want. Web stuff isn't my wheelhouse, but here's an example of adding it to the...

      It isn't hard to move (or recreate) the ignore button, you just need to add a bit of css to make it look the way you want. Web stuff isn't my wheelhouse, but here's an example of adding it to the footer:

          function moveIgnore() {
              let articles = Array.from(document.querySelectorAll("article"));
              for (let i = 1; i <= articles.length; i++) {
                  let article = articles[i - 1];
                  let footer = article.querySelectorAll("footer");
                  //Add another column to the footer
                  jQuery(footer).css("grid-template-columns", "1fr 1.5fr 0.7fr 1fr");
                  let ignoreButton = article.querySelector('[data-ic-src$=ignore]');
                  //Move button to the footer
                  jQuery(ignoreButton).detach().appendTo(footer);
              }
          }
      

      With the full script being:

      // ==UserScript==
      // @name         Tildes
      // @namespace    http://tampermonkey.net/
      // @version      0.1
      // @description  Quickie convenience hotkeys for tildes.net
      // @author       TT
      // @match        *://tildes.net/*
      // @icon         https://www.google.com/s2/favicons?sz=64&domain=tildes.net
      // @grant        none
      // @require      https://unpkg.com/hotkeys-js/dist/hotkeys.min.js
      // ==/UserScript==
      (function () {
          "use strict";
          let Action;
          (function (Action) {
              Action[Action["Invalid"] = 0] = "Invalid";
              Action[Action["Bookmark"] = 1] = "Bookmark";
              Action[Action["Comments"] = 2] = "Comments";
              Action[Action["Ignore"] = 3] = "Ignore";
              Action[Action["Open"] = 4] = "Open";
              Action[Action["Vote"] = 5] = "Vote";
          })(Action || (Action = {}));
          const regex = /tildes\.net(\/~(?<group>\w+))?/gi;
          let match = regex.exec(document.location);
          let defaultAction = Action.Ignore;
          //In a group if I wanted to support hotkeys there?
          if (match.groups.group) {
              //alert(match.groups.group);
          }
          else {
              addArticleNumbers();
              addMainHotkeys();
              moveIgnore();
          }
          //Route
          function routeAction(action, event, handler) {
              event.preventDefault();
              //Grab index.  Zero-index?
              let indexText = handler.key.substring(2);
              let index = parseInt(indexText) - 1;
              if (isNaN(index))
                  return;
              actOnArticle(action, index);
          }
          function actOnArticle(action, index) {
              //Get article for index
              let article = document.querySelector(".topic-listing").children[index].children[0];
              //Do the thing
              switch (action) {
                  case Action.Bookmark:
                      if (article.style.borderStyle === "solid")
                          article.style.borderStyle = "none";
                      else
                          article.style.borderStyle = "solid";
                      article.querySelector('button[data-ic-put-to$="bookmark"]').click();
                      break;
                  case Action.Comments:
                      article.querySelector(".topic-info-comments a").click();
                      break;
                  case Action.Ignore:
                      //Hide vs blank?
                      if (article.style.visibility === "hidden")
                          article.style.visibility = "visible";
                      else
                          article.style.visibility = "hidden";
                      // article.style.display = 'none';
                      article.querySelector('button[data-ic-put-to$="ignore"]').click();
                      break;
                  case Action.Open:
                      article.querySelector(".topic-title a").click();
                      break;
                  case Action.Vote:
                      article.querySelector(".topic-voting").click();
                      break;
              }
          }
          function addArticleNumbers() {
              let titles = Array.from(document.querySelectorAll(".topic-title a"));
              for (let i = 1; i <= titles.length; i++) {
                  let title = titles[i - 1];
                  title.text = i + " - " + title.text;
              }
          }
          function moveIgnore() {
              let articles = Array.from(document.querySelectorAll("article"));
              for (let i = 1; i <= articles.length; i++) {
                  let article = articles[i - 1];
                  let footer = article.querySelectorAll("footer");
                  //Add another column to the footer
                  jQuery(footer).css("grid-template-columns", "1fr 1.5fr 0.7fr 1fr");
                  let ignoreButton = article.querySelector('[data-ic-src$=ignore]');
                  //Move button to the footer
                  jQuery(ignoreButton).detach().appendTo(footer);
              }
          }
          function addMainHotkeys() {
              //Set up handlers
              const handleBookmark = (event, handler) => routeAction(Action.Bookmark, event, handler);
              const handleComments = (event, handler) => routeAction(Action.Comments, event, handler);
              const handleIgnore = (event, handler) => routeAction(Action.Ignore, event, handler);
              const handleOpen = (event, handler) => routeAction(Action.Open, event, handler);
              const handleVote = (event, handler) => routeAction(Action.Vote, event, handler);
              hotkeys("b", (e, h) => (defaultAction = Action.Bookmark));
              hotkeys("c", (e, h) => (defaultAction = Action.Comments));
              hotkeys("i", (e, h) => (defaultAction = Action.Ignore));
              hotkeys("o", (e, h) => (defaultAction = Action.Open));
              hotkeys("v", (e, h) => (defaultAction = Action.Vote));
              hotkeys("shift+/", (e, h) => alert(getHelpText()));
              hotkeys("0+1", (e, h) => alert("foo"));
              hotkeys("1+0", (e, h) => alert("bar"));
              //Page nav
              hotkeys("left", (e, h) => Array.from(document.querySelectorAll(".pagination a"))
                  .find((e) => e.textContent == "Prev")
                  .click());
              hotkeys("right", (e, h) => Array.from(document.querySelectorAll(".pagination a"))
                  .find((e) => e.textContent == "Next")
                  .click());
              for (let i = 1; i <= 9; i++) {
                  hotkeys("b+" + i, handleBookmark);
                  hotkeys("c+" + i, handleComments);
                  hotkeys("i+" + i, handleIgnore);
                  hotkeys("o+" + i, handleOpen);
                  hotkeys("v+" + i, handleVote);
                  //Can't just use a number
                  hotkeys("shift+" + i, (e, v) => {
                      e.preventDefault();
                      actOnArticle(defaultAction, i - 1);
                  });
              }
          }
          function getHelpText() {
              return (`
          ←/→ = navigation 
          b = Bookmark, i = Ignore, v = Vote, 
          c = Open comments, o = Open link, 
      
          Action+[1-9] calls that action on the corresponding article
      
          Action by itself sets the (ephemeral) default mode which will be used on the article
          Shift+[1-9] calls the default action on the corresponding article
      
          Default = ` + defaultAction);
          }
          //https://stackoverflow.com/questions/123999/how-can-i-tell-if-a-dom-element-is-visible-in-the-current-viewport/7557433#7557433
          function inViewport(el) {
              var rect = el.getBoundingClientRect();
              return (rect.top >= 0 &&
                  rect.left >= 0 &&
                  rect.bottom <=
                      (window.innerHeight || document.documentElement.clientHeight) &&
                  rect.right <= (window.innerWidth || document.documentElement.clientWidth));
          }
      })();
      
      3 votes
      1. [3]
        Zealotte
        Link Parent
        Thank you for this. I tried to copy and paste that into TamperMonkey on my Mac using the Safari extension, but it didn't seem to do anything. I'm sure it's something I'm doing wrong or...

        Thank you for this. I tried to copy and paste that into TamperMonkey on my Mac using the Safari extension, but it didn't seem to do anything.

        I'm sure it's something I'm doing wrong or misunderstanding.

        You have already spent too much time on this on my behalf.

        I'll try to look at this later and see if there is something I did wrong.

        2 votes
        1. [2]
          TemulentTeatotaler
          Link Parent
          Sorry for putting something out that wasn't working properly! It was fine in Chrome, but wasn't working in Firefox. I'm guessing its running prematurely, but I'm not sure of the clean way of...

          Sorry for putting something out that wasn't working properly! It was fine in Chrome, but wasn't working in Firefox. I'm guessing its running prematurely, but I'm not sure of the clean way of dealing with that in a userscript.

          For just moving the ignore button you could try this version (tested on FF as well) that runs after a 500ms delay:

          // ==UserScript==
          // @name         MoveIgnore
          // @namespace    http://tampermonkey.net/
          // @version      0.1
          // @description  try to take over the world!
          // @author       You
          // @match        https://tildes.net/
          // @icon         https://www.google.com/s2/favicons?sz=64&domain=tildes.net
          // @grant        none
          // ==/UserScript==
          
          (function() {
              'use strict';
          
              setTimeout(moveIgnore, 500);
          
                  function moveIgnore() {
                      let articles = Array.from(document.querySelectorAll("article"));
                      for (let i = 1; i <= articles.length; i++) {
                          let article = articles[i - 1];
                          let footer = article.querySelector("footer");
                          //Add another column to the footer
                          jQuery(footer).css("grid-template-columns", "1fr 1.5fr 0.7fr 1fr");
                          let ignoreButton = article.querySelector('[data-ic-src$=ignore]');
                          //Move button to the footer
                          jQuery(ignoreButton).detach().appendTo(footer);
                      }
                  }
          })();
          
          3 votes
          1. Zealotte
            Link Parent
            You are awesome! I had to download Firefox to my Mac, but it works! Thank you so much!

            You are awesome! I had to download Firefox to my Mac, but it works!

            Thank you so much!

            2 votes
  2. [3]
    WTFisthisOMGreally
    Link
    Where do you put all that text so it starts working? I’m using safari.

    Where do you put all that text so it starts working? I’m using safari.

    1 vote
    1. cfabbro
      Link Parent
      Tampermonkey is available for Safari. You need to install it in that, or some other similar User Script extension.

      Tampermonkey is available for Safari. You need to install it in that, or some other similar User Script extension.

      1 vote
    2. TemulentTeatotaler
      Link Parent
      Unfortunately I have limited experience and access to Safari. It has a version of Tampermonkey supported, but with a $2 charge. MeddleMonkey or Userscripts might work. All the script is doing is...

      Unfortunately I have limited experience and access to Safari. It has a version of Tampermonkey supported, but with a $2 charge.

      MeddleMonkey or Userscripts might work.

      All the script is doing is injecting some javascript (https://unpkg.com/hotkeys-js/dist/hotkeys.min.js, and the code above) on tildes.net pages.

      1 vote