您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Adds subscribed magazines and liked collections to the channel select menu.
当前为
// ==UserScript== // @name Improved Channel Select Menu for Kbin // @namespace http://tampermonkey.net/ // @version 0.3.0 // @description Adds subscribed magazines and liked collections to the channel select menu. // @author NeighborlyFedora // @match *://kbin.social/* // @match *://kbin.earth/* // @icon https://www.google.com/s2/favicons?sz=64&domain=kbin.social // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js // @license GPL-3.0-or-later // ==/UserScript== //Code partially based on Floating Subs List by raltsm4k (http://greasyfork.icu/en/scripts/469121-floating-subs-list) let user; let cacheId; let subs = []; let clls = []; let subsHtml = []; let cllsHtml = []; let settings = { cllsFirst: true, cacheEnabled: true, defaultIcons: true, fillOnChange: true }; const SETTINGS_TEXT = { cllsFirst: "List liked collections before subscribed magazines.", cacheEnabled: "Cache menu items for faster loading.", defaultIcons: "Add placeholder icons to collections and iconless magazines.", fillOnChange: "Immediately reload menu when a magazine is subscribed to." } let settingsOpen = false; let isFilling = false; let fetchTries = 3; main(); const observer = new MutationObserver( function(records){ for(const record of records){ if( Array.from(record.addedNodes).filter(node => node.nodeName == "BODY").length ){ main(); } if(settings["fillOnChange"]){ const subButtons = Array.from(record.addedNodes).filter( node => node.classList !== undefined && node.classList.contains("magazine__subscribe") ); if(subButtons.length){ fill(); }; } } }); observer.observe(document,{subtree: true, childList: true}); function main() { "use strict"; user = document.querySelector("#header a.login").getAttribute("href"); cacheId = "icsm_" + user; if(user === "/login") return; const channelList = document.querySelector("#header li:has(a[title='Select a channel']) .dropdown__menu"); Object.assign(channelList, { id: "channel-list" }); let clRefresh = document.querySelector("#cl-refresh") if(clRefresh === null){ clRefresh = Object.assign(document.createElement("button"), { id: "cl-refresh", title: "Refresh list", }); clRefresh.appendChild(Object.assign(document.createElement("i"), { className: "fa-solid fa-rotate" })); } channelList.prepend(clRefresh); clRefresh.addEventListener("click", fill); let clSettings = document.querySelector("#cl-settings") if(clSettings === null){ clSettings = Object.assign(document.createElement("button"), { id: "cl-settings", title: "Settings", }); clSettings.appendChild(Object.assign(document.createElement("i"), { className: "fa-solid fa-gear" })); } channelList.prepend(clSettings); clSettings.addEventListener("click", function(){ if(settingsOpen){ closeSettings(); }else{ openSettings(); } settingsOpen = !settingsOpen; }); $(document).on("keydown", function(event) { if(event.key == "Escape"){ closeSettings(); } }); const icsmSettings = Object.assign(document.createElement("ul"), { id: "icsm-settings" }); $(icsmSettings).hide(); channelList.append(icsmSettings); icsmSettings.append(Object.assign(document.createElement("h3"), { textContent: "Settings" })); $("<style>").text( ` #header #channel-list { scroll-behavior: auto; max-width: 60vw; width: 25rem; height: 25rem; overflow-x: hidden; overflow-y: scroll; } #header #channel-list h3 { border-bottom: var(--kbin-sidebar-header-border); color: var(--kbin-sidebar-header-text-color); font-size: .8rem; width: 95%; margin: 1rem 0 0 2.5%; text-transform: uppercase; } #cl-refresh { display: inline !important; position: absolute; background: none; border: 0; color: var(--kbin-meta-link-color); cursor: pointer; height: auto; width: auto; text-indent: 0; right: 2.5%; text-align: right; margin: 1rem 0 0 1rem; padding: 0 !important; } #cl-settings { display: inline !important; position: absolute; background: none; border: 0; color: var(--kbin-meta-link-color); cursor: pointer; height: auto; width: auto; text-indent: 0; right: calc(2.5% + 1.25rem); text-align: right; margin: 1rem 0 0 1rem; padding: 0 !important; } #cl-refresh:hover, #cl-settings:hover { color: var(--kbin-meta-link-hover-color); } #icsm-settings { padding: 0; } #icsm-settings li { padding: .35rem 1rem; } #icsm-settings input { min-width: 1.5rem; margin-right: .4rem; } #header #channel-list li { height: auto; } #header #channel-list #sub_item a:has(figure) { display: flex !important; justify-content: left; } #header #channel-list a { overflow-x: hidden; position: relative; padding: .35rem 1rem !important; } #header #channel-list #sub_item figure{ float: left; margin-right: .25rem; } #header #channel-list #sub_item figure :is(img,i){ width: 1.25rem; height: 1.25rem; vertical-align: middle; } #header #channel-list #sub_item figure i { display: flex; align-items: center; justify-content: center; position: relative; top: .15rem; } #header #channel-list #sub_item figure i::before{ vertical-align: middle; } #header #channel-list::-webkit-scrollbar { width: 8px; } .rounded-edges #header #channel-list::-webkit-scrollbar-track { border-top-right-radius: var(--kbin-rounded-edges-radius); border-bottom-right-radius: var(--kbin-rounded-edges-radius); } .rounded-edges #header #channel-list::-webkit-scrollbar-thumb { border-radius: var(--kbin-rounded-edges-radius); } #header #channel-list::-webkit-scrollbar-track { background: var(--kbin-bg); border-left: var(--kbin-section-border); } #header #channel-list::-webkit-scrollbar-thumb { background: var(--kbin-meta-link-color); border-left: var(--kbin-section-border); transition-duration: 0.4s; } #header #channel-list::-webkit-scrollbar-thumb:hover { background: var(--kbin-meta-link-hover-color); } ` ).appendTo(document.head); const settingsCache = JSON.parse(localStorage.getItem(cacheId + "_settings")); if(settingsCache !== null){ Object.assign(settings, settingsCache); } for (const [key, val] of Object.entries(settings)) { if(SETTINGS_TEXT[key] === undefined) continue; const item = document.createElement("li"); const checkbox = Object.assign(document.createElement("input"), { type: "checkbox", checked: val }); item.append(checkbox); item.append(Object.assign(document.createElement("span"), { textContent: SETTINGS_TEXT[key] })); icsmSettings.append(item); checkbox.addEventListener("change", function(event){ settings[key] = checkbox.checked; localStorage.setItem(cacheId + "_settings", JSON.stringify(settings)); }); } localStorage.setItem(cacheId + "_settings", JSON.stringify(settings)); const cache = JSON.parse(localStorage.getItem(cacheId)); if (!settings["cacheEnabled"] || cache === null || Date.now() >= cache.expire) { fill(); } else if(!isFilling) { isFilling = true; empty(); console.log("Fetching from cache...."); cache.subs.forEach(function(cached_html){ subs.push($(Object.assign(document.createElement("li"),{ id: "sub_item" })).html(cached_html)) }); cache.clls.forEach(function(cached_html){ clls.push($(Object.assign(document.createElement("li"),{ id: "sub_item" })).html(cached_html)) }); complete(); } } function fill(){ if(isFilling) return; isFilling = true; $("#channel-list").children("li").show(); $("#channel-list").children("h3").show(); $("#icsm-settings").hide(); localStorage.removeItem(cacheId); empty(); $("#cl-refresh").find("i").addClass("fa-spin"); fetchSubs(1); } function empty(){ subs.forEach((sub) => { sub.remove();} ); clls.forEach((cll) => { cll.remove();} ); subs = []; subsHtml = []; clls = []; cllsHtml = []; } function fetchSubs(page){ $.get( window.location.origin + "/settings/subscriptions/magazines?p=" + page, function(data) { const $dom = $($.parseHTML(data)); const $subsList = $dom.find("#content .magazines ul"); console.log("Fetching page " + page + " of subscriptions...."); if (!$subsList.length) { if (fetchTries > 0) { console.log("Failed to fetch page " + page + " of subscriptions. Retrying...."); fetchTries--; fetchSubs(page); } else { console.log("Failed to fetch page " + page + " of subscriptions. Out of attempts."); failSubs(); } }else{ fetchTries = 3; const $newSubs = $subsList.children("li"); $newSubs.each(function() { $(this).prop("id", "sub_item"); const $link = $(this).find("a"); const $icon = $(this).find("figure"); if($icon.length){ $link.prepend($icon); } $link.removeClass(); if(window.location.href.includes("/m/"+$link.text())){ $link.addClass("active"); } $(this).append($link); $(this).find("small").remove(); $(this).find("div").remove(); subs.push($(this)); subsHtml.push($(this).html()); }); const $pg = $dom.find("#content .pagination__item--next-page.pagination__item--disabled"); if ($pg.length) { fetchClls(1); } else { fetchSubs(page+1); } } }).fail(function() { console.log("Error occurred in reaching page " + page + " of subscriptions."); failSubs(); }); } function failSubs(){ console.log("Skipping to collections."); fetchClls(1); } function fetchClls(page){ $.get( window.location.origin + "/magazines/collections?p=" + page, function(data) { const $dom = $($.parseHTML(data)); const $cllsList = $dom.find("#content .categories tbody"); console.log("Fetching page " + page + " of collections...."); if (!$cllsList.length) { if (fetchTries > 0) { console.log("Failed to fetch page " + page + " of collections. Retrying..."); fetchTries--; fetchClls(page); } else { console.log("Failed to fetch page " + page + " of collections. Out of attempts."); failClls() } }else{ fetchTries = 3; const $newClls = $cllsList.children("tr:has(button.active)"); $newClls.each(function() { const $link = $(this).find("td:first-child a"); $link.removeClass(); const $li = $(document.createElement("li")); $li.prop("id", "sub_item"); $li.append($link); clls.push($li); cllsHtml.push($li.html()); }); const $pg = $dom.find("#content .pagination__item--next-page.pagination__item--disabled"); if ($pg.length) { if(settings["cacheEnabled"]){ cache(); } complete(); } else { fetchClls(page+1); } } }).fail(function() { console.log("Error occurred in reaching page " + page + " of collections."); failClls(); }); } function failClls(){ console.log("Skipping to menu completion."); cache(); complete(); } function cache(){ localStorage.setItem(cacheId, JSON.stringify({subs: subsHtml, clls: cllsHtml, expire: Date.now() + 15 * 60 * 1000})); } function complete(){ console.log("Completing menu...."); $("#cl-refresh").find("i").removeClass("fa-spin"); $("#channel-list").children("h3").remove(); $("#channel-list").prepend($(Object.assign(document.createElement("h3"), { id: "cl-header-feeds", textContent: "Feeds" }))); $("#channel-list").prepend($("#cl-refresh")); $("#channel-list").prepend($("#cl-settings")); if(settings["cllsFirst"]){ completeClls(); completeSubs(); }else{ completeSubs(); completeClls(); } $("#channel-list").append($("#icsm-settings")); console.log("Done!"); isFilling = false; } function completeSubs(){ $("#channel-list").append($(Object.assign(document.createElement("h3"), { id: "cl-header-subs", textContent: "Subscribed Magazines" }))); subs.sort(function(a, b){ return a.find("a").text().trim().toLowerCase().localeCompare(b.find("a").text().trim().toLowerCase()); }); subs.forEach(function($sub) { $("#channel-list").append($sub); const $link = $sub.find("a") if(!$sub.find("figure img").length) { $sub.find("figure").remove(); if(settings["defaultIcons"]){ const icon = document.createElement("figure"); icon.append(Object.assign(document.createElement("i"), { className: "fa-solid fa-newspaper" })); $link.prepend($(icon)); } } if( window.location.href.endsWith("/m/"+$link.text().trim()) || window.location.href.includes("/m/"+$link.text().trim()+"/") ){ $link.addClass("active"); }else{ $link.removeClass("active"); } }) } function completeClls(){ $("#channel-list").append($(Object.assign(document.createElement("h3"), { id: "cl-header-clls", textContent: "Liked Collections" }))); clls.sort(function(a, b){ return a.text().trim().toLowerCase().localeCompare(b.text().trim().toLowerCase()); }); clls.forEach(function($cll) { $("#channel-list").append($cll); const $link = $cll.find("a"); $cll.find("figure").remove(); if(settings["defaultIcons"]){ const icon = document.createElement("figure"); icon.append(Object.assign(document.createElement("i"), { className: "fa-solid fa-folder-open" })); $link.prepend($(icon)); } if( window.location.href.includes("/c/"+$link.text().trim()) || window.location.href.includes("/c/"+$link.text().trim()+"/") ){ $link.addClass("active"); }else{ $link.removeClass("active"); } }); } function openSettings(){ if(isFilling) return; $("#channel-list").children("li").hide(); $("#channel-list").children("h3").hide(); $("#icsm-settings").show(); } function closeSettings(){ complete(); $("#channel-list").children("li").show(); $("#channel-list").children("h3").show(); $("#icsm-settings").hide(); }