Created
August 2, 2025 01:24
-
-
Save slashedzer0/60b2cbbd9dc0e6786a5eb0dd2a6cf8a1 to your computer and use it in GitHub Desktop.
adds feature in libreddit/redlib to save/unsave posts, which is saved in localStorage as post ids (won't work over incognito sessions)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name redsave enhanced | |
// @namespace http://tampermonkey.net/ | |
// @version 1.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, @slashedzer0 | |
// @match https://redlib.perennialte.ch/* | |
// @icon https://redlib.perennialte.ch/favicon.ico | |
// @grant none | |
// @license GNU AGPL-3.0 | |
// @downloadURL https://update.greasyfork.org/scripts/497793/redsave.user.js | |
// @updateURL https://update.greasyfork.org/scripts/497793/redsave.meta.js | |
// ==/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://redlib.perennialte.ch/"; | |
// Flag to track if we're in saved posts view | |
var inSavedView = false; | |
// Check if the page loaded with the showSaved flag | |
const showSavedFlag = sessionStorage.getItem('showSaved'); | |
if (showSavedFlag === 'true' && window.location.href === domain) { | |
// Remove the flag so it doesn't trigger on normal page loads | |
sessionStorage.removeItem('showSaved'); | |
// Ensure the page is fully loaded before showing saved posts | |
if (document.readyState === 'complete') { | |
setTimeout(showSavedPosts, 300); // Small delay to ensure everything is ready | |
} else { | |
window.addEventListener('load', function() { | |
setTimeout(showSavedPosts, 300); | |
}); | |
} | |
} | |
// 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 = handleSavedClick; | |
link.style.cursor = "pointer"; // Fix #2: Set cursor to pointer | |
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); | |
} | |
} | |
// Handle click on "Saved" link - redirect if needed or show saved posts directly | |
function handleSavedClick() { | |
if (window.location.href != domain) { | |
// Set a flag in sessionStorage to indicate we should show saved posts after redirect | |
sessionStorage.setItem('showSaved', 'true'); | |
// Redirect to the home page | |
window.location.href = domain; | |
} else { | |
// Already on home page, show saved posts directly | |
showSavedPosts(); | |
} | |
} | |
// manage saving/unsaving when save/unsave button is clicked | |
function manageSaved(id, removeElement = false) { | |
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"; | |
// If we're in saved view and this is an unsave action, remove the post element | |
if (inSavedView && removeElement) { | |
const postElement = document.getElementById(id); | |
if (postElement) { | |
// If there's an HR element before this post, remove it too | |
const prevElement = postElement.previousElementSibling; | |
if (prevElement && prevElement.tagName === 'HR') { | |
prevElement.remove(); | |
} | |
postElement.remove(); | |
} | |
} | |
} | |
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; cursor: pointer;"; // Fix #2: Added cursor: pointer | |
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); | |
} | |
} | |
// Original showSaved function renamed to showSavedPosts | |
async function showSavedPosts() { | |
if (!savedPosts.length) { | |
alert("No saved posts yet. Go ahead and save some!"); | |
} | |
else { | |
// Set flag that we're in saved view | |
inSavedView = true; | |
// 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.cloneNode()); | |
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; cursor: pointer;"; // Fix #2: Added cursor: pointer | |
save.className = savedPosts[k]; | |
save.textContent = "unsave"; | |
// Add the ability to remove the post when unsaving in saved view | |
(function(index) { | |
save.onclick = function() { | |
manageSaved(savedPosts[index], true); // true means remove element when unsaving | |
}; | |
})(k); | |
document.getElementById(savedPosts[k]).querySelector(".post_footer").children[0].appendChild(save); | |
} | |
document.title = "saved"; | |
} | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment