« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/content/dependencies/generateAlbumGalleryTrackGrid.js6
-rw-r--r--src/content/dependencies/generateArtistGalleryPage.js6
-rw-r--r--src/content/dependencies/generateCoverGrid.js37
-rw-r--r--src/content/dependencies/generateGroupGalleryPageAlbumGrid.js18
-rw-r--r--src/static/css/site.css34
-rw-r--r--src/static/js/client/index.js2
-rw-r--r--src/static/js/client/reveal-all-grid-control.js72
-rw-r--r--src/strings-default.yaml5
8 files changed, 174 insertions, 6 deletions
diff --git a/src/content/dependencies/generateAlbumGalleryTrackGrid.js b/src/content/dependencies/generateAlbumGalleryTrackGrid.js
index fb5ed7ea..86c35b6f 100644
--- a/src/content/dependencies/generateAlbumGalleryTrackGrid.js
+++ b/src/content/dependencies/generateAlbumGalleryTrackGrid.js
@@ -77,6 +77,9 @@ export default {
           ? artwork.artistContribs
               .map(contrib => contrib.artist.name)
           : null)),
+
+    allWarnings:
+      query.artworks.flatMap(artwork => artwork?.contentWarnings),
   }),
 
   slots: {
@@ -117,6 +120,9 @@ export default {
                 artists:
                   language.formatUnitList(artists),
               })),
+
+          revealAllWarnings:
+            data.allWarnings,
         }),
       ]),
 };
diff --git a/src/content/dependencies/generateArtistGalleryPage.js b/src/content/dependencies/generateArtistGalleryPage.js
index 6a24275e..094edc0c 100644
--- a/src/content/dependencies/generateArtistGalleryPage.js
+++ b/src/content/dependencies/generateArtistGalleryPage.js
@@ -58,6 +58,10 @@ export default {
         .map(artwork => artwork.artistContribs
           .filter(contrib => contrib.artist !== artist)
           .map(contrib => contrib.artist.name)),
+
+    allWarnings:
+      query.artworks
+        .flatMap(artwork => artwork.contentWarnings),
   }),
 
   generate: (data, relations, {html, language}) =>
@@ -93,6 +97,8 @@ export default {
 
                     artists: language.formatUnitList(names),
                   })),
+
+              revealAllWarnings: data.allWarnings,
             }),
         ],
 
