« 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/common-util/wiki-data.js5
-rw-r--r--src/content/dependencies/generateArtistInfoPageChunk.js47
-rw-r--r--src/content/dependencies/generateArtistInfoPageTracksChunk.js17
-rw-r--r--src/content/dependencies/generateArtistInfoPageTracksChunkItem.js17
-rw-r--r--src/content/dependencies/generateTrackListItem.js115
-rw-r--r--src/content/dependencies/generateTrackListMissingDuration.js37
-rw-r--r--src/strings-default.yaml34
7 files changed, 173 insertions, 99 deletions
diff --git a/src/common-util/wiki-data.js b/src/common-util/wiki-data.js
index 21e15725..54f8b7ed 100644
--- a/src/common-util/wiki-data.js
+++ b/src/common-util/wiki-data.js
@@ -262,17 +262,14 @@ export function getArtistAvatar(artist, {to}) {
 // Used in multiple content functions for the artist info page,
 // because shared logic is torture oooooooooooooooo.
 export function chunkArtistTrackContributions(contributions) {
-  const date = contrib => contrib.date;
-
   const album = contrib =>
     (contrib.thing.isTrack
       ? contrib.thing.album
       : contrib.thing);
 
   return (
-    // First chunk by (contribution) date and album.
+    // First chunk by (contribution) album.
     chunkByConditions(contributions, [
-      (a, b) => +date(a) !== +date(b),
       (a, b) => album(a) !== album(b),
     ]).map(contribs =>
         // Then, *within* the boundaries of the existing chunks,
diff --git a/src/content/dependencies/generateArtistInfoPageChunk.js b/src/content/dependencies/generateArtistInfoPageChunk.js
index 3fa46c61..e19030c9 100644
--- a/src/content/dependencies/generateArtistInfoPageChunk.js
+++ b/src/content/dependencies/generateArtistInfoPageChunk.js
@@ -18,30 +18,30 @@ export default {
       mutable: false,
     },
 
-    dates: {
-      validate: v => v.sparseArrayOf(v.isDate),
-    },
+    // Container and items, respectively.
+    date: {validate: v => v.isDate},
+    dates: {validate: v => v.sparseArrayOf(v.isDate)},
 
     duration: {validate: v => v.isDuration},
     durationApproximate: {type: 'boolean'},
   },
 
   generate(slots, {html, language}) {
-    let earliestDate = null;
-    let latestDate = null;
-    let onlyDate = null;
+    let earliestItemDate = null;
+    let latestItemDate = null;
+    let onlyItemDate = null;
 
     if (!empty(slots.dates)) {
-      earliestDate =
-        slots.dates
-          .reduce((a, b) => a <= b ? a : b);
+      earliestItemDate = slots.dates[0];
+      latestItemDate = slots.dates[1];
 
-      latestDate =
-        slots.dates
-          .reduce((a, b) => a <= b ? b : a);
+      for (const date of slots.dates.slice(1)) {
+        if (date < earliestItemDate) earliestItemDate = date;
+        if (date > latestItemDate) latestItemDate = date;
+      }
 
-      if (+earliestDate === +latestDate) {
-        onlyDate = earliestDate;
+      if (+earliestItemDate === +latestItemDate) {
+        onlyItemDate = earliestItemDate;
       }
     }
 
@@ -51,9 +51,16 @@ export default {
         const options = {album: slots.link};
         const parts = ['artistPage.creditList.album'];
 
-        if (onlyDate) {
+        if (slots.date) {
+          parts.push('withDate');
+          options.date = language.formatDate(slots.date);
+        } else if (onlyItemDate) {
           parts.push('withDate');
-          options.date = language.formatDate(onlyDate);
+          options.date = language.formatDate(onlyItemDate);
+        } else if (earliestItemDate && latestItemDate) {
+          parts.push('withDateRange');
+          options.dateRange =
+            language.formatDateRange(earliestItemDate, latestItemDate);
         }
 
         if (slots.duration) {
@@ -72,13 +79,13 @@ export default {
         const options = {act: slots.link};
         const parts = ['artistPage.creditList.flashAct'];
 
-        if (onlyDate) {
+        if (onlyItemDate) {
           parts.push('withDate');
-          options.date = language.formatDate(onlyDate);
-        } else if (earliestDate && latestDate) {
+          options.date = language.formatDate(onlyItemDate);
+        } else if (earliestItemDate && latestItemDate) {
           parts.push('withDateRange');
           options.dateRange =
-            language.formatDateRange(earliestDate, latestDate);
+            language.formatDateRange(earliestItemDate, latestItemDate);
         }
 
         accentedLink = language.formatString(...parts, options);
diff --git a/src/content/dependencies/generateArtistInfoPageTracksChunk.js b/src/content/dependencies/generateArtistInfoPageTracksChunk.js
index b3727756..7a7fc6a9 100644
--- a/src/content/dependencies/generateArtistInfoPageTracksChunk.js
+++ b/src/content/dependencies/generateArtistInfoPageTracksChunk.js
@@ -61,7 +61,7 @@ export default {
         .filter(contribs => countTowardTrackTotals(contribs) === false),
   }),
 
-  relations: (relation, query, artist, album, _trackContribLists) => ({
+  relations: (relation, query, artist, album, trackContribLists) => ({
     template:
       relation('generateArtistInfoPageChunk'),
 
@@ -82,13 +82,15 @@ export default {
       query.contribListsCountingTowardTotals.map(trackContribs =>
         relation('generateArtistInfoPageTracksChunkItem',
           artist,
-          trackContribs)),
+          trackContribs,
+          trackContribLists)),
 
     itemsNotCountingTowardTotals:
       query.contribListsNotCountingTowardTotals.map(trackContribs =>
         relation('generateArtistInfoPageTracksChunkItem',
           artist,
-          trackContribs)),
+          trackContribs,
+          trackContribLists)),
   }),
 
   data(artist, _query, album, trackContribLists) {
@@ -97,7 +99,10 @@ export default {
     const contribs =
       trackContribLists.flat();
 
-    data.dates =
+    data.albumDate =
+      album.date;
+
+    data.contribDates =
       contribs
         .map(contrib => contrib.date);
 
@@ -168,7 +173,9 @@ export default {
           return language.$(workingCapsule, workingOptions);
         }),
 
-      dates: data.dates,
+      date: data.albumDate,
+      dates: data.contribDates,
+
       duration: data.duration,
       durationApproximate: data.durationApproximate,
 
diff --git a/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js b/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js
index 3d6e274b..69d8eebd 100644
--- a/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js
+++ b/src/content/dependencies/generateArtistInfoPageTracksChunkItem.js
@@ -2,14 +2,19 @@ import {sortAlbumsTracksChronologically} from '#sort';
 import {empty} from '#sugar';
 
 export default {
-  query(artist, contribs) {
+  query(artist, contribs, chunkContribs) {
     const query = {};
 
-    // TODO: Very mysterious what to do if the set of contributions is,
-    // in total, associated with more than one thing. No design yet.
     query.track =
       contribs[0].thing;
 
+    query.date =
+      contribs[0].date;
+
+    query.anyItemsExpresslyDated =
+      chunkContribs.flat()
+        .some(contrib => +contrib.date !== +query.track.album.date);
+
     const creditedAsNormalArtist =
       contribs
         .some(contrib =>
@@ -112,6 +117,11 @@ export default {
   }),
 
   data: (query) => ({
+    date:
+      (query.anyItemsExpresslyDated
+        ? query.date
+        : null),
+
     duration:
       query.track.duration,
 
@@ -146,6 +156,7 @@ export default {
               relations.trackListItem.slots({
                 showArtists: 'auto',
                 showDuration: slots.showDuration,
+                showDate: data.date,
               })),
         }),
     }),
diff --git a/src/content/dependencies/generateTrackListItem.js b/src/content/dependencies/generateTrackListItem.js
index c8c57534..383f0025 100644
--- a/src/content/dependencies/generateTrackListItem.js
+++ b/src/content/dependencies/generateTrackListItem.js
@@ -25,6 +25,9 @@ export default {
   }),
 
   data: (track, _contextContributions) => ({
+    date:
+      track.date,
+
     duration:
       track.duration ?? 0,
 
@@ -47,6 +50,11 @@ export default {
       default: false,
     },
 
+    showDate: {
+      validate: v => v.anyOf(v.isBoolean, v.isDate),
+      default: false,
+    },
+
     colorMode: {
       validate: v => v.is('none', 'track', 'line'),
       default: 'track',
@@ -62,48 +70,83 @@ export default {
         language.encapsulate(itemCapsule, workingCapsule => {
           const workingOptions = {};
 
-          workingOptions.track =
-            relations.trackLink
-              .slot('color', slots.colorMode === 'track');
+          const accent =
+            language.encapsulate(itemCapsule, 'accent', accentCapsule => {
+              let workingCapsule = accentCapsule;
+              let workingOptions = {};
+              let any = false;
+
+              if (slots.showDate) {
+                any = true;
+                workingCapsule += '.withDate';
+                workingOptions.date =
+                  language.$(accentCapsule, 'date', {
+                    date:
+                      (slots.showDate === true
+                        ? language.formatDate(data.date)
+                        : language.formatDate(slots.showDate)),
+                  });
+              }
+
+              if (slots.showDuration) {
+                any = true;
+                workingCapsule += '.withDuration';
+                workingOptions.duration =
+                  (data.trackHasDuration
+                    ? language.$(accentCapsule, 'duration', {
+                        duration:
+                          language.formatDuration(data.duration),
+                      })
+                    : relations.missingDuration);
+              }
+
+              if (any) {
+                return language.$(workingCapsule, workingOptions);
+              } else {
+                return html.blank();
+              }
+            });
 
-          if (slots.showDuration) {
-            workingCapsule += '.withDuration';
-            workingOptions.duration =
-              (data.trackHasDuration
-                ? language.$(itemCapsule, 'withDuration.duration', {
-                    duration:
-                      language.formatDuration(data.duration),
-                  })
-                : relations.missingDuration);
+          if (!html.isBlank(accent)) {
+            workingCapsule += '.withAccent';
+            workingOptions.accent = accent;
           }
 
-          const chosenCredit =
-            (slots.showArtists === true
-              ? relations.acontextualCredit
-           : slots.showArtists === 'auto'
-              ? relations.contextualCredit
-              : null);
-
-          if (chosenCredit) {
-            const artistCapsule = language.encapsulate(itemCapsule, 'withArtists');
-
-            chosenCredit.setSlots({
-              normalStringKey:
-                artistCapsule + '.by',
-
-              featuringStringKey:
-                artistCapsule + '.featuring',
+          workingOptions.track =
+            relations.trackLink
+              .slot('color', slots.colorMode === 'track');
 
-              normalFeaturingStringKey:
-                artistCapsule + '.by.featuring',
+          const artists =
+            language.encapsulate(itemCapsule, 'artists', artistsCapsule => {
+              const chosenCredit =
+                (slots.showArtists === true
+                  ? relations.acontextualCredit
+               : slots.showArtists === 'auto'
+                  ? relations.contextualCredit
+                  : null);
+
+              if (!chosenCredit) {
+                return html.blank();
+              }
+
+              // This might still be blank, if the contextual credit is chosen
+              // and it matches its context credit.
+              return chosenCredit.slots({
+                normalStringKey:
+                  artistsCapsule + '.by',
+
+                featuringStringKey:
+                  artistsCapsule + '.featuring',
+
+                normalFeaturingStringKey:
+                  artistsCapsule + '.by.featuring',
+              });
             });
 
-            if (!html.isBlank(chosenCredit)) {
-              workingCapsule += '.withArtists';
-              workingOptions.by =
-                html.tag('span', {class: 'by'},
-                  chosenCredit);
-            }
+          if (!html.isBlank(artists)) {
+            workingCapsule += '.withArtists';
+            workingOptions.artists =
+              html.tag('span', {class: 'by'}, artists);
           }
 
           return language.$(workingCapsule, workingOptions);
diff --git a/src/content/dependencies/generateTrackListMissingDuration.js b/src/content/dependencies/generateTrackListMissingDuration.js
index 70db23c2..f3c5c6ce 100644
--- a/src/content/dependencies/generateTrackListMissingDuration.js
+++ b/src/content/dependencies/generateTrackListMissingDuration.js
@@ -8,27 +8,26 @@ export default {
   }),
 
   generate: (relations, {html, language}) =>
-    language.encapsulate('trackList.item.withDuration', itemCapsule =>
-      language.encapsulate(itemCapsule, 'duration', durationCapsule =>
-        relations.textWithTooltip.slots({
-          attributes: {class: 'missing-duration'},
-          customInteractionCue: true,
+    language.encapsulate('trackList.item.accent.duration', capsule =>
+      relations.textWithTooltip.slots({
+        attributes: {class: 'missing-duration'},
+        customInteractionCue: true,
 
-          text:
-            language.$(durationCapsule, {
-              duration:
-                html.tag('span', {class: 'text-with-tooltip-interaction-cue'},
-                  {tabindex: '0'},
+        text:
+          language.$(capsule, {
+            duration:
+              html.tag('span', {class: 'text-with-tooltip-interaction-cue'},
+                {tabindex: '0'},
 
-                  language.$(durationCapsule, 'missing')),
-            }),
+                language.$(capsule, 'missing')),
+          }),
 
-          tooltip:
-            relations.tooltip.slots({
-              attributes: {class: 'missing-duration-tooltip'},
+        tooltip:
+          relations.tooltip.slots({
+            attributes: {class: 'missing-duration-tooltip'},
 
-              content:
-                language.$(durationCapsule, 'missing.info'),
-            }),
-        }))),
+            content:
+              language.$(capsule, 'missing.info'),
+          }),
+      })),
 };
diff --git a/src/strings-default.yaml b/src/strings-default.yaml
index daca8347..e8bda92f 100644
--- a/src/strings-default.yaml
+++ b/src/strings-default.yaml
@@ -447,25 +447,35 @@ trackList:
   item:
     _: "{TRACK}"
 
-    withDuration:
-      _: >-
-        {DURATION} {TRACK}
+    withAccent: >-
+      {ACCENT} {TRACK}
 
-      duration:
-        _: "({DURATION})"
-        missing: "_:__"
-        missing.info: "no duration provided; treated as zero seconds long"
+    accent.withDate: >-
+      ({DATE})
 
-    withArtists:
-      _: >-
-        {TRACK} {BY}
+    accent.withDate.withDuration: >-
+      ({DURATION}; {DATE})
+
+    accent.withDuration: >-
+      ({DURATION})
+
+    accent.date: "{DATE}"
+
+    accent.duration:
+      _: "{DURATION}"
+      missing: "_:__"
+      missing.info: "no duration provided; treated as zero seconds long"
+
+    withArtists: >-
+      {TRACK} {ARTISTS}
 
+    artists:
       by: "by {ARTISTS}"
       featuring: "feat. {ARTISTS}"
       by.featuring: "by {ARTISTS} feat. {FEATURING}"
 
-    withDuration.withArtists: >-
-      {DURATION} {TRACK} {BY}
+    withAccent.withArtists: >-
+      {ACCENT} {TRACK} {ARTISTS}
 
     rerelease: >-
       {TRACK} (rerelease)