From 0c318e1f217c60e694ab895f73a13580f77c91e6 Mon Sep 17 00:00:00 2001 From: Send_Nukez Date: Fri, 12 Nov 2021 17:57:19 +0100 Subject: [PATCH] Add Info class to show buttons / text next to the user icon (fixes #108) --- CHANGELOG.md | 3 +- src/js/ConfigMenu.js | 2 +- src/js/Info.js | 63 ++++++++++++++++++++++++++++++++++++++++++ src/js/Util.js | 20 +++++++++++--- src/js/main.js | 49 ++++++++++---------------------- src/styles/Info.scss | 38 +++++++++++++++++++++++++ src/styles/main.scss | 32 ++++++--------------- src/svg/arrow-down.svg | 3 ++ src/svg/code.svg | 3 ++ src/svg/undo.svg | 2 +- 10 files changed, 150 insertions(+), 65 deletions(-) create mode 100644 src/js/Info.js create mode 100644 src/styles/Info.scss create mode 100644 src/svg/arrow-down.svg create mode 100644 src/svg/code.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 19f8cc5..b973901 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,4 +9,5 @@ Fixed: Improved: - Settings that have been changed from default will now show a line next to them. Inspired by the [Visual Studio Code settings UI](https://d33wubrfki0l68.cloudfront.net/d1f1ea4def506997ced23d3d912154794e530e1c/063d2/assets/img/blog/2020-09-17-vscode-settings/settings-ui.png) -- Checkbox / Switch input styles are now more in line with other input styles \ No newline at end of file +- Checkbox / Switch input styles are now more in line with other input styles +- Available updates are noe shown as clickable button next to your user icon instead of having to open the user menu \ No newline at end of file diff --git a/src/js/ConfigMenu.js b/src/js/ConfigMenu.js index d39a377..7b15b42 100644 --- a/src/js/ConfigMenu.js +++ b/src/js/ConfigMenu.js @@ -2,7 +2,7 @@ import $ from "jquery"; import MarkdownIt from "markdown-it"; import MarkdownItAttrs from "markdown-it-attrs"; -import svgUndo from "../svg/undo.svg"; +import svgUndo from "svg/undo"; export default class ConfigMenu { /** diff --git a/src/js/Info.js b/src/js/Info.js new file mode 100644 index 0000000..9d9a470 --- /dev/null +++ b/src/js/Info.js @@ -0,0 +1,63 @@ +import $ from "jquery"; +import MarkdownIt from "markdown-it"; +import MarkdownItAttrs from "markdown-it-attrs"; + +import { waitForElement } from "./Util"; + +export default class Info { + /** + * @typedef {Object} DribbblishInfo + * @property {String} [text] + * @property {String} [tooltip] + * @property {String} [icon] + * @property {onClick} [onClick] + */ + + /** + * @callback onClick + * @returns {void} + */ + + /** @type {HTMLDivElement} */ + #container; + + constructor() { + this.md = MarkdownIt({ + html: true, + breaks: true, + linkify: true, + typographer: true + }); + this.md.use(MarkdownItAttrs); + + waitForElement([".main-topBar-container", ".main-userWidget-box"], ([topBarContainer, userWidget]) => { + this.#container = document.createElement("div"); + this.#container.id = "dribbblish-info-container"; + topBarContainer.insertBefore(this.#container, userWidget); + }); + } + + /** + * @param {String} key + * @param {DribbblishInfo} info + */ + set(key, info) { + this.remove(key); + if (info.text == null && info.icon == null) throw new Error("invalid info"); + + const elem = document.createElement("div"); + elem.classList.add("dribbblish-info-item"); + elem.addEventListener("click", info.onClick); + elem.setAttribute("key", key); + if (info.text == null) elem.setAttribute("icon-only", ""); + if (info.tooltip != null) elem.setAttribute("title", info.tooltip); + if (info.onClick != null) elem.setAttribute("clickable", ""); + elem.innerHTML = `${this.md.render(info.text ?? "")}${info.icon ?? ""}`; + + this.#container.appendChild(elem); + } + + remove(key) { + $(this.#container).find(`[key="${key}"]`).remove(); + } +} diff --git a/src/js/Util.js b/src/js/Util.js index e6e4639..5d8a6cf 100644 --- a/src/js/Util.js +++ b/src/js/Util.js @@ -1,9 +1,21 @@ -export function waitForElement(els, func, timeout = 100) { +/** + * @callback waitForElCb + * @param {HTMLElement[]} queries + */ + +/** + * + * @param {String[]} els + * @param {waitForElCb} cb + * @param {Number} [tries=100] + * @param {Number} [interval=300] + */ +export function waitForElement(els, cb, tries = 100, interval = 300) { const queries = els.map((el) => document.querySelector(el)); if (queries.every((a) => a)) { - func(queries); - } else if (timeout > 0) { - setTimeout(waitForElement, 300, els, func, --timeout); + cb(queries); + } else if (tries > 0) { + setTimeout(waitForElement, interval, els, cb, --tries); } } diff --git a/src/js/main.js b/src/js/main.js index 81a4237..56e1f4e 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -5,13 +5,15 @@ import moment from "moment"; import { waitForElement, copyToClipboard, capitalizeFirstLetter } from "./Util"; import ConfigMenu from "./ConfigMenu"; +import Info from "./Info"; -class _DribbblishShared { - constructor() { - this.config = new ConfigMenu(); - } -} -const DribbblishShared = new _DribbblishShared(); +import svgArrowDown from "svg/arrow-down"; +import svgCode from "svg/code"; + +const DribbblishShared = { + config: new ConfigMenu(), + info: new Info() +}; DribbblishShared.config.register({ type: "checkbox", @@ -741,39 +743,16 @@ function registerCoverListener() { registerCoverListener(); // Check latest release every 10m -waitForElement([".main-userWidget-box"], ([userWidget]) => { +waitForElement([".main-topBar-container"], ([topBarContainer]) => { function checkForUpdate() { fetch("https://api.github.com/repos/JulienMaille/dribbblish-dynamic-theme/releases/latest") - .then((response) => { - return response.json(); - }) + .then((response) => response.json()) .then((data) => { - let upd; - if (!document.getElementById("dribbblish-update")) { - upd = document.createElement("div"); - upd.id = "dribbblish-update"; - //upd.setAttribute("title", `Changes: ${data.name}`); - userWidget.append(upd); - } else { - upd = document.getElementById("dribbblish-update"); - } - - upd.style.display = "block"; - userWidget.classList.add("update-avail"); - if (process.env.DRIBBBLISH_VERSION == "Dev") { - upd.innerText = "Dev version!"; - } else if (data.tag_name > process.env.DRIBBBLISH_VERSION) { - upd.innerText = `Theme UPD v${data.tag_name} avail.`; - new Spicetify.Menu.Item("Update Dribbblish", false, () => window.open("https://github.com/JulienMaille/dribbblish-dynamic-theme/releases/latest", "_blank")).register(); - } else { - userWidget.classList.remove("update-avail"); - upd.style.display = "none"; - } + const isDev = process.env.DRIBBBLISH_VERSION == "Dev"; + DribbblishShared.info.set("upd", isDev || data.tag_name > process.env.DRIBBBLISH_VERSION ? { text: `v${data.tag_name}`, tooltip: "Open Release page to download", icon: svgArrowDown, onClick: () => window.open("https://github.com/JulienMaille/dribbblish-dynamic-theme/releases/latest", "_blank") } : null); + DribbblishShared.info.set("dev", isDev ? { tooltip: "Dev build", icon: svgCode } : null); }) - .catch((err) => { - // Do something for an error here - console.error(err); - }); + .catch(console.error); } setInterval(checkForUpdate(), 10 * 60 * 1000); diff --git a/src/styles/Info.scss b/src/styles/Info.scss new file mode 100644 index 0000000..7b77af3 --- /dev/null +++ b/src/styles/Info.scss @@ -0,0 +1,38 @@ +#dribbblish-info-container { + display: flex; + gap: 8px; + margin-right: 8px; + height: 100%; + + &:empty { + display: none; + } + + & > div { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + height: 100%; + padding: 0px 10px; + color: spiceColor("sidebar-text"); + background-color: spiceColor("button"); + border-radius: var(--sidebar-icons-border-radius); + line-height: 13px; + @include spiceFont("glue", 14px); + + &[clickable] { + cursor: pointer; + } + + &[icon-only] { + padding: 0px; + aspect-ratio: 1/1; + } + + & svg { + width: 1em; + height: 1em; + } + } +} diff --git a/src/styles/main.scss b/src/styles/main.scss index f7da074..80cb2e4 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -10,6 +10,7 @@ @import "ContextMenu.scss"; @import "NoAds"; @import "Markdown"; +@import "Info"; :root { --bar-height: 70px; @@ -273,17 +274,8 @@ span.artist-artistVerifiedBadge-badge svg > path:last-of-type { .main-userWidget-box { background-color: transparent !important; -} - -.main-userWidget-box.update-avail { - padding-right: 10px; - background-color: spiceColor("subtext", 0.1) !important; - - &:hover, - &:active, - &[data-context-menu-open="true"] { - background-color: spiceColor("subtext", 0.15) !important; - } + padding: 0px; + margin: 0px; } .main-avatar-avatar.main-avatar-withBadge:after { @@ -769,18 +761,6 @@ li.GlueDropTarget { pointer-events: none; } -#dribbblish-update { - color: spiceColor("button-active"); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 12px; - font-weight: 400; - letter-spacing: normal; - line-height: 16px; - text-transform: none; -} - /** Rearrange player bar */ .main-nowPlayingBar-left { order: 1; @@ -1254,3 +1234,9 @@ canvas[width="250"][height="250"] { .main-trackInfo-container > div > div:after { display: none; } + +// workaround to re-center track-info +#main:not([player-controls="spotify"]) .main-trackInfo-container > div > div > div { + display: flex; + justify-content: center; +} diff --git a/src/svg/arrow-down.svg b/src/svg/arrow-down.svg new file mode 100644 index 0000000..2de2e8f --- /dev/null +++ b/src/svg/arrow-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/svg/code.svg b/src/svg/code.svg new file mode 100644 index 0000000..4fbffd1 --- /dev/null +++ b/src/svg/code.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/svg/undo.svg b/src/svg/undo.svg index 7397f55..7276eb1 100644 --- a/src/svg/undo.svg +++ b/src/svg/undo.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file