diff --git a/src/content/dependencies/generateCoverGrid.js b/src/content/dependencies/generateCoverGrid.js
index e94eab05..89371015 100644
--- a/src/content/dependencies/generateCoverGrid.js
+++ b/src/content/dependencies/generateCoverGrid.js
@@ -1,4 +1,4 @@
-import {stitchArrays} from '#sugar';
+import {empty, stitchArrays, unique} from '#sugar';
 
 export default {
   contentDependencies: ['generateGridActionLinks'],
@@ -41,6 +41,10 @@ export default {
 
     lazy: {validate: v => v.anyOf(v.isWholeNumber, v.isBoolean)},
     actionLinks: {validate: v => v.sparseArrayOf(v.isHTML)},
+
+    revealAllWarnings: {
+      validate: v => v.looseArrayOf(v.isString),
+    },
   },
 
   generate: (relations, slots, {html, language}) =>
@@ -49,6 +53,37 @@ export default {
       {[html.onlyIfContent]: true},
 
       [
+        !empty((slots.revealAllWarnings ?? []).filter(Boolean)) &&
+          language.encapsulate('misc.coverGrid.revealAll', capsule =>
+            html.tag('div', {class: 'reveal-all-container'},
+              ((slots.tab ?? [])
+                .slice(0, 4)
+                .some(tab => tab && !html.isBlank(tab))) &&
+
+                {class: 'has-nearby-tab'},
+
+              html.tag('p', {class: 'reveal-all'}, [
+                html.tag('a', {href: '#'}, [
+                  html.tag('span', {class: 'reveal-label'},
+                    language.$(capsule, 'reveal')),
+
+                  html.tag('span', {class: 'conceal-label'},
+                    {style: 'display: none'},
+                    language.$(capsule, 'conceal')),
+                ]),
+
+                html.tag('br'),
+
+                html.tag('span', {class: 'warnings'},
+                  language.$(capsule, 'warnings', {
+                    warnings:
+                      language.formatUnitList(
+                        unique(slots.revealAllWarnings.filter(Boolean))
+                          .sort()
+                          .map(warning => html.tag('b', warning))),
+                  })),
+              ]))),
+
         stitchArrays({
           classes: slots.classes,
           attributes: slots.itemAttributes,
diff --git a/src/content/dependencies/generateGroupGalleryPageAlbumGrid.js b/src/content/dependencies/generateGroupGalleryPageAlbumGrid.js
index 25e57a67..9167a5ad 100644
--- a/src/content/dependencies/generateGroupGalleryPageAlbumGrid.js
+++ b/src/content/dependencies/generateGroupGalleryPageAlbumGrid.js
@@ -37,6 +37,12 @@ export default {
 
         return album.artistContribs;
       }),
+
+    artworks:
+      albums.map(album =>
+        (album.hasCoverArt
+          ? album.coverArtworks[0]
+          : null)),
   }),
 
   relations: (relation, query, albums, _group) => ({
@@ -52,11 +58,8 @@ export default {
         .map(album => relation('linkAlbum', album)),
 
     images:
-      albums
-        .map(album =>
-          (album.hasCoverArt
-            ? relation('image', album.coverArtworks[0])
-            : relation('image')))
+      query.artworks
+        .map(artwork => relation('image', artwork)),
   }),
 
   data: (query, albums, group) => ({
@@ -69,6 +72,9 @@ export default {
     tracks:
       albums.map(album => album.tracks.length),
 
+    allWarnings:
+      query.artworks.flatMap(artwork => artwork?.contentWarnings),
+
     durations:
       albums.map(album =>
         (album.hideDuration
@@ -141,5 +147,7 @@ export default {
                     time: language.formatDuration(duration),
                   })
                 : null)),
+
+        revealAllWarnings: data.allWarnings,
       })),
 };
diff --git a/src/static/css/site.css b/src/static/css/site.css
index 5faed373..d85b8283 100644
--- a/src/static/css/site.css
+++ b/src/static/css/site.css
@@ -3078,6 +3078,40 @@ video.pixelate, .pixelate video {
   border: 1px dashed #fff3;
 }
 
+.grid-listing .reveal-all-container {
+  flex-basis: 100%;
+}
+
+.grid-listing:not(:has(.grid-item:not([class*="hidden-by-"]))) .reveal-all-container {
+  display: none;
+}
+
+.grid-listing .reveal-all-container.has-nearby-tab {
+  margin-bottom: 0.6em;
+}
+
+.grid-listing .reveal-all {
+  max-width: 400px;
+  margin: 0.20em auto 0;
+  text-align: center;
+}
+
+.grid-listing .reveal-all .warnings:not(.reveal-all:hover *) {
+  opacity: 0.4;
+}
+
+.grid-listing .reveal-all a {
+  display: inline-block;
+  margin-bottom: 0.15em;
+
+  text-decoration: underline;
+  text-decoration-style: dotted;
+}
+
+.grid-listing .reveal-all b {
+  white-space: nowrap;
+}
+
 .grid-item {
   line-height: 1.2;
   font-size: 0.9em;
diff --git a/src/static/js/client/index.js b/src/static/js/client/index.js
index 4ca4700e..016ce9ad 100644
--- a/src/static/js/client/index.js
+++ b/src/static/js/client/index.js
@@ -19,6 +19,7 @@ import * as imageOverlayModule from './image-overlay.js';
 import * as intrapageDotSwitcherModule from './intrapage-dot-switcher.js';
 import * as liveMousePositionModule from './live-mouse-position.js';
 import * as quickDescriptionModule from './quick-description.js';
+import * as revealAllGridControlModule from './reveal-all-grid-control.js';
 import * as scriptedLinkModule from './scripted-link.js';
 import * as sidebarSearchModule from './sidebar-search.js';
 import * as stickyHeadingModule from './sticky-heading.js';
@@ -44,6 +45,7 @@ export const modules = [
   intrapageDotSwitcherModule,
   liveMousePositionModule,
   quickDescriptionModule,
+  revealAllGridControlModule,
   scriptedLinkModule,
   sidebarSearchModule,
   stickyHeadingModule,
diff --git a/src/static/js/client/reveal-all-grid-control.js b/src/static/js/client/reveal-all-grid-control.js
new file mode 100644
index 00000000..1b362bea
--- /dev/null
+++ b/src/static/js/client/reveal-all-grid-control.js
@@ -0,0 +1,72 @@
+/* eslint-env browser */
+
+import {cssProp} from '../client-util.js';
+
+export const info = {
+  id: 'revealAllGridControlInfo',
+
+  revealAllLinks: null,
+  revealables: null,
+
+  revealLabels: null,
+  concealLabels: null,
+};
+
+export function getPageReferences() {
+  info.revealAllLinks =
+    Array.from(document.querySelectorAll('.reveal-all a'));
+
+  info.revealables =
+    info.revealAllLinks
+      .map(link => link.closest('.grid-listing'))
+      .map(listing => listing.querySelectorAll('.reveal'));
+
+  info.revealLabels =
+    info.revealAllLinks
+      .map(link => link.querySelector('.reveal-label'));
+
+  info.concealLabels =
+    info.revealAllLinks
+      .map(link => link.querySelector('.conceal-label'));
+}
+
+export function addPageListeners() {
+  for (const [index, link] of info.revealAllLinks.entries()) {
+    link.addEventListener('click', domEvent => {
+      domEvent.preventDefault();
+      handleRevealAllLinkClicked(index);
+    });
+  }
+}
+
+export function addInternalListeners() {
+  // Don't even think about it. "Reveal all artworks" is a stable control,
+  // meaning it only changes because the user interacted with it directly.
+}
+
+function handleRevealAllLinkClicked(index) {
+  const revealables = info.revealables[index];
+  const revealLabel = info.revealLabels[index];
+  const concealLabel = info.concealLabels[index];
+
+  const shouldReveal =
+    (cssProp(revealLabel, 'display') === 'none'
+      ? false
+      : true);
+
+  for (const revealable of revealables) {
+    if (shouldReveal) {
+      revealable.classList.add('revealed');
+    } else {
+      revealable.classList.remove('revealed');
+    }
+  }
+
+  if (shouldReveal) {
+    cssProp(revealLabel, 'display', 'none');
+    cssProp(concealLabel, 'display', null);
+  } else {
+    cssProp(revealLabel, 'display', null);
+    cssProp(concealLabel, 'display', 'none');
+  }
+}
diff --git a/src/strings-default.yaml b/src/strings-default.yaml
index f7fb1f2b..02e1ee31 100644
--- a/src/strings-default.yaml
+++ b/src/strings-default.yaml
@@ -1002,6 +1002,11 @@ misc:
   #   that thing.
 
   coverGrid:
+    revealAll:
+      reveal: "Reveal all artworks"
+      conceal: "Conceal all artworks"
+      warnings: "In this gallery: {WARNINGS}"
+
     noCoverArt: "{ALBUM}"
 
     tab: