Greasy Fork is available in English.
Adds a side panel with all magazine subscriptions.
当前为
// ==UserScript==
// @name Kbin Subscriptions Panel
// @namespace https://perry.dev
// @license MIT
// @version 0.6
// @description Adds a side panel with all magazine subscriptions.
// @author Daniel Pervan
// @match https://kbin.social/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=kbin.social
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// ==/UserScript==
(function () {
'use strict';
function addSubscriptionsSidePanel() {
const loginElement = document.querySelector(".login");
console.log(loginElement.href);
if (loginElement.href.endsWith("/login")) {
return;
}
GM_addStyle(`
body.extend-width .kbin-container {
max-width: 1620px;
}
#subscription-panel-settings-button {
position: absolute;
top: 0;
right: 0;
margin: 0.5rem;
padding: 0.5rem;
color: var(--kbin-section-text-color);
font-size: 0.8em;
cursor: pointer;
}
#subscription-panel-settings-button:hover {
color: var(--kbin-sidebar-header-text-color);
}
.subscription-panel-settings-modal {
position: fixed;
z-index: 100;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
backdrop-filter: blur(2px);
}
.subscription-panel-settings-modal-content {
background-color: var(--kbin-section-bg);
margin: 15% auto;
padding: 20px;
width: 80%;
max-width: 600px;
min-width: 300px;
animation: modalopen 0.25s ease-in-out;
}
.subscription-panel-settings-modal-content {
border: var(--kbin-options-border);
border-radius: 0.5rem;
}
.subscription-panel-settings-modal-content .close {
color: #aaa;
float: right;
font-size: 28px;
cursor: pointer;
}
.subscription-panel-settings-modal-content ul {
list-style: none;
padding-inline: 0;
}
.subscription-panel-settings-modal-content ul li {
margin-bottom: 1rem;
}
.subscription-panel-settings-modal-content ul li label {
display: block;
margin-bottom: 0.5rem;
}
.subscription-panel-settings-modal-content ul li .description {
font-size: 0.8em;
font-weight: 100;
font-style: italic;
opacity: 0.8;
}
.subscription-panel-settings-modal-content ul li input[type="checkbox"] {
margin-right: 0.5rem;
}
@keyframes modalopen {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1
transform: scale(1);
}
}
.subscription-panel-settings-modal-content h2 {
margin-top: 0;
}
#middle > .kbin-container {
grid-template-areas: "subscription-panel main sidebar";
grid-template-columns: minmax(200px, 1fr) 3fr 1fr;
}
#subscription-panel {
background-color: var(--kbin-section-bg);
border: var(--kbin-options-border);
color: var(--kbin-section-text-color);
margin-bottom: .5rem;
padding: 2rem 1rem;
border-radius: 0 0 .5rem .5rem !important;
height: fit-content;
font-size: 0.8em;
margin-right: 0.5rem;
position: relative;
}
#subscription-panel h3 {
border-bottom: var(--kbin-sidebar-header-border);
color: var(--kbin-sidebar-header-text-color);
font-size: .8rem;
margin: 0 0 1rem;
text-transform: uppercase;
}
#subscription-panel ul {
list-style: none;
line-height: 2.5em;
padding-inline: 0;
}
#subscription-panel ul li {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
animation: showItem 0.5s ease-in-out;
}
#subscription-panel ul li.hideItem {
animation: hideItem 0.25s ease-in-out;
animation-fill-mode: forwards;
}
#subscription-panel ul li a img {
height: 1.4em;
margin-right: .5em;
border-radius: 50%;
vertical-align: middle;
}
#subscription-panel ul li.no-image {
padding-left: 1.9em;
}
#subscription-panel .instance-name {
opacity: 0.8;
font-weight: 100;
}
#subscription-panel-spinner {
text-align: center;
font-size: 2em;
}
@keyframes showItem {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes hideItem {
0% {
opacity: 1;
max-height: 2.5em;
}
100% {
opacity: 0;
max-height: 0;
}
}
`);
/** Create the subscription panel */
const kbinContainer = document.querySelector("#middle > .kbin-container");
const magazinePanel = document.createElement("aside");
const magazinePanelUl = document.createElement("ul");
const title = document.createElement("h3");
magazinePanel.id = "subscription-panel";
title.innerHTML = "Subscriptions";
magazinePanel.appendChild(title);
kbinContainer.appendChild(magazinePanel);
/** Add search box */
const searchBox = document.createElement("input");
searchBox.type = "text";
searchBox.placeholder = "Filter";
searchBox.addEventListener("input", (e) => {
let filter = e.target.value.toLowerCase();
magazinePanelUl.querySelectorAll("li").forEach((li) => {
let a = li.querySelector("a");
if (a.innerText.toLowerCase().indexOf(filter) > -1) {
li.classList.remove("hideItem");
} else {
li.classList.add("hideItem");
}
});
});
magazinePanel.appendChild(searchBox);
/** Add settings button */
const settingsButton = document.createElement("div");
settingsButton.id = "subscription-panel-settings-button";
settingsButton.innerHTML = '<i class="fa-solid fa-cog"></i>';
settingsButton.addEventListener("click", () => {
showSettingsModal();
});
title.appendChild(settingsButton);
magazinePanel.appendChild(magazinePanelUl);
/** Add spinner */
const spinner = document.createElement("div");
spinner.id = "subscription-panel-spinner";
spinner.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
magazinePanel.appendChild(spinner);
/** Fetch subscription page */
const xhr = new XMLHttpRequest();
xhr.open("GET", "/settings/subscriptions/magazines", true);
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
spinner.remove();
if (this.status === 200) {
/** Parse the page */
let dom = new DOMParser().parseFromString(this.responseText, 'text/html');
let magazinesElements = dom.querySelectorAll(".section.magazines.magazines-columns ul>li");
let magazines = []
/** Find subscriptions */
magazinesElements.forEach((el) => {
let magA = el.querySelector("a")
let mag = {};
mag.fullName = magA.innerText;
const instanceName = mag.fullName.match(/@(.*)/);
mag.instanceName = instanceName ? instanceName[1] : undefined;
mag.name = mag.fullName.replace(/@(.*)/, "");
mag.url = magA.href;
mag.img = el.querySelector("figure img")?.src;
magazines.push(mag);
});
magazines.sort((a,b) => {
return a.name.localeCompare(b.name);
})
magazines.forEach(mag => {
/** Create the item dom element */
let li = document.createElement("li");
let a = document.createElement("a");
a.href = mag.url;
a.title = mag.fullName;
if (mag.img) {
let img = document.createElement("img");
img.src = mag.img;
a.appendChild(img);
} else {
/** Add some padding when there is no magazine image */
li.classList.add("no-image");
}
a.appendChild(document.createTextNode(mag.name));
if (mag.instanceName) {
let span = document.createElement("span");
span.className = "instance-name";
span.appendChild(document.createTextNode("@" + mag.instanceName));
a.appendChild(span);
}
li.appendChild(a);
magazinePanelUl.appendChild(li);
});
} else {
magazinePanel.appendChild(document.createTextNode("Failed to load subscriptions"));
}
}
};
xhr.send();
}
function showSettingsModal() {
const settings = getSettings();
const modal = document.createElement("div");
modal.className = "subscription-panel-settings-modal";
modal.innerHTML = `
<div class="subscription-panel-settings-modal-content">
<div class="subscription-panel-settings-modal-header">
<span class="close">
<i class="fa-solid fa-times"></i>
</span>
<h2>Subscription Panel Settings</h2>
</div>
<div class="subscription-panel-settings-modal-body">
<ul>
<li>
<label>
<input type="checkbox" id="subscription-panel-extend-width" />
Extend page width
<span class="description">Extend the page width to fit the subscription panel</span>
</label>
</li>
</div>
</div>
`;
document.body.appendChild(modal);
const extendWidthEl = modal.querySelector("#subscription-panel-extend-width");
if (settings?.extendWidth) {
extendWidthEl.checked = true;
}
modal.querySelector(".subscription-panel-settings-modal .close").addEventListener("click", () => {
modal.remove();
});
modal.addEventListener("click", (e) => {
if (e.target === modal) {
modal.remove();
}
});
modal.querySelector("#subscription-panel-extend-width").addEventListener("change", (e) => {
const settings = getSettings();
if (e.target.checked) {
settings.extendWidth = true;
} else {
settings.extendWidth = false;
}
saveSettings(settings);
applySettings();
});
}
function applySettings() {
const settings = getSettings();
if (settings?.extendWidth) {
document.body.classList.add("extend-width");
} else {
document.body.classList.remove("extend-width");
}
}
function getSettings() {
let settings = localStorage.getItem("subscription-panel-settings");
if (!settings) {
settings = {};
} else {
settings = JSON.parse(settings);
}
return settings;
}
function saveSettings(settings) {
localStorage.setItem("subscription-panel-settings", JSON.stringify(settings));
}
addSubscriptionsSidePanel();
applySettings();
})();