« get me outta code hell

content: decompose generateCoverArtwork - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2024-11-19 13:25:22 -0400
committer(quasar) nebula <qznebula@protonmail.com>2024-11-19 13:25:22 -0400
commit7cb88275fd3c813114271c0a136b12c72c5a172a (patch)
treeb3b007e73c4681e206162362f5d9a8b99b6762d7 /src
parent943cb8d05f5ef8572edfa081fb9912107769241a (diff)
content: decompose generateCoverArtwork
No visual/site changes yet.

This involves introducing an unfortunate mega-hack in
generateStickyHeadingContainer, which sets slots on cover
artworks. Very scary. Oooooo

Otherwise, all cover artwork code is much more compositional.
Pass-through slots (`image`) are removed in generateCoverArtwork
and a partially-formed `image` slot is accepted instead.
Diffstat (limited to 'src')
-rw-r--r--src/content/dependencies/generateAlbumCoverArtwork.js47
-rw-r--r--src/content/dependencies/generateCoverArtwork.js165
-rw-r--r--src/content/dependencies/generateCoverArtworkArtTagDetails.js50
-rw-r--r--src/content/dependencies/generateCoverArtworkArtistDetails.js23
-rw-r--r--src/content/dependencies/generateFlashCoverArtwork.js15
-rw-r--r--src/content/dependencies/generateReferencedArtworksPage.js3
-rw-r--r--src/content/dependencies/generateReferencingArtworksPage.js3
-rw-r--r--src/content/dependencies/generateStickyHeadingContainer.js62
-rw-r--r--src/content/dependencies/generateTrackCoverArtwork.js49
9 files changed, 257 insertions, 160 deletions
diff --git a/src/content/dependencies/generateAlbumCoverArtwork.js b/src/content/dependencies/generateAlbumCoverArtwork.js
index 99003034..f6583882 100644
--- a/src/content/dependencies/generateAlbumCoverArtwork.js
+++ b/src/content/dependencies/generateAlbumCoverArtwork.js
@@ -1,10 +1,25 @@
 export default {
-  contentDependencies: ['generateCoverArtwork'],
-  extraDependencies: ['language'],
+  contentDependencies: [
+    'generateCoverArtwork',
+    'generateCoverArtworkArtTagDetails',
+    'generateCoverArtworkArtistDetails',
+    'image',
+  ],
+
+  extraDependencies: ['html', 'language'],
 
   relations: (relation, album) => ({
     coverArtwork:
-      relation('generateCoverArtwork', album.artTags, album.coverArtistContribs),
+      relation('generateCoverArtwork'),
+
+    image:
+      relation('image'),
+
+    artTagDetails:
+      relation('generateCoverArtworkArtTagDetails', album.artTags),
+
+    artistDetails:
+      relation('generateCoverArtworkArtistDetails', album.coverArtistContribs),
   }),
 
   data: (album) => ({
@@ -18,11 +33,29 @@ export default {
       album.coverArtDimensions,
   }),
 
-  generate: (data, relations, {language}) =>
+  slots: {
+    details: {
+      validate: v => v.is('tags', 'artists'),
+      default: 'tags',
+    },
+  },
+
+  generate: (data, relations, slots, {language}) =>
     relations.coverArtwork.slots({
-      path: data.path,
-      color: data.color,
+      image:
+        relations.image.slots({
+          path: data.path,
+          color: data.color,
+          alt: language.$('misc.alt.albumCover'),
+        }),
+
       dimensions: data.dimensions,
-      alt: language.$('misc.alt.albumCover'),
+
+      details:
+        (slots.details === 'tags'
+          ? relations.artTagDetails
+       : slots.details === 'artists'
+          ? relations.artistDetails
+          : null),
     }),
 };
diff --git a/src/content/dependencies/generateCoverArtwork.js b/src/content/dependencies/generateCoverArtwork.js
index 70d71bc6..50089f69 100644
--- a/src/content/dependencies/generateCoverArtwork.js
+++ b/src/content/dependencies/generateCoverArtwork.js
@@ -1,83 +1,29 @@
-import {stitchArrays} from '#sugar';
-
 export default {
-  contentDependencies: ['image', 'linkArtTag', 'linkArtistGallery'],
-  extraDependencies: ['html', 'language'],
-
-  query: (artTags, _coverArtistContribs) => ({
-    linkableArtTags:
-      (artTags
-        ? artTags.filter(tag => !tag.isContentWarning)
-        : []),
-  }),
-
-  relations: (relation, query, artTags, coverArtistContribs) => ({
-    image:
-      relation('image', artTags),
-
-    tagLinks:
-      query.linkableArtTags
-        .map(tag => relation('linkArtTag', tag)),
-
-    artistLinks:
-      coverArtistContribs
-        .map(contrib => contrib.artist)
-        .map(artist =>
-          relation('linkArtistGallery', artist)),
-  }),
-
-  data: (query, _artTags, _coverArtistContribs) => {
-    const data = {};
-
-    const seenShortNames = new Set();
-    const duplicateShortNames = new Set();
-
-    for (const {nameShort: shortName} of query.linkableArtTags) {
-      if (seenShortNames.has(shortName)) {
-        duplicateShortNames.add(shortName);
-      } else {
-        seenShortNames.add(shortName);
-      }
-    }
-
-    data.preferShortName =
-      query.linkableArtTags
-        .map(artTag => !duplicateShortNames.has(artTag.nameShort));
-
-    return data;
-  },
+  contentDependencies: ['image', 'linkArtistGallery'],
+  extraDependencies: ['html'],
 
   slots: {
-    path: {
-      validate: v => v.validateArrayItems(v.isString),
-    },
-
-    alt: {
-      type: 'string',
-    },
-
-    color: {
-      validate: v => v.isColor,
+    image: {
+      type: 'html',
+      mutable: true,
     },
 
     mode: {
-      validate: v =>
-        v.is(...[
-          'primary-tags',
-          'primary-artists',
-          'thumbnail',
-          'commentary',
-        ]),
-
-      default: 'primary-tags',
+      validate: v => v.is('primary', 'thumbnail', 'commentary'),
+      default: 'primary',
     },
 
     dimensions: {
       validate: v => v.isDimensions,
     },
+
+    details: {
+      type: 'html',
+      mutable: false,
+    },
   },
 
-  generate(data, relations, slots, {html, language}) {
+  generate(slots, {html}) {
     const square =
       (slots.dimensions
         ? slots.dimensions[0] === slots.dimensions[1]
@@ -88,83 +34,38 @@ export default {
         ? {square: true}
         : {dimensions: slots.dimensions});
 
-    switch (slots.mode) {
-      case 'primary-tags':
-        return html.tags([
-          relations.image.slots({
-            path: slots.path,
-            alt: slots.alt,
-            color: slots.color,
+    return html.tags([
+      (slots.mode === 'primary'
+        ? slots.image.slots({
             thumb: 'medium',
             reveal: true,
             link: true,
             ...sizeSlots,
-          }),
-
-          html.tag('ul', {class: 'image-details'},
-            {[html.onlyIfContent]: true},
+          })
 
-            {class: 'art-tag-details'},
-
-            stitchArrays({
-              tagLink: relations.tagLinks,
-              preferShortName: data.preferShortName,
-            }).map(({tagLink, preferShortName}) =>
-                html.tag('li',
-                  tagLink.slot('preferShortName', preferShortName)))),
-        ]);
+     : slots.mode === 'thumbnail'
+        ? slots.image.slots({
+            thumb: 'small',
+            reveal: false,
+            link: false,
+            ...sizeSlots,
+          })
 
-      case 'primary-artists':
-        return html.tags([
-          relations.image.slots({
-            path: slots.path,
-            alt: slots.alt,
-            color: slots.color,
+     : slots.mode === 'commentary'
+        ? slots.image.slots({
             thumb: 'medium',
             reveal: true,
             link: true,
+            lazy: true,
             ...sizeSlots,
-          }),
-
-          html.tag('p', {class: 'image-details'},
-            {[html.onlyIfContent]: true},
-
-            {class: 'illustrator-details'},
-
-            language.$('misc.coverGrid.details.coverArtists', {
-              artists:
-                language.formatConjunctionList(relations.artistLinks),
-            })),
-        ]);
-
-      case 'thumbnail':
-        return relations.image.slots({
-          path: slots.path,
-          alt: slots.alt,
-          color: slots.color,
-          thumb: 'small',
-          reveal: false,
-          link: false,
-          ...sizeSlots,
-        });
 
-      case 'commentary':
-        return relations.image.slots({
-          path: slots.path,
-          alt: slots.alt,
-          color: slots.color,
-          thumb: 'medium',
-          reveal: true,
-          link: true,
-          lazy: true,
-          ...sizeSlots,
+            attributes:
+              {class: 'commentary-art'},
+          })
 
-          attributes:
-            {class: 'commentary-art'},
-        });
+        : html.blank()),
 
-      default:
-        return html.blank();
-    }
+      html.tags([slots.details], {[html.onlyIfSiblings]: true}),
+    ]);
   },
 };
diff --git a/src/content/dependencies/generateCoverArtworkArtTagDetails.js b/src/content/dependencies/generateCoverArtworkArtTagDetails.js
new file mode 100644
index 00000000..81ead8a9
--- /dev/null
+++ b/src/content/dependencies/generateCoverArtworkArtTagDetails.js
@@ -0,0 +1,50 @@
+import {stitchArrays} from '#sugar';
+
+export default {
+  contentDependencies: ['linkArtTag'],
+  extraDependencies: ['html'],
+
+  query: (artTags) => ({
+    linkableArtTags:
+      artTags
+        .filter(tag => !tag.isContentWarning),
+  }),
+
+  relations: (relation, query, _artTags) => ({
+    tagLinks:
+      query.linkableArtTags
+        .map(tag => relation('linkArtTag', tag)),
+  }),
+
+  data: (query, _artTags) => {
+    const seenShortNames = new Set();
+    const duplicateShortNames = new Set();
+
+    for (const {nameShort: shortName} of query.linkableArtTags) {
+      if (seenShortNames.has(shortName)) {
+        duplicateShortNames.add(shortName);
+      } else {
+        seenShortNames.add(shortName);
+      }
+    }
+
+    const preferShortName =
+      query.linkableArtTags
+        .map(artTag => !duplicateShortNames.has(artTag.nameShort));
+
+    return {preferShortName};
+  },
+
+  generate: (data, relations, {html}) =>
+    html.tag('ul', {class: 'image-details'},
+      {[html.onlyIfContent]: true},
+
+      {class: 'art-tag-details'},
+
+      stitchArrays({
+        tagLink: relations.tagLinks,
+        preferShortName: data.preferShortName,
+      }).map(({tagLink, preferShortName}) =>
+          html.tag('li',
+            tagLink.slot('preferShortName', preferShortName)))),
+};
diff --git a/src/content/dependencies/generateCoverArtworkArtistDetails.js b/src/content/dependencies/generateCoverArtworkArtistDetails.js
new file mode 100644
index 00000000..5b235353
--- /dev/null
+++ b/src/content/dependencies/generateCoverArtworkArtistDetails.js
@@ -0,0 +1,23 @@
+export default {
+  contentDependencies: ['linkArtistGallery'],
+  extraDependencies: ['html', 'language'],
+
+  relations: (relation, contributions) => ({
+    artistLinks:
+      contributions
+        .map(contrib => contrib.artist)
+        .map(artist =>
+          relation('linkArtistGallery', artist)),
+  }),
+
+  generate: (relations, {html, language}) =>
+    html.tag('p', {class: 'image-details'},
+      {[html.onlyIfContent]: true},
+
+      {class: 'illustrator-details'},
+
+      language.$('misc.coverGrid.details.coverArtists', {
+        artists:
+          language.formatConjunctionList(relations.artistLinks),
+      })),
+};
diff --git a/src/content/dependencies/generateFlashCoverArtwork.js b/src/content/dependencies/generateFlashCoverArtwork.js
index bcf2a55c..02590a09 100644
--- a/src/content/dependencies/generateFlashCoverArtwork.js
+++ b/src/content/dependencies/generateFlashCoverArtwork.js
@@ -4,7 +4,10 @@ export default {
 
   relations: (relation) => ({
     coverArtwork:
-      relation('generateCoverArtwork', [], []),
+      relation('generateCoverArtwork'),
+
+    image:
+      relation('image'),
   }),
 
   data: (flash) => ({
@@ -20,9 +23,13 @@ export default {
 
   generate: (data, relations, {language}) =>
     relations.coverArtwork.slots({
-      path: data.path,
-      color: data.color,
+      image:
+        relations.image.slots({
+          data: data.path,
+          color: data.color,
+          alt: language.$('misc.alt.flashArt'),
+        }),
+
       dimensions: data.dimensions,
-      alt: language.$('misc.alt.flashArt'),
     }),
 };
diff --git a/src/content/dependencies/generateReferencedArtworksPage.js b/src/content/dependencies/generateReferencedArtworksPage.js
index fa65a245..3d21b15d 100644
--- a/src/content/dependencies/generateReferencedArtworksPage.js
+++ b/src/content/dependencies/generateReferencedArtworksPage.js
@@ -77,8 +77,7 @@ export default {
         styleRules: slots.styleRules,
 
         cover:
-          slots.cover
-            .slot('mode', 'primary-artists'),
+          slots.cover.slot('details', 'artists'),
 
         mainClasses: ['top-index'],
         mainContent: [
diff --git a/src/content/dependencies/generateReferencingArtworksPage.js b/src/content/dependencies/generateReferencingArtworksPage.js
index 468d5f78..78dae5b0 100644
--- a/src/content/dependencies/generateReferencingArtworksPage.js
+++ b/src/content/dependencies/generateReferencingArtworksPage.js
@@ -77,8 +77,7 @@ export default {
         styleRules: slots.styleRules,
 
         cover:
-          slots.cover
-            .slot('mode', 'primary-artists'),
+          slots.cover.slot('details', 'artists'),
 
         mainClasses: ['top-index'],
         mainContent: [
diff --git a/src/content/dependencies/generateStickyHeadingContainer.js b/src/content/dependencies/generateStickyHeadingContainer.js
index 7f271715..ab607a4f 100644
--- a/src/content/dependencies/generateStickyHeadingContainer.js
+++ b/src/content/dependencies/generateStickyHeadingContainer.js
@@ -28,11 +28,63 @@ export default {
             html.tag('div', {class: 'content-sticky-heading-cover'},
               {[html.onlyIfContent]: true},
 
-              // TODO: We shouldn't need to do an isBlank check here,
-              // but a live blank value doesn't have a slot functions, so.
-              (html.isBlank(slots.cover)
-                ? html.blank()
-                : slots.cover.slot('mode', 'thumbnail')))),
+              (() => {
+                if (html.isBlank(slots.cover)) {
+                  return html.blank();
+                }
+
+                // Try very hard to set the cover's 'mode' slot to 'thumbnail'
+                // and its 'details' slot to html.blank().
+                let setMode = false;
+                let setDetails = false;
+                let cover = slots.cover;
+                while (true) {
+                  if (!cover) {
+                    return html.blank();
+                  }
+
+                  if (!(cover instanceof html.Template)) {
+                    return cover;
+                  }
+
+                  // The cover from `slots` is already cloned (since it's
+                  // mutable), but later ones are not, and for extremely scary
+                  // content function infrastructure reasons, it is possible
+                  // for the identity of the content of the clone-template
+                  // to be the same as the cloned template.
+                  if (cover !== slots.cover) {
+                    cover = cover.clone();
+                  }
+
+                  if (!setMode) {
+                    try {
+                      cover.setSlot('mode', 'thumbnail');
+                      setMode = true;
+                    } catch {
+                      // No mode slot, or it doesn't allow 'thumbnail'.
+                    }
+                  }
+
+                  if (!setDetails) {
+                    try {
+                      cover.setSlot('details', html.blank());
+                      setDetails = true;
+                    } catch {
+                      // No details slot, or it doesn't allow html.blank().
+                      // We're setting a blank instead of null because null is
+                      // always allowed, and can carry a different semantic
+                      // meaning, like "put something else here by default
+                      // instead please".
+                    }
+                  }
+
+                  if (setMode && setDetails) {
+                    return cover;
+                  }
+
+                  cover = cover.content;
+                }
+              })())),
         ]),
 
         html.tag('div', {class: 'content-sticky-subheading-row'},
diff --git a/src/content/dependencies/generateTrackCoverArtwork.js b/src/content/dependencies/generateTrackCoverArtwork.js
index 64690c71..f75bf00b 100644
--- a/src/content/dependencies/generateTrackCoverArtwork.js
+++ b/src/content/dependencies/generateTrackCoverArtwork.js
@@ -1,13 +1,28 @@
 export default {
-  contentDependencies: ['generateCoverArtwork'],
-  extraDependencies: ['language'],
+  contentDependencies: [
+    'generateCoverArtwork',
+    'generateCoverArtworkArtTagDetails',
+    'generateCoverArtworkArtistDetails',
+    'image',
+  ],
+
+  extraDependencies: ['html', 'language'],
 
   relations: (relation, track) => ({
     coverArtwork:
-      relation('generateCoverArtwork',
+      relation('generateCoverArtwork'),
+
+    image:
+      relation('image'),
+
+    artTagDetails:
+      relation('generateCoverArtworkArtTagDetails',
         (track.hasUniqueCoverArt
           ? track.artTags
-          : track.album.artTags),
+          : track.album.artTags)),
+
+    artistDetails:
+      relation('generateCoverArtworkArtistDetails',
         (track.hasUniqueCoverArt
           ? track.coverArtistContribs
           : track.album.coverArtistContribs)),
@@ -28,12 +43,30 @@ export default {
         : track.album.coverArtDimensions),
   }),
 
-  generate: (data, relations, {language}) =>
+  slots: {
+    details: {
+      validate: v => v.is('tags', 'artists'),
+      default: 'tags',
+    },
+  },
+
+  generate: (data, relations, slots, {language}) =>
     relations.coverArtwork.slots({
-      path: data.path,
-      color: data.color,
+      image:
+        relations.image.slots({
+          path: data.path,
+          color: data.color,
+          alt: language.$('misc.alt.trackCover'),
+        }),
+
       dimensions: data.dimensions,
-      alt: language.$('misc.alt.trackCover'),
+
+      details:
+        (slots.details === 'tags'
+          ? relations.artTagDetails
+       : slots.details === 'artists'
+          ? relations.artistDetails
+          : null),
     }),
 };