Filter location tags
This commit is contained in:
		
							parent
							
								
									72fda72de5
								
							
						
					
					
						commit
						62e7ccf17b
					
				
					 5 changed files with 93 additions and 47 deletions
				
			
		| 
						 | 
				
			
			@ -22,7 +22,7 @@ function addPopoverStyle() {
 | 
			
		|||
	document.querySelectorAll("[popovertarget]").forEach(e => {
 | 
			
		||||
		e.addEventListener("click", () => {
 | 
			
		||||
			const rect = e.getBoundingClientRect()
 | 
			
		||||
			const width = Math.floor(rect.width + 85)
 | 
			
		||||
			const width = 266
 | 
			
		||||
			const left = Math.max(Math.floor(rect.left + rect.width / 2), width / 2)
 | 
			
		||||
			const t = `:popover-open { position: fixed; top: ${Math.floor(rect.bottom)}px; left: ${left}px; width: ${width}px; transform: translateX(-50%); margin: 0 }`
 | 
			
		||||
			document.styleSheets[0].insertRule(t, document.styleSheets[0].cssRules.length)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,6 +44,28 @@ html
 | 
			
		|||
                  .ml6 Switch account
 | 
			
		||||
              li.s-menu--divider(role="separator")
 | 
			
		||||
              li.s-menu--title Settings
 | 
			
		||||
              li(role="menuitem")
 | 
			
		||||
                if arrange === "tag"
 | 
			
		||||
                  form.s-block-link.d-flex.ai-center(hx-post="/api/settings/location-tags" hx-trigger="change" hx-indicator="#location-tags-loading" hx-select="#tag-page" hx-target="#tag-page")
 | 
			
		||||
                    span.fl-grow1.d-flex.ai-center
 | 
			
		||||
                      != icons.get("map-pin")
 | 
			
		||||
                      .pl6 Region tags
 | 
			
		||||
                    #location-tags-loading
 | 
			
		||||
                    input(type="hidden" name="account" value=account)
 | 
			
		||||
                    .s-toggle-switch.s-toggle-switch__multiple
 | 
			
		||||
                      - const locationTagsSetting = h3.getCookie(event, "bcex-location-tags") || "all"
 | 
			
		||||
                      input#location-tags-all(type="radio" name="location_tags" value="all" autocomplete="off" checked=(locationTagsSetting === "all"))
 | 
			
		||||
                      label.px8.py6(for="location-tags-all") All
 | 
			
		||||
                      input#location-tags-hide(type="radio" name="location_tags" value="hide" autocomplete="off" checked=(locationTagsSetting === "hide"))
 | 
			
		||||
                      label.px8.py6(for="location-tags-hide") Hide
 | 
			
		||||
                      input#location-tags-only(type="radio" name="location_tags" value="only" autocomplete="off" checked=(locationTagsSetting === "only"))
 | 
			
		||||
                      label.px8.py6(for="location-tags-only") Only
 | 
			
		||||
                else
 | 
			
		||||
                  .s-block-link.d-flex.ai-center
 | 
			
		||||
                    span.fl-grow1.d-flex.ai-center.fc-black-400
 | 
			
		||||
                      != icons.get("map-pin")
 | 
			
		||||
                      .pl6 Region tags
 | 
			
		||||
                    a.fc-black-400.td-underline(href=and({arrange: "tag"})) Set on tags view
 | 
			
		||||
              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
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +74,7 @@ html
 | 
			
		|||
                  #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")
 | 
			
		||||
                form.s-block-link.py2.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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,47 +4,56 @@ block title
 | 
			
		|||
  - title = `Tags | ${title}`
 | 
			
		||||
 | 
			
		||||
block view
 | 
			
		||||
  script
 | 
			
		||||
    | var items =
 | 
			
		||||
    != JSON.stringify(items)
 | 
			
		||||
    | ; var filter_field =
 | 
			
		||||
    != JSON.stringify(filter_field || null)
 | 
			
		||||
  .mx-auto.w100.wmx11.fs-body1
 | 
			
		||||
    .word-cloud(style="cursor: default")
 | 
			
		||||
    script.
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        const content = document.querySelector(".word-cloud")
 | 
			
		||||
        content.style.height = `${Math.round(Math.min(content.clientWidth, window.innerHeight)*0.8)}px`
 | 
			
		||||
        const dark = window.matchMedia?.("(prefers-color-scheme: dark)").matches
 | 
			
		||||
        WordCloud(content, {
 | 
			
		||||
          list: items,
 | 
			
		||||
          fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI Adjusted", "Segoe UI", "Liberation Sans", sans-serif',
 | 
			
		||||
          fontWeight: "bold",
 | 
			
		||||
          color: dark ? "random-light": "random-dark",
 | 
			
		||||
          minSize: 4,
 | 
			
		||||
          gridSize: Math.round(content.clientWidth / 200),
 | 
			
		||||
          weightFactor: size => size * content.clientWidth / 180,
 | 
			
		||||
          rotateRatio: 0,
 | 
			
		||||
          click: item => {
 | 
			
		||||
            const highlightedItem = document.querySelector(".wc-hl")?.textContent || item[0]
 | 
			
		||||
            const newURL = new URL(location)
 | 
			
		||||
            newURL.searchParams.set("filter", highlightedItem)
 | 
			
		||||
            newURL.searchParams.set("filter_field", "tag")
 | 
			
		||||
            newURL.searchParams.delete("filter_fuzzy")
 | 
			
		||||
            newURL.searchParams.set("arrange", filter_field ? "album" : "label")
 | 
			
		||||
            content.setAttribute("hx-push-url", newURL) // workaround for https://github.com/bigskysoftware/htmx/issues/2744
 | 
			
		||||
            htmx.ajax("GET", newURL.search, {context: content, source: content})
 | 
			
		||||
          }
 | 
			
		||||
  #tag-page
 | 
			
		||||
    script
 | 
			
		||||
      -
 | 
			
		||||
        const locationTags = location_tags || h3.getCookie(event, "bcex-location-tags") || "all"
 | 
			
		||||
        const filteredItems = items.filter(item => {
 | 
			
		||||
          if (locationTags === "all") return true
 | 
			
		||||
          const isLocation = item[0][0].match(/[A-Z]/)
 | 
			
		||||
          if (locationTags === "only") return isLocation
 | 
			
		||||
          else return !isLocation
 | 
			
		||||
        })
 | 
			
		||||
        content.style.background = "none"
 | 
			
		||||
        content.addEventListener("wordcloudstop", () => {
 | 
			
		||||
          for (const child of content.children) {
 | 
			
		||||
            child.addEventListener("mouseenter", () => {
 | 
			
		||||
              child.classList.add("wc-hl")
 | 
			
		||||
            })
 | 
			
		||||
            child.addEventListener("mouseleave", () => {
 | 
			
		||||
              child.classList.remove("wc-hl")
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
      | var items =
 | 
			
		||||
      != JSON.stringify(filteredItems)
 | 
			
		||||
      | ; var filter_field =
 | 
			
		||||
      != JSON.stringify(filter_field || null)
 | 
			
		||||
    .mx-auto.w100.wmx11.fs-body1
 | 
			
		||||
      .word-cloud(style="cursor: default")
 | 
			
		||||
      script.
 | 
			
		||||
        setTimeout(() => {
 | 
			
		||||
          const content = document.querySelector(".word-cloud")
 | 
			
		||||
          content.style.height = `${Math.round(Math.min(content.clientWidth, window.innerHeight)*0.8)}px`
 | 
			
		||||
          const dark = window.matchMedia?.("(prefers-color-scheme: dark)").matches
 | 
			
		||||
          WordCloud(content, {
 | 
			
		||||
            list: items,
 | 
			
		||||
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI Adjusted", "Segoe UI", "Liberation Sans", sans-serif',
 | 
			
		||||
            fontWeight: "bold",
 | 
			
		||||
            color: dark ? "random-light": "random-dark",
 | 
			
		||||
            minSize: 4,
 | 
			
		||||
            gridSize: Math.round(content.clientWidth / 200),
 | 
			
		||||
            weightFactor: size => size * content.clientWidth / 180,
 | 
			
		||||
            rotateRatio: 0,
 | 
			
		||||
            click: item => {
 | 
			
		||||
              const highlightedItem = document.querySelector(".wc-hl")?.textContent || item[0]
 | 
			
		||||
              const newURL = new URL(location)
 | 
			
		||||
              newURL.searchParams.set("filter", highlightedItem)
 | 
			
		||||
              newURL.searchParams.set("filter_field", "tag")
 | 
			
		||||
              newURL.searchParams.delete("filter_fuzzy")
 | 
			
		||||
              newURL.searchParams.set("arrange", filter_field ? "album" : "label")
 | 
			
		||||
              content.setAttribute("hx-push-url", newURL) // workaround for https://github.com/bigskysoftware/htmx/issues/2744
 | 
			
		||||
              htmx.ajax("GET", newURL.search, {context: content, source: content})
 | 
			
		||||
            }
 | 
			
		||||
          })
 | 
			
		||||
          content.style.background = "none"
 | 
			
		||||
          content.addEventListener("wordcloudstop", () => {
 | 
			
		||||
            for (const child of content.children) {
 | 
			
		||||
              child.addEventListener("mouseenter", () => {
 | 
			
		||||
                child.classList.add("wc-hl")
 | 
			
		||||
              })
 | 
			
		||||
              child.addEventListener("mouseleave", () => {
 | 
			
		||||
                child.classList.remove("wc-hl")
 | 
			
		||||
              })
 | 
			
		||||
            }
 | 
			
		||||
          })
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,8 @@
 | 
			
		|||
 | 
			
		||||
const {z} = require("zod")
 | 
			
		||||
 | 
			
		||||
const account = z.string().regex(/^[a-z0-9_-]+$/)
 | 
			
		||||
 | 
			
		||||
const schema = {
 | 
			
		||||
	appQuery: z.object({
 | 
			
		||||
		arrange: z.enum(["album", "artist", "label", "tag", "track"]),
 | 
			
		||||
| 
						 | 
				
			
			@ -11,11 +13,11 @@ const schema = {
 | 
			
		|||
		filter_fuzzy: z.enum(["true"]).optional()
 | 
			
		||||
	}),
 | 
			
		||||
	account: z.object({
 | 
			
		||||
		account: z.string().regex(/^[a-z0-9_-]+$/)
 | 
			
		||||
		account
 | 
			
		||||
	}),
 | 
			
		||||
	postCurrency: z.object({
 | 
			
		||||
		currency: z.string().regex(/^[A-Z]{3}$/),
 | 
			
		||||
		account: z.string()
 | 
			
		||||
		account
 | 
			
		||||
	}),
 | 
			
		||||
	play: z.object({
 | 
			
		||||
		item_type: z.enum(["album", "track"]),
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +26,10 @@ const schema = {
 | 
			
		|||
	}),
 | 
			
		||||
	inlinePlayer: z.object({
 | 
			
		||||
		inline_player: z.string().optional()
 | 
			
		||||
	}),
 | 
			
		||||
	locationTags: z.object({
 | 
			
		||||
		location_tags: z.enum(["all", "hide", "only"]),
 | 
			
		||||
		account
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,10 @@
 | 
			
		|||
// @ts-check
 | 
			
		||||
 | 
			
		||||
const {sync, router} = require("../passthrough")
 | 
			
		||||
const {defineEventHandler, readValidatedBody, setCookie, setResponseHeader} = require("h3")
 | 
			
		||||
const {defineEventHandler, readValidatedBody, setCookie, setResponseHeader, sendRedirect} = require("h3")
 | 
			
		||||
 | 
			
		||||
/** @type {import("../pug-sync")} */
 | 
			
		||||
const pugSync = sync.require("../pug-sync")
 | 
			
		||||
 | 
			
		||||
/** @type {import("./schema")} */
 | 
			
		||||
const schema = sync.require("./schema")
 | 
			
		||||
| 
						 | 
				
			
			@ -12,3 +15,9 @@ router.post("/api/settings/inline-player", defineEventHandler(async event => {
 | 
			
		|||
	setResponseHeader(event, "HX-Refresh", "true")
 | 
			
		||||
	return null
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
router.post("/api/settings/location-tags", defineEventHandler(async event => {
 | 
			
		||||
	const {location_tags, account} = await readValidatedBody(event, schema.schema.locationTags.parse)
 | 
			
		||||
	setCookie(event, "bcex-location-tags", location_tags)
 | 
			
		||||
	return sendRedirect(event, `/${account}/?arrange=tag&shape=grid`)
 | 
			
		||||
}))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue