« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/content/dependencies/generateArtistInfoPage.js9
-rw-r--r--src/content/dependencies/generateCoverArtwork.js2
-rw-r--r--src/content/dependencies/generatePageLayout.js2
-rw-r--r--src/content/dependencies/generateStickyHeadingContainer.js8
-rw-r--r--src/content/dependencies/generateTrackInfoPage.js1
-rw-r--r--src/content/dependencies/generateTrackInfoPageContent.js19
-rw-r--r--src/content/dependencies/image.js8
-rw-r--r--src/data/things/thing.js26
-rw-r--r--src/data/things/track.js2
-rw-r--r--src/data/yaml.js2
-rw-r--r--src/listing-spec.js3
-rw-r--r--src/page/artist.js13
-rw-r--r--src/static/site4.css4
-rwxr-xr-xsrc/upd8.js2
-rw-r--r--src/util/wiki-data.js105
15 files changed, 143 insertions, 63 deletions
diff --git a/src/content/dependencies/generateArtistInfoPage.js b/src/content/dependencies/generateArtistInfoPage.js
index 821512b8..a91eebf2 100644
--- a/src/content/dependencies/generateArtistInfoPage.js
+++ b/src/content/dependencies/generateArtistInfoPage.js
@@ -585,7 +585,7 @@ export function write(artist, {wikiData}) {
 
   let flashes, flashListChunks;
   if (wikiInfo.enableFlashesAndGames) {
-    flashes = sortChronologically(artist.flashesAsContributor.slice());
+    flashes = sortFlashesChronologically(artist.flashesAsContributor.slice());
     flashListChunks = chunkByProperties(
       flashes.map((flash) => ({
         act: flash.act,
@@ -760,6 +760,13 @@ export function write(artist, {wikiData}) {
     },
   };
 
+  const artThingsGallery = sortAlbumsTracksChronologically(
+    [
+      ...(artist.albumsAsCoverArtist ?? []),
+      ...(artist.tracksAsCoverArtist ?? []),
+    ],
+    {latestFirst: true, getDate: (o) => o.coverArtDate});
+
   const galleryPage = hasGallery && {
     type: 'page',
     path: ['artistGallery', artist.directory],
diff --git a/src/content/dependencies/generateCoverArtwork.js b/src/content/dependencies/generateCoverArtwork.js
index 68680060..a7a7f859 100644
--- a/src/content/dependencies/generateCoverArtwork.js
+++ b/src/content/dependencies/generateCoverArtwork.js
@@ -51,6 +51,7 @@ export default {
                   alt: slots.alt,
                   thumb: 'medium',
                   id: 'cover-art',
+                  reveal: true,
                   link: true,
                   square: true,
                 }),
@@ -68,6 +69,7 @@ export default {
                 path: slots.path,
                 alt: slots.alt,
                 thumb: 'small',
+                reveal: false,
                 link: false,
                 square: true,
               });
diff --git a/src/content/dependencies/generatePageLayout.js b/src/content/dependencies/generatePageLayout.js
index 32effbfe..55f5b940 100644
--- a/src/content/dependencies/generatePageLayout.js
+++ b/src/content/dependencies/generatePageLayout.js
@@ -102,6 +102,7 @@ export default {
       slots: {
         title: {type: 'html'},
         cover: {type: 'html'},
+        coverNeedsReveal: {type: 'boolean'},
 
         socialEmbed: {type: 'html'},
 
@@ -201,6 +202,7 @@ export default {
                 relations.stickyHeadingContainer.slots({
                   title: slots.title,
                   cover: slots.cover,
+                  needsReveal: slots.coverNeedsReveal,
                 });
               break;
             case 'static':
diff --git a/src/content/dependencies/generateStickyHeadingContainer.js b/src/content/dependencies/generateStickyHeadingContainer.js
index c08ca08d..fb6d8307 100644
--- a/src/content/dependencies/generateStickyHeadingContainer.js
+++ b/src/content/dependencies/generateStickyHeadingContainer.js
@@ -8,6 +8,7 @@ export default {
       slots: {
         title: {type: 'html'},
         cover: {type: 'html'},
+        needsReveal: {type: 'boolean', default: false},
       },
 
       content(slots) {
@@ -26,7 +27,12 @@ export default {
 
               hasCover &&
                 html.tag('div', {class: 'content-sticky-heading-cover-container'},
-                  html.tag('div', {class: 'content-sticky-heading-cover'},
+                  html.tag('div',
+                    {class: [
+                      'content-sticky-heading-cover',
+                      slots.needsReveal &&
+                        'content-sticky-heading-cover-needs-reveal',
+                    ]},
                     slots.cover.slot('displayMode', 'thumbnail')))
             ]),
 
diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js
index 4795bac6..ee68f534 100644
--- a/src/content/dependencies/generateTrackInfoPage.js
+++ b/src/content/dependencies/generateTrackInfoPage.js
@@ -84,6 +84,7 @@ export default {
         additionalStyleRules: [relations.albumStyleRules],
 
         cover: relations.content.cover,
+        coverNeedsReveal: relations.content.coverNeedsReveal,
         mainContent: relations.content.main.content,
 
         navLinkStyle: 'hierarchical',
diff --git a/src/content/dependencies/generateTrackInfoPageContent.js b/src/content/dependencies/generateTrackInfoPageContent.js
index 67975cb4..c33c2f62 100644
--- a/src/content/dependencies/generateTrackInfoPageContent.js
+++ b/src/content/dependencies/generateTrackInfoPageContent.js
@@ -1,5 +1,5 @@
 import {empty} from '../../util/sugar.js';
-import {sortChronologically} from '../../util/wiki-data.js';
+import {sortFlashesChronologically} from '../../util/wiki-data.js';
 
 export default {
   contentDependencies: [
@@ -12,6 +12,7 @@ export default {
     'linkAlbum',
     'linkContribution',
     'linkExternal',
+    'linkFlash',
     'linkTrack',
     'transformContent',
   ],
@@ -32,8 +33,10 @@ export default {
     const sections = relations.sections = {};
 
     const contributionLinksRelation = contribs =>
-      contribs.map(contrib =>
-        relation('linkContribution', contrib.who, contrib.what));
+      contribs
+        .slice(0, 4)
+        .map(contrib =>
+          relation('linkContribution', contrib.who, contrib.what));
 
     const additionalFilesSection = additionalFiles => ({
       heading: relation('generateContentHeading'),
@@ -161,7 +164,7 @@ export default {
 
     if (sprawl.enableFlashesAndGames) {
       const sortedFeatures =
-        sortChronologically(
+        sortFlashesChronologically(
           [track, ...track.otherReleases].flatMap(track =>
             track.featuredInFlashes.map(flash => ({
               // These aren't going to be exposed directly, they're processed
@@ -169,9 +172,8 @@ export default {
               flash, track,
 
               // These properties are only used for the sort.
+              act: flash.act,
               date: flash.date,
-              name: flash.name,
-              directory: flash.directory,
             }))));
 
       if (!empty(sortedFeatures)) {
@@ -250,6 +252,7 @@ export default {
       data.albumCoverArtDirectory = album.directory;
       data.trackCoverArtDirectory = track.directory;
       data.coverArtFileExtension = track.coverArtFileExtension;
+      data.coverNeedsReveal = track.artTags.some(t => t.isContentWarning);
 
       if (track.coverArtDate && +track.coverArtDate !== +track.date) {
         data.coverArtDate = track.coverArtDate;
@@ -257,6 +260,7 @@ export default {
     } else if (track.album.hasCoverArt) {
       data.albumCoverArtDirectory = album.directory;
       data.coverArtFileExtension = album.coverArtFileExtension;
+      data.coverNeedsReveal = album.artTags.some(t => t.isContentWarning);
     }
 
     if (!empty(track.additionalFiles)) {
@@ -291,6 +295,7 @@ export default {
             data.coverArtFileExtension,
           ],
         });
+      content.coverNeedsReveal = data.coverNeedsReveal;
     } else if (data.hasAlbumCoverArt) {
       content.cover = relations.cover
         .slots({
@@ -300,8 +305,10 @@ export default {
             data.coverArtFileExtension,
           ],
         });
+      content.coverNeedsReveal = data.coverNeedsReveal;
     } else {
       content.cover = null;
+      content.coverNeedsReveal = null;
     }
 
     content.main = {
diff --git a/src/content/dependencies/image.js b/src/content/dependencies/image.js
index 822a8996..f9cb00bf 100644
--- a/src/content/dependencies/image.js
+++ b/src/content/dependencies/image.js
@@ -45,6 +45,7 @@ export default {
 
         thumb: {type: 'string'},
 
+        reveal: {type: 'boolean', default: true},
         link: {type: 'boolean', default: false},
         lazy: {type: 'boolean', default: false},
         square: {type: 'boolean', default: false},
@@ -75,7 +76,12 @@ export default {
               : originalSrc);
 
         const willLink = typeof slots.link === 'string' || slots.link;
-        const willReveal = originalSrc && !empty(data.contentWarnings);
+
+        const willReveal =
+          slots.reveal &&
+          originalSrc &&
+          !empty(data.contentWarnings);
+
         const willSquare = slots.square;
 
         const idOnImg = willLink ? null : slots.id;
diff --git a/src/data/things/thing.js b/src/data/things/thing.js
index b74f45f5..5004f4e6 100644
--- a/src/data/things/thing.js
+++ b/src/data/things/thing.js
@@ -264,26 +264,42 @@ export default class Thing extends CacheableObject {
     // Dynamically inherit a contribution list from some other object, if it
     // hasn't been overridden on this object. This is handy for solo albums
     // where all tracks have the same artist, for example.
-    //
-    // Note: The arguments of this function aren't currently final! The final
-    // format will look more like (contribsByRef, parentContribsByRef), e.g.
-    // ('artistContribsByRef', '@album/artistContribsByRef').
     dynamicInheritContribs: (
+      // If this property is explicitly false, the contribution list returned
+      // will always be empty.
+      nullerProperty,
+
+      // Property holding contributions on the current object.
       contribsByRefProperty,
+
+      // Property holding corresponding "default" contributions on the parent
+      // object, which will fallen back to if the object doesn't have its own
+      // contribs.
       parentContribsByRefProperty,
+
+      // Data array to search in and "find" function to locate parent object
+      // (which will be passed the child object and the wiki data array).
       thingDataProperty,
       findFn
     ) => ({
       flags: {expose: true},
       expose: {
-        dependencies: [contribsByRefProperty, thingDataProperty, 'artistData'],
+        dependencies: [
+          contribsByRefProperty,
+          thingDataProperty,
+          nullerProperty,
+          'artistData',
+        ].filter(Boolean),
+
         compute({
           [Thing.instance]: thing,
+          [nullerProperty]: nuller,
           [contribsByRefProperty]: contribsByRef,
           [thingDataProperty]: thingData,
           artistData,
         }) {
           if (!artistData) return [];
+          if (nuller === false) return [];
           const refs =
             contribsByRef ??
             findFn(thing, thingData, {mode: 'quiet'})?.[parentContribsByRefProperty];
diff --git a/src/data/things/track.js b/src/data/things/track.js
index 1c2013a2..00585c1e 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -245,6 +245,7 @@ export class Track extends Thing {
     },
 
     artistContribs: Thing.common.dynamicInheritContribs(
+      null,
       'artistContribsByRef',
       'artistContribsByRef',
       'albumData',
@@ -254,6 +255,7 @@ export class Track extends Thing {
     contributorContribs: Thing.common.dynamicContribs('contributorContribsByRef'),
 
     coverArtistContribs: Thing.common.dynamicInheritContribs(
+      'hasCoverArt',
       'coverArtistContribsByRef',
       'trackCoverArtistContribsByRef',
       'albumData',
diff --git a/src/data/yaml.js b/src/data/yaml.js
index 5a6f2031..73450f17 100644
--- a/src/data/yaml.js
+++ b/src/data/yaml.js
@@ -24,6 +24,7 @@ import {
   sortAlbumsTracksChronologically,
   sortAlphabetically,
   sortChronologically,
+  sortFlashesChronologically,
 } from '../util/wiki-data.js';
 
 import find, {bindFind} from '../util/find.js';
@@ -1154,6 +1155,7 @@ export function sortWikiDataArrays(wikiData) {
   Object.assign(wikiData, {
     albumData: sortChronologically(wikiData.albumData.slice()),
     trackData: sortAlbumsTracksChronologically(wikiData.trackData.slice()),
+    flashData: sortFlashesChronologically(wikiData.flashData.slice()),
   });
 
   // Re-link data arrays, so that every object has the new, sorted versions.
diff --git a/src/listing-spec.js b/src/listing-spec.js
index 08799f2f..36637ee0 100644
--- a/src/listing-spec.js
+++ b/src/listing-spec.js
@@ -12,6 +12,7 @@ import {
   sortAlphabetically,
   sortByDate,
   sortChronologically,
+  sortFlashesChronologically,
 } from './util/wiki-data.js';
 
 const listingSpec = [];
@@ -775,7 +776,7 @@ listingSpec.push({
     wikiInfo.enableFlashesAndGames,
 
   data: ({wikiData: {flashData}}) =>
-    sortChronologically(flashData.slice())
+    sortFlashesChronologically(flashData.slice())
       .map(flash => ({
         flash,
         tracks: flash.featuredTracks,
diff --git a/src/page/artist.js b/src/page/artist.js
index 852310d3..ad365161 100644
--- a/src/page/artist.js
+++ b/src/page/artist.js
@@ -2,19 +2,6 @@
 //
 // NB: See artist-alias.js for artist alias redirect pages.
 
-import {
-  bindOpts,
-  empty,
-  unique,
-} from '../util/sugar.js';
-
-import {
-  chunkByProperties,
-  getTotalDuration,
-  sortAlbumsTracksChronologically,
-  sortChronologically,
-} from '../util/wiki-data.js';
-
 export const description = `per-artist info & artwork gallery pages`;
 
 export function targets({wikiData}) {
diff --git a/src/static/site4.css b/src/static/site4.css
index 08512559..28a4924d 100644
--- a/src/static/site4.css
+++ b/src/static/site4.css
@@ -1559,12 +1559,12 @@ html[data-language-code="preview-en"][data-url-key="localized.home"] #content
     z-index: 2;
   }
 
-  html[data-url-key="localized.home"] .layout-columns.has-one-sidebar .grid-listing > .grid-item:not(:nth-child(n+10)) {
+  html[data-url-key="localized.home"] .layout-columns.has-one-sidebar .grid-listing > .grid-item:not(:nth-child(n+7)) {
     flex-basis: 23%;
     margin: 15px;
   }
 
-  html[data-url-key="localized.home"] .layout-columns.has-one-sidebar .grid-listing > .grid-item:nth-child(n+10) {
+  html[data-url-key="localized.home"] .layout-columns.has-one-sidebar .grid-listing > .grid-item:nth-child(n+7) {
     flex-basis: 18%;
     margin: 10px;
   }
diff --git a/src/upd8.js b/src/upd8.js
index ed54ec47..366dc21b 100755
--- a/src/upd8.js
+++ b/src/upd8.js
@@ -87,7 +87,7 @@ import FileSizePreloader from './file-size-preloader.js';
 
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
 
-const CACHEBUST = 19;
+const CACHEBUST = 20;
 
 let COMMIT;
 try {
diff --git a/src/util/wiki-data.js b/src/util/wiki-data.js
index 0eb86a1e..89c621c5 100644
--- a/src/util/wiki-data.js
+++ b/src/util/wiki-data.js
@@ -181,6 +181,7 @@ export function sortByName(data, {
 }
 
 export function sortByDate(data, {
+  latestFirst = false,
   getDate = (o) => o.date,
 } = {}) {
   return data.sort((a, b) => {
@@ -191,7 +192,7 @@ export function sortByDate(data, {
     // together in the same array. If that's the case, we put all items
     // without dates at the end.
     if (ad && bd) {
-      return ad - bd;
+      return (latestFirst ? bd - ad : ad - bd);
     } else if (ad) {
       return -1;
     } else if (bd) {
@@ -206,35 +207,52 @@ export function sortByDate(data, {
   });
 }
 
-export function sortByPositionInAlbum(data) {
+export function sortByPositionInParent(data, {
+  getParent,
+  getChildren,
+}) {
   return data.sort((a, b) => {
-    const aa = a.album;
-    const ba = b.album;
+    const parentA = getParent(a);
+    const parentB = getParent(b);
 
-    // Don't change the sort when the two tracks are from separate albums.
-    // This function doesn't change the order of albums or try to "merge"
-    // two separated chunks of tracks from the same album together.
-    if (aa !== ba) {
+    // Don't change the sort when the two items are from separate parents.
+    // This function doesn't change the order of parents or try to "merge"
+    // two separated chunks of items from the same parent together.
+    if (parentA !== parentB) {
       return 0;
     }
 
-    // Don't change the sort when only one (or neither) item is actually
-    // a track (i.e. has an album).
-    if (!aa || !ba) {
+    // Don't change the sort when either (or both) of the items doesn't
+    // even have a parent (e.g. it's the passed data is a mixed array of
+    // children and parents).
+    if (!parentA || !parentB) {
       return 0;
     }
 
-    const ai = aa.tracks.indexOf(a);
-    const bi = ba.tracks.indexOf(b);
+    const indexA = getChildren(parentA).indexOf(a);
+    const indexB = getChildren(parentB).indexOf(b);
 
-    // There's no reason this two-way reference (a track's album and the
-    // album's track list) should be broken, but if for any reason it is,
-    // don't change the sort.
-    if (ai === -1 || bi === -1) {
+    // If the getParent/getChildren relationship doesn't go both ways for
+    // some reason, don't change the sort.
+    if (indexA === -1 || indexB === -1) {
       return 0;
     }
 
-    return ai - bi;
+    return indexA - indexB;
+  });
+}
+
+export function sortByPositionInAlbum(data) {
+  return sortByPositionInParent(data, {
+    getParent: track => track.album,
+    getChildren: album => album.tracks,
+  });
+}
+
+export function sortByPositionInFlashAct(data) {
+  return sortByPositionInParent(data, {
+    getParent: flash => flash.act,
+    getChildren: act => act.flashes,
   });
 }
 
@@ -242,7 +260,7 @@ export function sortByPositionInAlbum(data) {
 // set of arbitrary given conditions is true first. If no conditions are met
 // for a given item, it's moved over to the end!
 export function sortByConditions(data, conditions) {
-  data.sort((a, b) => {
+  return data.sort((a, b) => {
     const ai = conditions.findIndex((f) => f(a));
     const bi = conditions.findIndex((f) => f(b));
 
@@ -292,18 +310,8 @@ export function sortChronologically(data, {
   getName,
   getDate,
 } = {}) {
-  if (latestFirst) {
-    // Double reverse: Since we reverse after sorting by date, also reverse
-    // after sorting A-Z, so the second reverse restores A-Z relative
-    // positioning (for entries with the same date).
-    sortAlphabetically(data, {getDirectory, getName});
-    data.reverse();
-    sortByDate(data, {getDate});
-    data.reverse();
-  } else {
-    sortAlphabetically(data, {getDirectory, getName});
-    sortByDate(data, {getDate});
-  }
+  sortAlphabetically(data, {getDirectory, getName});
+  sortByDate(data, {latestFirst, getDate});
   return data;
 }
 
@@ -316,6 +324,7 @@ export function sortChronologically(data, {
 //
 // This function also works for data lists which contain only tracks.
 export function sortAlbumsTracksChronologically(data, {
+  latestFirst = false,
   getDate,
 } = {}) {
   // Sort albums before tracks...
@@ -333,7 +342,39 @@ export function sortAlbumsTracksChronologically(data, {
   // released on the same date, they'll still be grouped together by album,
   // and tracks within an album will retain their relative positioning (i.e.
   // stay in the same order as part of the album's track listing).
-  sortByDate(data, {getDate});
+  sortByDate(data, {latestFirst, getDate});
+
+  return data;
+}
+
+export function sortFlashesChronologically(data, {
+  latestFirst = false,
+  getDate,
+} = {}) {
+  // Flash acts don't actually have any identifying properties because they
+  // don't have dedicated pages (yet), so don't have a directory. Make up a
+  // fake key identifying them so flashes can be grouped together.
+  const flashActs = new Set(data.map(flash => flash.act));
+  const flashActIdentifiers = new Map();
+
+  let counter = 0;
+  for (const act of flashActs) {
+    flashActIdentifiers.set(act, ++counter);
+  }
+
+  // Group flashes by act...
+  data.sort((a, b) => {
+    return flashActIdentifiers.get(a.act) - flashActIdentifiers.get(b.act);
+  });
+
+  // Sort flashes by position in act...
+  sortByPositionInFlashAct(data);
+
+  // ...and finally sort by date. If flashes from more than one act were
+  // released on the same date, they'll still be grouped together by act,
+  // and flashes within an act will retain their relative positioning (i.e.
+  // stay in the same order as the act's flash listing).
+  sortByDate(data, {latestFirst, getDate});
 
   return data;
 }