From 3273362bd501cc029d22c7903fb8fd9d6eda073c Mon Sep 17 00:00:00 2001 From: murdle Date: Fri, 12 Dec 2025 20:09:03 +0200 Subject: [PATCH] add custom css support and fix push bugs --- assets/public/custom/pushMessages.js | 55 ++++--- assets/public/custom/utils.js | 36 ++++- assets/public/custom/web/main.js | 74 ++++++++- assets/public/custom/web/switch.js | 39 +++-- assets/public/index.html | 6 + .../3c62db47bb5d5e184a4077a273594c4b.svg | 146 ++++++++++++++++++ 6 files changed, 305 insertions(+), 51 deletions(-) create mode 100644 assets/public/thirdparty/3c62db47bb5d5e184a4077a273594c4b.svg diff --git a/assets/public/custom/pushMessages.js b/assets/public/custom/pushMessages.js index 15b4e346..5afc54e1 100644 --- a/assets/public/custom/pushMessages.js +++ b/assets/public/custom/pushMessages.js @@ -3,21 +3,46 @@ function checkServiceWorkerSupport() { throw new Error("Your browser does not have Service Worker support") } +async function isPushRegistered() { + checkServiceWorkerSupport(); + try { + const registrations = await navigator.serviceWorker.getRegistrations(); + if (!registrations?.length) return false; + + return await Promise.any( + registrations.map(async (reg) => !!(await reg.pushManager.getSubscription())) + ).catch(() => false); + } catch { + return false; + } +} + async function unregisterPush() { checkServiceWorkerSupport(); + try { + const registrations = await navigator.serviceWorker.getRegistrations(); + if (!registrations?.length) return; - const registrations = await navigator.serviceWorker.getRegistrations(); - - for (const reg of registrations) { - const sub = await reg.pushManager.getSubscription(); - if (sub) await sub.unsubscribe(); - await reg.unregister(); + await Promise.all( + registrations.map(async (reg) => { + const sub = await reg.pushManager.getSubscription(); + if (sub) await sub.unsubscribe(); + await reg.unregister(); + }) + ); + } catch (err) { + console.error("Failed to unregister push:", err); } } async function registerPush(publicKey) { checkServiceWorkerSupport(); + const permission = await Notification.requestPermission(); + if (permission !== "granted") { + throw new Error("Notification permission not granted"); + } + const registration = await navigator.serviceWorker.register("/assets/custom/serviceWorker.js"); console.log("Service Worker registered"); @@ -36,7 +61,7 @@ async function registerPush(publicKey) { method: "POST", headers: { "Content-Type": "application/json", - "Authorization": getToken() + "Authorization": window.AUTH_TOKEN }, body: JSON.stringify({ provider: "webpush", @@ -50,20 +75,4 @@ async function registerPush(publicKey) { const text = await response.text(); throw new Error("Failed to register: ", text) } -} - -async function isPushRegistered() { - checkServiceWorkerSupport(); - - const registrations = await navigator.serviceWorker.getRegistrations(); - if (!registrations || registrations.length === 0) { - return false; - } - - for (const reg of registrations) { - const sub = await reg.pushManager.getSubscription(); - if (sub) return true; - } - - return false; } \ No newline at end of file diff --git a/assets/public/custom/utils.js b/assets/public/custom/utils.js index 74004a16..ca28bc74 100644 --- a/assets/public/custom/utils.js +++ b/assets/public/custom/utils.js @@ -1,9 +1,3 @@ -let authToken = null; - -function getToken() { - return authToken; -} - function urlBase64ToUint8Array(base64String) { const padding = "=".repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding) @@ -23,7 +17,33 @@ function urlBase64ToUint8Array(base64String) { let proxied = window.XMLHttpRequest.prototype.setRequestHeader; window.XMLHttpRequest.prototype.setRequestHeader = function() { if (arguments[0] === "Authorization") { - authToken = arguments[1]; + window.AUTH_TOKEN = arguments[1]; } return proxied.apply(this, [].slice.call(arguments)); -}; \ No newline at end of file +}; + +window.CSS_CLASSES = { + container: "container-1zDvAE", + labelRow: "labelRow-2jl9gK", + label: "title-2dsDLn", + control: "control-1fl03-", + switchControl: "container-2nx-BQ", + switchControlChecked: "checked-25WXMf", + input: "input-2XRLou", + slider: "slider-32CRPX", + note: "note-2C4pGr", + description: "colorStandard-1Xxp1s size14-k_3Hy4 description-30xx7u formText-2ngGjI modeDefault-2fEh7a", + divider: "divider-_0um2u dividerDefault-3C2-ws", + tabItem: "item-3XjbnG", + selected: "selected-g-kMVV", + contentColumn: "contentColumn-1C7as6", + settingsRegion: "sidebarRegion-1VBisG", + contentRegion: "contentRegion-3HkfJJ", + settingsSidebar: "standardSidebarView-E9Pc3j", + title: "colorStandard-1Xxp1s size14-k_3Hy4 h1-34Txb0 title-3hptVQ defaultColor-2cKwKo defaultMarginh1-EURXsm", + hidden: "hidden-27eifz", + hamburger: "btnHamburger-3GF0_5", +}; + +window.AUTH_TOKEN = null; +window.localStorage_ = localStorage; \ No newline at end of file diff --git a/assets/public/custom/web/main.js b/assets/public/custom/web/main.js index 17e40fd5..1e6147c7 100644 --- a/assets/public/custom/web/main.js +++ b/assets/public/custom/web/main.js @@ -2,18 +2,35 @@ 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_STORAGE = "custom_css"; + const CUSTOM_CSS_INPUT_ID = "custom-css-input"; const CSS_CLASSES = { tabItem: "item-3XjbnG", selected: "selected-g-kMVV", contentColumn: "contentColumn-1C7as6", + settingsRegion: "sidebarRegion-1VBisG", + contentRegion: "contentRegion-3HkfJJ", settingsSidebar: "standardSidebarView-E9Pc3j", - title: "colorStandard-1Xxp1s size14-k_3Hy4 h1-34Txb0 title-3hptVQ defaultColor-2cKwKo defaultMarginh1-EURXsm" + title: "colorStandard-1Xxp1s size14-k_3Hy4 h1-34Txb0 title-3hptVQ defaultColor-2cKwKo defaultMarginh1-EURXsm", + miniTitle: "colorStandard-1Xxp1s size14-k_3Hy4 h5-2RwDNl title-3hptVQ title-1HgbhV", + hidden: "hidden-27eifz", + hamburger: "btnHamburger-3GF0_5", + textInput: "inputDefault-3FGxgL input-2g-os5" }; + const IS_MOBILE = window + .matchMedia("(pointer: coarse)") + .matches; + const TAB_CONTENT = `

RSCord Settings

+
Options
+
Custom CSS
+ `; async function addSwitches(container) { @@ -39,6 +56,33 @@ )); } + function applySavedCSS() { + let css = localStorage_.getItem(CUSTOM_CSS_STORAGE) || ""; + let style = document.getElementById(CUSTOM_CSS_ID); + + if (!style) { + style = document.createElement("style"); + style.id = CUSTOM_CSS_ID; + document.head.appendChild(style); + } + + style.textContent = css; + } + + function loadCustomCSS(textarea) { + if (!textarea) return; + textarea.value = localStorage_.getItem(CUSTOM_CSS_STORAGE) || ""; + + let style = document.getElementById(CUSTOM_CSS_ID); + if (!style) return; + + textarea.addEventListener("input", () => { + const css = textarea.value; + style.textContent = css; + localStorage_.setItem(CUSTOM_CSS_STORAGE, css); + }); + } + function addSettingsTab() { const advancedTab = document.querySelector(`.${CSS_CLASSES.tabItem}[aria-controls='advanced-tab']`); if (!advancedTab) return; @@ -69,6 +113,9 @@ addSwitches(switchContainer); document.querySelector(`.${CSS_CLASSES.contentColumn}`).appendChild(customContent); + + const cssInput = document.getElementById(CUSTOM_CSS_INPUT_ID); + loadCustomCSS(cssInput); } const contentColumn = document.querySelector(`.${CSS_CLASSES.contentColumn}`); @@ -91,8 +138,18 @@ } startObserver(); + applySavedCSS(); 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; @@ -107,8 +164,19 @@ 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 (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 => { diff --git a/assets/public/custom/web/switch.js b/assets/public/custom/web/switch.js index 2ab7695d..acaa8117 100644 --- a/assets/public/custom/web/switch.js +++ b/assets/public/custom/web/switch.js @@ -1,17 +1,3 @@ -const CSS_CLASSES = { - container: "container-1zDvAE", - labelRow: "labelRow-2jl9gK", - label: "title-2dsDLn", - control: "control-1fl03-", - switchControl: "container-2nx-BQ", - switchControlChecked: "checked-25WXMf", - input: "input-2XRLou", - slider: "slider-32CRPX", - note: "note-2C4pGr", - description: "colorStandard-1Xxp1s size14-k_3Hy4 description-30xx7u formText-2ngGjI modeDefault-2fEh7a", - divider: "divider-_0um2u dividerDefault-3C2-ws" -}; - const ICON_PATHS = { checked: [ "M7.89561 14.8538L6.30462 13.2629L14.3099 5.25755L15.9009 6.84854L7.89561 14.8538Z", @@ -33,6 +19,8 @@ function createSwitch( defaultChecked = false, onToggle = null ) { + let isToggling = false; + const container = document.createElement("div"); container.className = CSS_CLASSES.container; @@ -93,16 +81,29 @@ function createSwitch( }; const handleToggle = async () => { + if (isToggling) return; + isToggling = true; + const prev = !checkbox.checked; + if (typeof onToggle === "function") { - const ret = await onToggle(checkbox.checked); - if (ret === false) checkbox.checked = prev; + try { + const ret = await onToggle(checkbox.checked); + if (ret === false) checkbox.checked = prev; + } finally { + isToggling = false; + } + } else { + isToggling = false; } + updateSwitch(checkbox.checked); }; switchControl.addEventListener("click", (e) => { + if (isToggling) return; if (e.target === checkbox) return; + e.preventDefault(); e.stopPropagation(); checkbox.checked = !checkbox.checked; @@ -110,7 +111,11 @@ function createSwitch( }); checkbox.addEventListener("click", (e) => { - e.stopPropagation(); + if (isToggling) { + e.stopPropagation(); + e.preventDefault(); + return; + } }); checkbox.addEventListener("change", handleToggle); diff --git a/assets/public/index.html b/assets/public/index.html index 9f60e1e7..d3c8e8dd 100644 --- a/assets/public/index.html +++ b/assets/public/index.html @@ -16,6 +16,12 @@ Discord + + + + + + diff --git a/assets/public/thirdparty/3c62db47bb5d5e184a4077a273594c4b.svg b/assets/public/thirdparty/3c62db47bb5d5e184a4077a273594c4b.svg new file mode 100644 index 00000000..1752ad88 --- /dev/null +++ b/assets/public/thirdparty/3c62db47bb5d5e184a4077a273594c4b.svg @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file