redsave

adds feature in libreddit/redlib to save/unsave posts, which is saved in localStorage as post ids (won't work over incognito sessions)

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         redsave
// @namespace    http://tampermonkey.net/
// @version      1
// @description  adds feature in libreddit/redlib to save/unsave posts, which is saved in localStorage as post ids (won't work over incognito sessions)
// @author       @yokelman
// @match        https://libreddit.kavin.rocks/*
// @icon         https://libreddit.kavin.rocks/favicon.ico
// @grant        none
// @license      GNU AGPL-3.0
// ==/UserScript==

(function() {
    // !!!!!! THE SCRIPT CAN ACT WEIRDLY IF USING MULTIPLE TABS TO BROWSE THE SITE SINCE AFAIK LOCALSTORAGE ISN'T SYNCHRONIZED IN REAL TIME TO WORK ACROSS MULTIPLE TABS
    // !!!!!! EXAMPLE: IF YOU SAVE POST A AND B ON THE MAIN PAGE, THEN OPEN POST A ON A NEW TAB, THEN UNSAVE POST B ON THE MAIN PAGE TAB AND THEN UNSAVE POST A THROUGH THE NEWLY OPENED TAB (WHICH ONLY CONTAINS A)...
    // !!!!!! POST B (WHICH SHOULD HAVE BEEN UNSAVED THROUGH MAIN PAGE TAB) WILL STILL BE IN LOCALSTORAGE
    // !!!!!! MORAL: DON'T GO SAVING/UNSAVING ON MULTIPLE PAGES AS FAR AS POSSIBLE, AND AFTER SAVING/UNSAVING ON ONE TAB IDEALLY RELOAD ALL THE OTHER TABS

    // tested on v0.30.1, v0.31.0, v0.31.2, v0.34.0 on both libreddit and redlib which should be the majority of public instances

    'use strict';

    var domain = "https://libreddit.kavin.rocks/";

    // load savedPosts from localStorage and handle if empty
    var savedPosts = window.localStorage.getItem("savedPosts");
    if (!savedPosts) {
        savedPosts = [];
    }
    else {
        savedPosts = savedPosts.split(",");
        // if localStorage has savedPosts="", splitting gives you [""], don't want that empty string as an element
        if (!savedPosts[0]) {
            savedPosts.splice(0);
        }
    }

    // adds an option to view saved posts when user clicks on Feeds at top left corner (if saved posts exist)
    if (savedPosts.length) {
        var link = document.createElement("a");
        link.textContent = "Saved";
        link.onclick = showSaved;
        if (document.getElementById("feed_list").querySelectorAll("p").length == 2) {
            document.getElementById("feed_list").insertBefore(link, document.getElementById("feed_list").children[4]);
        }
        else {
            document.getElementById("feed_list").appendChild(link);
        }
    }

    // manage saving/unsaving when save/unsave button is clicked
    function manageSaved(id) {
        if (document.getElementsByClassName(id)[0].textContent == "save") {
            savedPosts.push(id);
            document.getElementsByClassName(id)[0].textContent = "unsave";
        }
        else {
            savedPosts.splice(savedPosts.indexOf(id), 1);
            document.getElementsByClassName(id)[0].textContent = "save";
        }
        window.localStorage.setItem("savedPosts", savedPosts);
    }

    var post_footer = document.getElementsByClassName("post_footer");

    // go post by post and add the save/unsave element
    for (var i = 0; i < post_footer.length; i++) {
        var save = document.createElement("a");
        // the below is basically a somehow-works hack to get the post id (because the page could either have a collection of posts, or just a single post and i don't want to code for different cases) - can definitely lead to errors if site layout changes
        var postId = post_footer[i].querySelector("a").href.split("/")[6];
        if ((savedPosts.includes(postId))) {
            save.textContent = "unsave";
        }
        else {
            save.textContent = "save";
        }
        save.style = "font-weight: bold;";
        save.className = post_footer[i].querySelector("a").href.split("/")[6];
        (function(localPostId) {save.onclick = function() {manageSaved(localPostId);};})(postId);
        // if url includes comments, it means you're viewing an individual post (hopefully there's no loophole in this)
        if (!window.location.href.includes("comments")) {
            post_footer[i].appendChild(save);
        }
        else {
            document.getElementById("post_links").appendChild(save);
        }
    }

    // function to make a div (manipulate the existing div tbh) to show all the saved posts
    // function is async so posts will show in chronological order but it's a compromise for speed as posts load one by one (speed depends on your network and libreddit/redlib server chosen)
    async function showSaved() {
        if (window.location.href != domain) {
            alert("You can only see Saved posts from home page. Try from there.");
        }
        else {
            if (!savedPosts.length) {
                alert("No saved posts yet. Go ahead and save some!");
            }
            else {
                // no pagination for now
                if (document.querySelectorAll("footer").length == 2) {
                    document.querySelector("footer").remove();
                }
                if (document.querySelectorAll("form").length == 2) {
                    document.querySelectorAll("form")[1].remove();
                }
                document.title = "loading saved...";
                var postPage;
                var hr = document.createElement("hr");
                hr.className = "sep";
                document.getElementById("posts").innerHTML = "";

                for (var j = savedPosts.length - 1; j > -1; j--) {
                    document.getElementById("posts").appendChild(hr);
                    await fetch(domain + "comments/" + savedPosts[j])
                        .then(response => {return response.text();})
                        .then(html => {postPage = new DOMParser().parseFromString(html, "text/html");
                                       document.getElementById("posts").appendChild(postPage.getElementsByClassName("post")[0]);
                                       console.log(j);
                                       document.getElementsByClassName("post")[document.getElementsByClassName("post").length - 1].id = savedPosts[j];})
                        .catch(err => console.error("Error occurred: " + err));
                }
                for (var k = 0; k < savedPosts.length; k++) {
                    var save = document.createElement("a");
                    save.style = "font-weight: bold;";
                    save.className = savedPosts[k];
                    save.textContent = "unsave";
                    (function(index) {save.onclick = function() {manageSaved(savedPosts[index]);};})(k);
                    document.getElementById(savedPosts[k]).querySelector(".post_footer").children[0].appendChild(save);
                }
                document.title = "saved";
            }
        }
    }
})();