This repository has been archived on 2026-02-28. You can view files and clone it, but cannot push or open issues or pull requests.
2025-12-14 02:47:17 +02:00

254 lines
9.7 KiB
JavaScript

(function () {
const BUTTON_ID = "rscord-settings-btn";
const CONTENT_CONTAINER_ID = "custom-content";
const SWITCH_CONTAINER_ID = "switch-container";
const CUSTOM_CSS_ID = "custom-css";
const CUSTOM_CSS_INPUT_ID = "custom-css-input";
const CONFIG_KEY = "rscord_config";
const DEFAULT_CONFIG = { customCss: "", revertStatusIcons: false };
const STATUS_COLOR_MAP = {
"hsl(38, calc(var(--saturation-factor, 1) * 95.7%), 54.1%)": "#FAA61A", // Idle (yellow)
"hsl(0, calc(var(--saturation-factor, 1) * 100%), 71.4%)": "#F04747", // DND (red)
"hsl(235, calc(var(--saturation-factor, 1) * 85.6%), 64.7%)": "#7289DA", // Away (blue)
"hsl(0, calc(var(--saturation-factor, 1) * 0%), 47.5%)": "#747F8D", // Offline (gray)
"hsl(139, calc(var(--saturation-factor, 1) * 47.3%), 43.9%)": "#43B581", // Online (green)
};
const IS_MOBILE = window
.matchMedia("(pointer: coarse)")
.matches;
const TAB_CONTENT = `
<h2 class="${CSS_CLASSES.title}">RSCord Settings</h2>
<h5 class="${CSS_CLASSES.miniTitle}" style="margin-bottom: 10px">Options</h5>
<div id="${SWITCH_CONTAINER_ID}"></div>
<h5 class="${CSS_CLASSES.miniTitle}" style="margin-bottom: 10px">Custom CSS</h5>
<textarea id="${CUSTOM_CSS_INPUT_ID}" class="${CSS_CLASSES.textInput}" placeholder="CSS goes here" type="text" name="" style="resize: vertical;height: 400px;"></textarea>
`;
function loadConfig() {
try {
return {
...DEFAULT_CONFIG,
...JSON.parse(localStorage_.getItem(CONFIG_KEY) || "{}")
};
} catch {
return { ...DEFAULT_CONFIG };
}
}
function saveConfig(patch) {
const config = loadConfig();
const updated = { ...config, ...patch };
localStorage_.setItem(CONFIG_KEY, JSON.stringify(updated));
return updated;
}
// this is a shitty patch
// you can just grab this shit from the req but i do not care
function addJoinDates() {
if (document.getElementById("join-date")) return;
const noteTextArea = document.querySelector(".userInfoSection-2u2hir > :has(+ .note-3M15gE)") ?? document.querySelector(".bodyInnerWrapper-2bQs1k > h3");
const nitroBadge = document.querySelector(".clickable-1knRMS[aria-label]");
if (!noteTextArea || !nitroBadge) return;
const nitroText = nitroBadge.getAttribute("aria-label");
if (!nitroText) return;
const div1 = document.createElement("div");
div1.id = "join-date";
div1.className = CSS_CLASSES.profileHeader;
div1.textContent = "Account since";
const div2 = document.createElement("div");
div2.id = "join-date";
div2.className = CSS_CLASSES.profileText;
div2.textContent = nitroText.replace("Subscriber since ", "");
noteTextArea.insertAdjacentElement("beforebegin", div1);
div1.insertAdjacentElement("afterend", div2);
}
async function addSwitches(container) {
const config = loadConfig();
container.appendChild(createSwitch(
"Push Notifications",
"Sends you notifications even when you close the app",
"push-notifications",
await isPushRegistered(),
async (checked) => {
const publicKey = window.GLOBAL_ENV.VAPID_KEY;
if (!publicKey) return false;
try {
checked ? await registerPush(publicKey) : await unregisterPush();
return true;
} catch {
return false;
}
}
));
container.appendChild(createSwitch(
"Revert Status Icons",
"Revert the colorblind status icons added in 2019",
"revert-status",
config.revertStatusIcons,
(checked) => saveConfig({ revertStatusIcons: checked })
));
}
function revertStatusIcons() {
const statusRects = document.querySelectorAll("rect[fill^='hsl']");
for (const rect of statusRects) {
const fill = rect.getAttribute("fill");
if (STATUS_COLOR_MAP[fill]) {
rect.setAttribute("fill", STATUS_COLOR_MAP[fill]);
}
const maskRef = rect.getAttribute("mask");
if (!maskRef) continue;
const maskId = maskRef.match(/#([^)]+)/)?.[1];
if (!maskId) continue;
const mask = document.getElementById(maskId);
if (!mask) continue;
mask.querySelectorAll("[fill='black']").forEach(el => el.remove());
}
}
function applyCustomCss() {
const { customCss } = loadConfig();
let style = document.getElementById(CUSTOM_CSS_ID);
if (!style) {
style = document.createElement("style");
style.id = CUSTOM_CSS_ID;
document.head.appendChild(style);
}
style.textContent = customCss;
}
function setupCssInput(textarea) {
if (!textarea) return;
const config = loadConfig();
textarea.value = config.customCss;
const style = document.getElementById(CUSTOM_CSS_ID);
if (!style) return;
textarea.addEventListener("input", () => {
style.textContent = textarea.value;
saveConfig({ customCss: textarea.value });
});
}
function addSettingsTab() {
const advancedTab = document.querySelector(`.${CSS_CLASSES.tabItem}[aria-controls="advanced-tab"]`);
if (!advancedTab) return;
if (document.getElementById(BUTTON_ID)) return;
const settingsBtn = document.createElement("div");
settingsBtn.id = BUTTON_ID;
settingsBtn.className = advancedTab.className;
settingsBtn.role = "tab";
settingsBtn.tabIndex = -1;
settingsBtn.ariaSelected = "false";
settingsBtn.textContent = "RSCord Settings";
settingsBtn.addEventListener("click", () => {
const tabs = settingsBtn.parentElement.querySelectorAll("[role='tab']");
tabs.forEach(tab => tab.classList.remove(CSS_CLASSES.selected));
tabs.forEach(tab => tab.setAttribute("aria-selected", "false"));
settingsBtn.classList.add(CSS_CLASSES.selected);
settingsBtn.setAttribute("aria-selected", "true");
let customContent = document.getElementById(CONTENT_CONTAINER_ID);
if (!customContent) {
customContent = document.createElement("div");
customContent.id = CONTENT_CONTAINER_ID;
customContent.innerHTML = TAB_CONTENT;
const switchContainer = customContent.querySelector(`#${SWITCH_CONTAINER_ID}`);
addSwitches(switchContainer);
document.querySelector(`.${CSS_CLASSES.contentColumn}`).appendChild(customContent);
const cssInput = document.getElementById(CUSTOM_CSS_INPUT_ID);
setupCssInput(cssInput);
}
const contentColumn = document.querySelector(`.${CSS_CLASSES.contentColumn}`);
Array.from(contentColumn.children).forEach(child => {
if (child.id !== CONTENT_CONTAINER_ID) child.style.display = "none";
});
customContent.style.display = "block";
});
advancedTab.insertAdjacentElement("afterend", settingsBtn);
return true;
}
const observer = new MutationObserver(() => {
if (document.querySelector(`.${CSS_CLASSES.settingsSidebar}`)) addSettingsTab();
if (loadConfig().revertStatusIcons) revertStatusIcons();
addJoinDates();
});
observer.observe(document.body, { childList: true, subtree: true });
applyCustomCss();
document.body.addEventListener("click", (e) => {
const clickedHamburger = e.target.closest(`.${CSS_CLASSES.hamburger}`)
if (clickedHamburger && IS_MOBILE) {
const settingsRegion = document.querySelector(`.${CSS_CLASSES.settingsRegion}`)
settingsRegion.classList.remove(CSS_CLASSES.hidden);
const contentRegion = document.querySelector(`.${CSS_CLASSES.contentRegion}`)
contentRegion.classList.add(CSS_CLASSES.hidden);
}
const clickedTab = e.target.closest("[role='tab']");
if (!clickedTab) return;
const allTabs = document.querySelectorAll("[role='tab']");
allTabs.forEach(tab => tab.classList.remove(CSS_CLASSES.selected));
allTabs.forEach(tab => tab.setAttribute("aria-selected", "false"));
clickedTab.classList.add(CSS_CLASSES.selected);
clickedTab.setAttribute("aria-selected", "true");
const contentColumn = document.querySelector(`.${CSS_CLASSES.contentColumn}`);
if (!contentColumn) return;
if (clickedTab.id === BUTTON_ID) {
Array.from(contentColumn.children).forEach(child => {
if (child.id === CONTENT_CONTAINER_ID) {
child.style.display = "block";
} else {
child.style.display = "none";
}
if (IS_MOBILE) {
const settingsRegion = document.querySelector(`.${CSS_CLASSES.settingsRegion}`)
settingsRegion.classList.add(CSS_CLASSES.hidden);
const contentRegion = document.querySelector(`.${CSS_CLASSES.contentRegion}`)
contentRegion.classList.remove(CSS_CLASSES.hidden);
}
});
} else {
Array.from(contentColumn.children).forEach(child => {
if (child.id !== CONTENT_CONTAINER_ID) child.style.display = "";
else child.style.display = "none";
});
}
});
})();