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 @@
-