From 82234f9706532324d4f5a1802f42d7b5e4bcee0e Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Tue, 8 Apr 2025 14:45:18 +1200 Subject: [PATCH] Add settings for currency and inline player --- public/player-marker.js | 8 ++++++++ public/style.css | 3 +++ pug/album_grid.pug | 2 +- pug/artist_grid.pug | 2 +- pug/home.pug | 2 +- pug/includes/layout.pug | 35 +++++++++++++++++++++++++++++++---- pug/label_grid.pug | 2 +- routes/app.js | 36 +++++++++++++++++++++++++++++------- routes/settings.js | 28 ++++++++++++++++++++++++++++ start.js | 1 + 10 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 routes/settings.js diff --git a/public/player-marker.js b/public/player-marker.js index bcc6716..bd4b30b 100644 --- a/public/player-marker.js +++ b/public/player-marker.js @@ -9,3 +9,11 @@ function movePlayer() { } movePlayer() document.body.addEventListener("htmx:load", movePlayer) + +document.querySelectorAll("[popovertarget]").forEach(e => { + e.addEventListener("click", () => { + const rect = e.getBoundingClientRect() + const t = `:popover-open { position: fixed; top: ${Math.floor(rect.bottom)}px; left: ${Math.floor(rect.left + rect.width / 2)}px; width: ${Math.floor(rect.width + 85)}px; transform: translateX(-50%); margin: 0 }` + document.styleSheets[0].insertRule(t, document.styleSheets[0].cssRules.length) + }) +}) diff --git a/public/style.css b/public/style.css index e6fd506..57af210 100644 --- a/public/style.css +++ b/public/style.css @@ -45,6 +45,9 @@ svg { button.s-link.is-loading { padding-left: 2.2em; } +.s-btn__dropdown:has(+ :popover-open) { + background-color: var(--theme-topbar-item-background-hover, var(--black-200)) !important; +} /* album covers are done with styles instead of attributes to reduce bytes of html needing to be downloaded and parsed */ .cover { diff --git a/pug/album_grid.pug b/pug/album_grid.pug index 3141145..4c01bb0 100644 --- a/pug/album_grid.pug +++ b/pug/album_grid.pug @@ -9,7 +9,7 @@ block view .d-grid.gx8.gy12.jc-center.break-word(style="grid-template-columns: repeat(auto-fit, 210px)") each item in items div - a.cover(href=item.item_url hx-get=`/api/play/${item.item_type}/${item.item_id}` hx-target="#player" hx-select="#player" hx-indicator="null" hx-push-url="false") + a.cover&attributes(getAlbumCoverAttributes(event, item)) picture img(loading="lazy" src=item.item_art_url width=210 height=210) != icons.use("play-solid", 64) diff --git a/pug/artist_grid.pug b/pug/artist_grid.pug index a0a73a1..1461da3 100644 --- a/pug/artist_grid.pug +++ b/pug/artist_grid.pug @@ -28,5 +28,5 @@ block view span.s-tag--sponsor!= icons.use("flower", 16) = label each preview in item.previews - a.d-flex(href=preview.item_url hx-get=`/api/play/${preview.item_type}/${preview.item_id}` hx-target="#player" hx-select="#player" hx-indicator="null" hx-push-url="false") + a.d-flex&attributes(getAlbumCoverAttributes(event, preview)) img(loading="lazy" src=preview.item_art_url width=210 height=210 style="height: auto; width: auto; max-height: 70px") diff --git a/pug/home.pug b/pug/home.pug index c3e7102..c34b716 100644 --- a/pug/home.pug +++ b/pug/home.pug @@ -13,7 +13,7 @@ html .s-topbar--container.wmx9 .s-topbar--logo != icons.get("compass-solid", 24) - .ml4 BC Explorer + .ml6 BC Explorer .mx-auto.wmx9.py24.px16.g24 .s-prose diff --git a/pug/includes/layout.pug b/pug/includes/layout.pug index edf5a3e..032479e 100644 --- a/pug/includes/layout.pug +++ b/pug/includes/layout.pug @@ -29,9 +29,36 @@ html #page(hx-history-elt) header.s-topbar.ps-sticky.t0 .s-topbar--container.wmx9 - .s-topbar--logo + button.s-topbar--logo.s-btn.s-btn__muted.s-btn__dropdown.pr24.bar0.fc-black(popovertarget="menu") != icons.get("compass-solid", 24) - .ml4 BC Explorer + .ml6 BC Explorer + #menu(popover data-popper-placement="bottom" style="display: revert;").s-popover.overflow-visible.px0.py8 + .s-popover--arrow.s-popover--arrow__tc + ul.s-menu(role="menu") + li(role="menuitem") + a.s-block-link.d-flex.ai-center.fc-black(href="/" hx-boost="false") + != icons.get("log-out") + .ml6 Switch account + li.s-menu--divider(role="separator") + li.s-menu--title Settings + li(role="menuitem") + form.s-block-link.d-flex.ai-center(hx-post="/api/settings/inline-player" hx-trigger="change" hx-indicator="#inline-player-loading") + label.fl-grow1.d-flex.ai-center + != icons.get("layout-right") + .pl6(for="inline-player-switch") Inline player + #inline-player-loading + input.s-toggle-switch#inline-player-switch(type="checkbox" name="inline_player" checked=(h3.getCookie(event, "bcex-inline-player-disabled") !== "true")) + li(role="menuitem") + form.s-block-link.d-flex.ai-center(hx-post="/api/settings/currency" hx-trigger="change" hx-select="#collection-stats" hx-target="#collection-stats" hx-indicator="#currency-loading") + label.fl-grow1(for="currency-select").d-flex.ai-center + != icons.get("coins") + .pl6 Currency + #currency-loading + input(type="hidden" name="account" value=account) + .s-select + select#currency-select(name="currency") + each currency in currencies + option(selected=(currency === count.displayCurrency))= currency .fl-grow1 nav ul.s-navigation @@ -79,7 +106,7 @@ html #collection-sync.d-none - .s-sidebarwidget + #collection-stats.s-sidebarwidget .s-sidebarwidget--header Collection .s-sidebarwidget--action input(type="hidden" name="account" value=account) @@ -125,7 +152,7 @@ html tr.s-sidebarwidget--item th value td - = `${count.displayCurrencySymbol}${count.value} ` + = `${count.value} ` span.fc-black-400 #{count.displayCurrency} tr.s-sidebarwidget--item th diversity diff --git a/pug/label_grid.pug b/pug/label_grid.pug index 887dbd4..cc8b58a 100644 --- a/pug/label_grid.pug +++ b/pug/label_grid.pug @@ -28,5 +28,5 @@ block view span.s-tag--sponsor!= icons.use("compact-disc", 16) = item.total_duration each preview in item.previews - a.d-flex(href=preview.item_url hx-get=`/api/play/${preview.item_type}/${preview.item_id}` hx-target="#player" hx-select="#player" hx-indicator="null" hx-push-url="false") + a.d-flex&attributes(getAlbumCoverAttributes(event, preview)) img(loading="lazy" src=preview.item_art_url width=210 height=210 style="height: auto; width: auto; max-height: 70px") diff --git a/routes/app.js b/routes/app.js index f2288f8..1a0a6f8 100644 --- a/routes/app.js +++ b/routes/app.js @@ -1,17 +1,16 @@ // @ts-check const {z} = require("zod") -const {defineEventHandler, getQuery, getValidatedQuery, sendRedirect, createError, getValidatedRouterParams} = require("h3") +const {defineEventHandler, getQuery, getValidatedQuery, sendRedirect, createError, getValidatedRouterParams, getCookie} = require("h3") const {router, db, sync, select, from} = require("../passthrough") const pugSync = sync.require("../pug-sync") /** @type {import("./load-tags")} */ const loadTags = sync.require("./load-tags") -const displayCurrency = "NZD" -const displayCurrencySymbol = "$" const currencyExchange = new Map([ ["AUD", 0.63], + ["BRL", 0.17], ["CAD", 0.7], ["CHF", 1.13], ["EUR", 1.08], @@ -63,6 +62,24 @@ function loadPreviews(locals, field, number, whereClause, account, filter_field, } } +pugSync.addGlobals({ + getAlbumCoverAttributes(event, item) { + /** @type {any} */ + let attributes = { + "hx-get": `/api/play/${item.item_type}/${item.item_id}`, + "hx-target": "#player", + "hx-select": "#player", + "hx-indicator": "null", + "hx-push-url": "false" + } + if (getCookie(event, "bcex-inline-player-disabled") === "true") { + attributes = {"target": "_blank"} + } + attributes.href = item.item_url + return attributes + } +}) + const schema = { query: z.object({ arrange: z.enum(["album", "artist", "label", "tag", "track"]), @@ -132,6 +149,11 @@ router.get("/:account/", defineEventHandler({ console.error(sql, params) throw e } + + let displayCurrency = getCookie(event, "bcex-currency") || "" + if (!currencyExchange.has(displayCurrency)) displayCurrency = "NZD" + const currencyRoundTo = displayCurrency === "JPY" ? 1000 : 10 + const locals = { items, account, @@ -149,12 +171,12 @@ router.get("/:account/", defineEventHandler({ avgTags: Math.round(db.prepare("SELECT avg(count) FROM (SELECT count(*) AS count FROM item_tag WHERE account = ? GROUP BY item_id)").pluck().get(account)*10)/10, lonelyTags: db.prepare("SELECT count(*) FROM (SELECT tag FROM item_tag WHERE account = ? GROUP BY tag HAVING count(*) = 1)").pluck().get(account), value: Math.round(select("item", ["currency", "price"], {account}).all().map(c => { - return (currencyExchange.get(c.currency) || 0.6) * c.price / (currencyExchange.get(displayCurrency) || 1) / 10 - }).reduce((a, c) => a + c, 0)) * 10, + return (currencyExchange.get(c.currency) || 0.6) * c.price / (currencyExchange.get(displayCurrency) || 1) / currencyRoundTo + }).reduce((a, c) => a + c, 0)) * currencyRoundTo, displayCurrency, - displayCurrencySymbol, taste: db.prepare("with popularity (a) as (select avg(also_collected_count) from item WHERE account = ? group by band_url) select sum(iif(a >= 0 and a < 20, 1, 0)) as cold, sum(iif(a >= 20 and a < 200, 1, 0)) as warm, sum(iif(a >= 200 and a < 2000, 1, 0)) as hot, sum(iif(a >= 2000, 1, 0)) as supernova from popularity").raw().get(account) - } + }, + currencies: [...currencyExchange.keys()] } if (mode === "artist_grid") { loadPreviews(locals, "band_name", 4, whereClause, account, filter_field, filter, filter_fuzzy) diff --git a/routes/settings.js b/routes/settings.js new file mode 100644 index 0000000..a7cb337 --- /dev/null +++ b/routes/settings.js @@ -0,0 +1,28 @@ +// @ts-check + +const {z} = require("zod") +const {router} = require("../passthrough") +const {defineEventHandler, readValidatedBody, setCookie, setResponseHeader, sendRedirect} = require("h3") + +const schema = { + inline_player: z.object({ + inline_player: z.string().optional() + }), + currency: z.object({ + currency: z.string().regex(/^[A-Z]{3}$/), + account: z.string() + }) +} + +router.post("/api/settings/inline-player", defineEventHandler(async event => { + const {inline_player} = await readValidatedBody(event, schema.inline_player.parse) + setCookie(event, "bcex-inline-player-disabled", String(!inline_player)) + setResponseHeader(event, "HX-Refresh", "true") + return null +})) + +router.post("/api/settings/currency", defineEventHandler(async event => { + const {currency, account} = await readValidatedBody(event, schema.currency.parse) + setCookie(event, "bcex-currency", currency) + return sendRedirect(event, `/${account}/?arrange=tag&shape=grid`, 302) +})) diff --git a/start.js b/start.js index 6a850e7..b680ec7 100644 --- a/start.js +++ b/start.js @@ -37,6 +37,7 @@ pugSync.createRoute(router, "/", "home.pug") sync.require("./routes/app") sync.require("./routes/load-collection") sync.require("./routes/play") +sync.require("./routes/settings") // Files