« get me outta code hell

Merge pull request #117 from hsmusic/track-sections - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2023-01-15 20:37:34 -0400
committerGitHub <noreply@github.com>2023-01-15 20:37:34 -0400
commit62dbb79342927cb1fe19651e33fcc27bc2804f6e (patch)
tree313c34f694337cae483047d9e06f0a410e44d155
parent088a0d4ef42c90d6e81586601ee6cb37340b5bf4 (diff)
parent8093844a5460683b48219dba238b0e6e864dacd8 (diff)
Merge pull request #117 from hsmusic/track-sections
Change trackGroups to trackSections, avoid usage of a dedicated "domain-specific" Thing subclass
-rw-r--r--src/data/things/album.js113
-rw-r--r--src/data/things/track.js6
-rw-r--r--src/data/things/validators.js14
-rw-r--r--src/data/yaml.js69
-rw-r--r--src/page/album.js34
-rw-r--r--src/strings-default.json2
-rw-r--r--test/things.js8
7 files changed, 110 insertions, 136 deletions
diff --git a/src/data/things/album.js b/src/data/things/album.js
index ade34e3d..0c0c7fb4 100644
--- a/src/data/things/album.js
+++ b/src/data/things/album.js
@@ -10,13 +10,11 @@ export class Album extends Thing {
     Artist,
     Group,
     Track,
-    TrackGroup,
 
     validators: {
       isDate,
       isDimensions,
-      validateArrayItems,
-      validateInstanceOf,
+      isTrackSectionList,
     },
   }) => ({
     // Update & expose
@@ -56,11 +54,39 @@ export class Album extends Thing {
     groupsByRef: Thing.common.referenceList(Group),
     artTagsByRef: Thing.common.referenceList(ArtTag),
 
-    trackGroups: {
+    trackSections: {
       flags: {update: true, expose: true},
 
       update: {
-        validate: validateArrayItems(validateInstanceOf(TrackGroup)),
+        validate: isTrackSectionList,
+      },
+
+      expose: {
+        dependencies: ['color', 'trackData'],
+        transform(trackSections, {
+          color: albumColor,
+          trackData,
+        }) {
+          let startIndex = 0;
+          return trackSections?.map(section => ({
+            name: section.name ?? null,
+            color: section.color ?? albumColor ?? null,
+            dateOriginallyReleased: section.dateOriginallyReleased ?? null,
+            isDefaultTrackSection: section.isDefaultTrackSection ?? false,
+
+            startIndex: (
+              startIndex += section.tracksByRef.length,
+              startIndex - section.tracksByRef.length
+            ),
+
+            tracksByRef: section.tracksByRef ?? [],
+            tracks:
+              (trackData && section.tracksByRef
+                ?.map(ref => find.track(ref, trackData, {mode: 'quiet'}))
+                .filter(Boolean)) ??
+              [],
+          }));
+        },
       },
     },
 
@@ -114,11 +140,11 @@ export class Album extends Thing {
       flags: {expose: true},
 
       expose: {
-        dependencies: ['trackGroups', 'trackData'],
-        compute: ({trackGroups, trackData}) =>
-          trackGroups && trackData
-            ? trackGroups
-                .flatMap((group) => group.tracksByRef ?? [])
+        dependencies: ['trackSections', 'trackData'],
+        compute: ({trackSections, trackData}) =>
+          trackSections && trackData
+            ? trackSections
+                .flatMap((section) => section.tracksByRef ?? [])
                 .map((ref) => find.track(ref, trackData, {mode: 'quiet'}))
                 .filter(Boolean)
             : [],
@@ -179,72 +205,11 @@ export class Album extends Thing {
   });
 }
 
-export class TrackGroup extends Thing {
-  static [Thing.getPropertyDescriptors] = ({
-    isColor,
-    Track,
-
-    validators: {
-      validateInstanceOf,
-    },
-  }) => ({
-    // Update & expose
-
+export class TrackSectionHelper extends Thing {
+  static [Thing.getPropertyDescriptors] = () => ({
     name: Thing.common.name('Unnamed Track Group'),
-
-    color: {
-      flags: {update: true, expose: true},
-
-      update: {validate: isColor},
-
-      expose: {
-        dependencies: ['album'],
-
-        transform(color, {album}) {
-          return color ?? album?.color ?? null;
-        },
-      },
-    },
-
+    color: Thing.common.color(),
     dateOriginallyReleased: Thing.common.simpleDate(),
-
-    tracksByRef: Thing.common.referenceList(Track),
-
     isDefaultTrackGroup: Thing.common.flag(false),
-
-    // Update only
-
-    album: {
-      flags: {update: true},
-      update: {validate: validateInstanceOf(Album)},
-    },
-
-    trackData: Thing.common.wikiData(Track),
-
-    // Expose only
-
-    tracks: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['tracksByRef', 'trackData'],
-        compute: ({tracksByRef, trackData}) =>
-          tracksByRef && trackData
-            ? tracksByRef.map((ref) => find.track(ref, trackData)).filter(Boolean)
-            : [],
-      },
-    },
-
-    startIndex: {
-      flags: {expose: true},
-
-      expose: {
-        dependencies: ['album'],
-        compute: ({album, [TrackGroup.instance]: trackGroup}) =>
-          album.trackGroups
-            .slice(0, album.trackGroups.indexOf(trackGroup))
-            .reduce((acc, tg) => acc + tg.tracks.length, 0),
-      },
-    },
   })
 }
diff --git a/src/data/things/track.js b/src/data/things/track.js
index a8020956..6b1e958b 100644
--- a/src/data/things/track.js
+++ b/src/data/things/track.js
@@ -151,9 +151,9 @@ export class Track extends Thing {
         dependencies: ['albumData'],
 
         compute: ({albumData, [Track.instance]: track}) =>
-          Track.findAlbum(track, albumData)?.trackGroups.find((tg) =>
-            tg.tracks.includes(track)
-          )?.color ?? null,
+          Track.findAlbum(track, albumData)
+            ?.trackSections.find(({tracks}) => tracks.includes(track))
+              ?.color ?? null,
       },
     },
 
diff --git a/src/data/things/validators.js b/src/data/things/validators.js
index a0d473ba..24db3c79 100644
--- a/src/data/things/validators.js
+++ b/src/data/things/validators.js
@@ -96,6 +96,10 @@ export function isStringNonEmpty(value) {
   return true;
 }
 
+export function optional(validator) {
+  return value => value === null || value === undefined || validator(value);
+}
+
 // Complex types (non-primitives)
 
 export function isInstance(value, constructor) {
@@ -252,6 +256,16 @@ export const isAdditionalFile = validateProperties({
 
 export const isAdditionalFileList = validateArrayItems(isAdditionalFile);
 
+export const isTrackSection = validateProperties({
+  name: optional(isString),
+  color: optional(isColor),
+  dateOriginallyReleased: optional(isDate),
+  isDefaultTrackSection: optional(isBoolean),
+  tracksByRef: optional(validateReferenceList('track')),
+});
+
+export const isTrackSectionList = validateArrayItems(isTrackSection);
+
 export function isDimensions(dimensions) {
   isArray(dimensions);
 
diff --git a/src/data/yaml.js b/src/data/yaml.js
index b00d68ee..f269d447 100644
--- a/src/data/yaml.js
+++ b/src/data/yaml.js
@@ -227,7 +227,7 @@ export const processAlbumDocument = makeProcessDocument(T.Album, {
   },
 });
 
-export const processTrackGroupDocument = makeProcessDocument(T.TrackGroup, {
+export const processTrackSectionDocument = makeProcessDocument(T.TrackSectionHelper, {
   fieldTransformations: {
     'Date Originally Released': (value) => new Date(value),
   },
@@ -652,7 +652,7 @@ export const dataSteps = [
     processHeaderDocument: processAlbumDocument,
     processEntryDocument(document) {
       return 'Group' in document
-        ? processTrackGroupDocument(document)
+        ? processTrackSectionDocument(document)
         : processTrackDocument(document);
     },
 
@@ -661,39 +661,39 @@ export const dataSteps = [
       const trackData = [];
 
       for (const {header: album, entries} of results) {
-        // We can't mutate an array once it's set as a property
-        // value, so prepare the tracks and track groups that will
-        // show up in a track list all the way before actually
-        // applying them.
-        const trackGroups = [];
-        let currentTracksByRef = null;
-        let currentTrackGroup = null;
+        // We can't mutate an array once it's set as a property value,
+        // so prepare the track sections that will show up in a track list
+        // all the way before actually applying them. (It's okay to mutate
+        // an individual section before applying it, since those are just
+        // generic objects; they aren't Things in and of themselves.)
+        const trackSections = [];
+
+        let currentTrackSection = {
+          name: `Default Track Section`,
+          isDefaultTrackSection: true,
+          tracksByRef: [],
+        };
 
         const albumRef = T.Thing.getReference(album);
 
-        const closeCurrentTrackGroup = () => {
-          if (currentTracksByRef) {
-            let trackGroup;
-
-            if (currentTrackGroup) {
-              trackGroup = currentTrackGroup;
-            } else {
-              trackGroup = new T.TrackGroup();
-              trackGroup.name = `Default Track Group`;
-              trackGroup.isDefaultTrackGroup = true;
-            }
-
-            trackGroup.album = album;
-            trackGroup.tracksByRef = currentTracksByRef;
-            trackGroups.push(trackGroup);
+        const closeCurrentTrackSection = () => {
+          if (!empty(currentTrackSection.tracksByRef)) {
+            trackSections.push(currentTrackSection);
           }
         };
 
         for (const entry of entries) {
-          if (entry instanceof T.TrackGroup) {
-            closeCurrentTrackGroup();
-            currentTracksByRef = [];
-            currentTrackGroup = entry;
+          if (entry instanceof T.TrackSectionHelper) {
+            closeCurrentTrackSection();
+
+            currentTrackSection = {
+              name: entry.name,
+              color: entry.color,
+              dateOriginallyReleased: entry.dateOriginallyReleased,
+              isDefaultTrackSection: false,
+              tracksByRef: [],
+            };
+
             continue;
           }
 
@@ -701,17 +701,12 @@ export const dataSteps = [
 
           entry.dataSourceAlbumByRef = albumRef;
 
-          const trackRef = T.Thing.getReference(entry);
-          if (currentTracksByRef) {
-            currentTracksByRef.push(trackRef);
-          } else {
-            currentTracksByRef = [trackRef];
-          }
+          currentTrackSection.tracksByRef.push(T.Thing.getReference(entry));
         }
 
-        closeCurrentTrackGroup();
+        closeCurrentTrackSection();
 
-        album.trackGroups = trackGroups;
+        album.trackSections = trackSections;
         albumData.push(album);
       }
 
@@ -1126,8 +1121,6 @@ export function linkWikiDataArrays(wikiData) {
   assignWikiData([WD.wikiInfo], 'groupData');
 
   assignWikiData(WD.albumData, 'artistData', 'artTagData', 'groupData', 'trackData');
-  WD.albumData.forEach((album) => assignWikiData(album.trackGroups, 'trackData'));
-
   assignWikiData(WD.trackData, 'albumData', 'artistData', 'artTagData', 'flashData', 'trackData');
   assignWikiData(WD.artistData, 'albumData', 'artistData', 'flashData', 'trackData');
   assignWikiData(WD.groupData, 'albumData', 'groupCategoryData');
diff --git a/src/page/album.js b/src/page/album.js
index 1fc74520..906c02ea 100644
--- a/src/page/album.js
+++ b/src/page/album.js
@@ -50,10 +50,10 @@ export function write(album, {wikiData}) {
   const hasAdditionalFiles = !empty(album.additionalFiles);
   const albumDuration = getTotalDuration(album.tracks);
 
-  const displayTrackGroups =
-    album.trackGroups &&
-      (album.trackGroups.length > 1 ||
-        !album.trackGroups[0].isDefaultTrackGroup);
+  const displayTrackSections =
+    album.trackSections &&
+      (album.trackSections.length > 1 ||
+        !album.trackSections[0].isDefaultTrackSection);
 
   const listTag = getAlbumListTag(album);
 
@@ -107,10 +107,10 @@ export function write(album, {wikiData}) {
       wallpaperArtistContribs: serializeContribs(album.wallpaperArtistContribs),
       bannerArtistContribs: serializeContribs(album.bannerArtistContribs),
       groups: serializeGroupsForAlbum(album),
-      trackGroups: album.trackGroups?.map((trackGroup) => ({
-        name: trackGroup.name,
-        color: trackGroup.color,
-        tracks: trackGroup.tracks.map((track) => track.directory),
+      trackSections: album.trackSections?.map((section) => ({
+        name: section.name,
+        color: section.color,
+        tracks: section.tracks.map((track) => track.directory),
       })),
       tracks: album.tracks.map((track) => ({
         link: serializeLink(track),
@@ -300,10 +300,10 @@ export function write(album, {wikiData}) {
                   ),
                 })),
 
-            displayTrackGroups &&
+            displayTrackSections &&
               html.tag('dl',
                 {class: 'album-group-list'},
-                album.trackGroups.flatMap(({
+                album.trackSections.flatMap(({
                   name,
                   startIndex,
                   tracks,
@@ -322,7 +322,7 @@ export function write(album, {wikiData}) {
                       tracks.map(trackToListItem))),
                 ])),
 
-            !displayTrackGroups &&
+            !displayTrackSections &&
               html.tag(listTag,
                 album.tracks.map(trackToListItem)),
 
@@ -515,7 +515,7 @@ export function generateAlbumSidebar(album, currentTrack, {
 
   const listTag = getAlbumListTag(album);
 
-  const {trackGroups} = album;
+  const {trackSections} = album;
 
   const trackToListItem = (track) =>
     html.tag('li',
@@ -524,19 +524,19 @@ export function generateAlbumSidebar(album, currentTrack, {
         track: link.track(track),
       }));
 
-  const nameOrDefault = (isDefaultTrackGroup, name) =>
-    isDefaultTrackGroup
-      ? language.$('albumSidebar.trackList.fallbackGroupName')
+  const nameOrDefault = (isDefaultTrackSection, name) =>
+    isDefaultTrackSection
+      ? language.$('albumSidebar.trackList.fallbackSectionName')
       : name;
 
   const trackListPart = [
     html.tag('h1', link.album(album)),
-    ...trackGroups.map(({name, color, startIndex, tracks, isDefaultTrackGroup}) => {
+    ...trackSections.map(({name, color, startIndex, tracks, isDefaultTrackSection}) => {
       const groupName =
         html.tag('span',
           {class: 'group-name'},
           nameOrDefault(
-            isDefaultTrackGroup,
+            isDefaultTrackSection,
             name
           ));
       return html.tag('details',
diff --git a/src/strings-default.json b/src/strings-default.json
index f9c1db8b..13ae61af 100644
--- a/src/strings-default.json
+++ b/src/strings-default.json
@@ -191,7 +191,7 @@
   "homepage.title": "{TITLE}",
   "homepage.news.title": "News",
   "homepage.news.entry.viewRest": "(View rest of entry!)",
-  "albumSidebar.trackList.fallbackGroupName": "Track list",
+  "albumSidebar.trackList.fallbackSectionName": "Track list",
   "albumSidebar.trackList.group": "{GROUP}",
   "albumSidebar.trackList.group.withRange": "{GROUP} ({RANGE})",
   "albumSidebar.trackList.item": "{TRACK}",
diff --git a/test/things.js b/test/things.js
index f36a4995..0d74b60d 100644
--- a/test/things.js
+++ b/test/things.js
@@ -9,9 +9,11 @@ import {
 
 function stubAlbum(tracks) {
   const album = new Album();
-  const trackGroup = new TrackGroup();
-  trackGroup.tracksByRef = tracks.map(t => Thing.getReference(t));
-  album.trackGroups = [trackGroup];
+  album.trackSections = [
+    {
+      tracksByRef: tracks.map(t => Thing.getReference(t)),
+    },
+  ];
   album.trackData = tracks;
   return album;
 }