Discord Sidebar Toggle

7/17/2023, 9:38:40 AM

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         Discord Sidebar Toggle
// @namespace    Violentmonkey Scripts
// @match        https://discord.com/*
// @grant        none
// @version      1.1
// @author       Shaun Mitchell <[email protected]>
// @description  7/17/2023, 9:38:40 AM
// @license      WTFPL
// ==/UserScript==

var DEBUG = true;
var SIDEBARS_VISIBILITY_OPTION = 0;
const SIDEBARS_VISIBILITY_OPTIONS = ["auto", "hidden", "visible"];
const sidebarSelectors = [
  '#app-mount nav[aria-label="Servers sidebar"]',
  '#app-mount div[class^="sidebar"]'
];
const triggerWidth = 1130;

function debug(...args) {
  if (DEBUG) {
    const timestamp = new Date().toISOString();
    console.debug(`%c[discord.hidenavs | ${timestamp}]`, "color: green; font-weight: bold;", ...args);
  }
}

function shiftSidebarVisibility() {
  SIDEBARS_VISIBILITY_OPTION += 1;
  SIDEBARS_VISIBILITY_OPTION %= SIDEBARS_VISIBILITY_OPTIONS.length;
  let sidebarVisibilityOption = SIDEBARS_VISIBILITY_OPTIONS[SIDEBARS_VISIBILITY_OPTION];
  const sidebars = document.querySelectorAll(sidebarSelectors.join(", "));
  const sidebarToggleIconSVG = document.getElementById("toggle-sidebars-svg");

  if (sidebarVisibilityOption === "auto") {
    // Remove all classes from the sidebars
    sidebars.forEach((el) => {
      el.classList.remove("hidden");
      el.classList.remove("visible");
    });
    sidebarToggleIconSVG.style.stroke = "currentColor";
  } else if (sidebarVisibilityOption === "hidden") {
    hideElements(...sidebars);
    sidebarToggleIconSVG.style.stroke = "var(--channel-icon)";
  } else if (sidebarVisibilityOption === "visible") {
    showElements(...sidebars);
    sidebarToggleIconSVG.style.stroke = "#FFFFFF";
  }
}

function hideElements(...els) {
  els.forEach((el) => {
    el.classList.add("hidden");
    el.classList.remove("visible");
  });
}

function showElements(...els) {
  els.forEach((el) => {
    el.classList.remove("hidden");
    el.classList.add("visible");
  });
}

function toggleElementVisibility(...els) {
  els.forEach((el) => {
    let removeClass = "hidden";
    let addClass = "visible";

    if (
      (! (el.classList.contains("hidden") || el.classList.contains("visible")))
      || (el.classList.contains("hidden") && el.classList.contains("visible"))
    ) {
      // If the element contains neither of the "hidden" and "visible" classes,
      // or if it contains both classes, figure out which to use based on its width
      let width = el.getBoundingClientRect().width;
      if (width == 0) {
        // It's hidden, so show it
        showElements(el);
      } else {
        hideElements(el);
      }
    } else if (el.classList.contains("visible")) {
      hideElements(el);
    } else {
      showElements(el);
    }
  });
}

// Add CSS to the page to hide the side bars when the width is <=${triggerWidth}px
const style = document.createElement('style');
style.innerHTML = `
  ${sidebarSelectors.join(", ")} {
    transition: width 500ms ease-in-out;
  }

  .hidden {
    width: 0 !important;
  }

  @media (max-width: ${triggerWidth}px) {
      ${sidebarSelectors.map((el) => `${el}:not(.visible)`).join(", ")} {
        width: 0;
      }
  }
`;
document.head.appendChild(style);

/**
 * Toggle Menu Icon
**/

// Taken from Yong Wang @ StackOverflow:
// https://stackoverflow.com/a/61511955
function waitForElement(selector) {
    return new Promise(resolve => {
        if (document.querySelector(selector)) {
            return resolve(document.querySelector(selector));
        }

        const observer = new MutationObserver(mutations => {
            if (document.querySelector(selector)) {
                observer.disconnect();
                resolve(document.querySelector(selector));
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    });
}

// Insert the toggle sidebars menu icon into the toolbar
function insertToggleMenuToolbarIcon() {
  // Get the toolbar element
  let toolbar = document.querySelector('section[aria-label="Channel header"] > div > div[class^="toolbar"]');
  debug("got toolbar", toolbar);

  // Create the div wrapper for the SVG
  const iconWrapperSample = toolbar.querySelector('div[class^="iconWrapper"]');
  const sidebarToggleIconWrapper = iconWrapperSample.cloneNode(false);
  sidebarToggleIconWrapper.setAttribute("aria-label", "Toggle sidebars");
  sidebarToggleIconWrapper.setAttribute("id", "toggle-sidebars-wrapper");
  debug("got iconWrapperSample", iconWrapperSample);

  // Create the base SVG element
  const svgSample = iconWrapperSample.querySelector("svg");
  const sidebarToggleIconSVG = svgSample.cloneNode(false);
  sidebarToggleIconSVG.setAttribute("style", `
    stroke: currentColor;
    stroke-linecap: round;
    stroke-width: 2.4px;
  `);
  sidebarToggleIconSVG.setAttribute("id", "toggle-sidebars-svg");
  debug("got svgSample", svgSample);

  // Create its lines
  const pathSample = svgSample.querySelector("path");
  for (i = 0; i < 3; i++) {
    const path = pathSample.cloneNode(false);
    path.setAttribute("d", `M 4.8 ${6 + (6 * i)} L ${19.2 - (3.6 * i)} ${6 + (6 * i)}"`);
    sidebarToggleIconSVG.appendChild(path);
  }

  // Add the SVG to the wrapper
  sidebarToggleIconWrapper.appendChild(sidebarToggleIconSVG);

  // Add the toggle event listener to the wrapper
  sidebarToggleIconWrapper.addEventListener("click", shiftSidebarVisibility);

  toolbar.prepend(sidebarToggleIconWrapper);
  debug("added", sidebarToggleIconWrapper, "to", toolbar);

  // If the icon wrapper gets removed, re-add it
  const observer = new MutationObserver((mutations, observer) => {
    // Fetch the toolbar again and check if it contains the wrapper
    toolbar = document.querySelector('section[aria-label="Channel header"] > div > div[class^="toolbar"]');
    let sidebarToggleIconWrapperValidation = toolbar.querySelector("#toggle-sidebars-wrapper");

    if (! sidebarToggleIconWrapperValidation) {
      toolbar.prepend(sidebarToggleIconWrapper);
      debug("re-added", sidebarToggleIconWrapper, "to", toolbar);
    }
  });
  debug("set up toolbar mutation observer", observer, "on", toolbar.parentNode.parentNode.parentNode);
  observer.observe(toolbar.parentNode.parentNode.parentNode, {
    childList: true,
    subtree: true
  });
}

// Wait for the DOM and toolbar to load, then insert the toggle sidebar icon
document.addEventListener("DOMContentLoaded", function(event) {
  debug("DOM loaded, waiting for toolbar to load");

  waitForElement('section[aria-label="Channel header"] > div > div[class^="toolbar"]').then((el) => {
    debug("toolbar loaded, inserting toggle sidebar icon")
    insertToggleMenuToolbarIcon();
  })
});