« 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/generateListAllAdditionalFilesChunk.js77
-rw-r--r--src/content/dependencies/listAllAdditionalFiles.js9
-rw-r--r--src/content/dependencies/listAllAdditionalFilesTemplate.js206
-rw-r--r--src/content/dependencies/listAllMidiProjectFiles.js29
-rw-r--r--src/content/dependencies/listAllSheetMusicFiles.js29
-rw-r--r--src/listing-spec.js72
-rw-r--r--src/strings-default.json7
7 files changed, 316 insertions, 113 deletions
diff --git a/src/content/dependencies/generateListAllAdditionalFilesChunk.js b/src/content/dependencies/generateListAllAdditionalFilesChunk.js
new file mode 100644
index 00000000..29ef2c05
--- /dev/null
+++ b/src/content/dependencies/generateListAllAdditionalFilesChunk.js
@@ -0,0 +1,77 @@
+import {empty, stitchArrays} from '../../util/sugar.js';
+
+export default {
+  extraDependencies: ['html', 'language'],
+
+  slots: {
+    title: {type: 'html'},
+
+    additionalFileTitles: {
+      validate: v => v.strictArrayOf(v.isHTML),
+    },
+
+    additionalFileLinks: {
+      validate: v => v.strictArrayOf(v.strictArrayOf(v.isHTML)),
+    },
+
+    additionalFileFiles: {
+      validate: v => v.strictArrayOf(v.strictArrayOf(v.isString)),
+    },
+
+    stringsKey: {type: 'string'},
+  },
+
+  generate(slots, {html, language}) {
+    if (empty(slots.additionalFileLinks)) {
+      return html.blank();
+    }
+
+    return html.tags([
+      html.tag('dt', slots.title),
+      html.tag('dd',
+        html.tag('ul',
+          stitchArrays({
+            additionalFileTitle: slots.additionalFileTitles,
+            additionalFileLinks: slots.additionalFileLinks,
+            additionalFileFiles: slots.additionalFileFiles,
+          }).map(({
+              additionalFileTitle,
+              additionalFileLinks,
+              additionalFileFiles,
+            }) =>
+              (additionalFileLinks.length === 1
+                ? html.tag('li',
+                    additionalFileLinks[0].slots({
+                      content:
+                        language.$(`listingPage.${slots.stringsKey}.file`, {
+                          title: additionalFileTitle,
+                        }),
+                    }))
+
+                : html.tag('li', {class: 'has-details'},
+                    html.tag('details', [
+                      html.tag('summary',
+                        html.tag('span',
+                          language.$(`listingPage.${slots.stringsKey}.file.withMultipleFiles`, {
+                            title:
+                              html.tag('span', {class: 'group-name'}, additionalFileTitle),
+                            files:
+                              language.countAdditionalFiles(additionalFileLinks.length, {unit: true}),
+                          }))),
+
+                      html.tag('ul',
+                        stitchArrays({
+                          additionalFileLink: additionalFileLinks,
+                          additionalFileFile: additionalFileFiles,
+                        }).map(({additionalFileLink, additionalFileFile}) =>
+                            html.tag('li',
+                              additionalFileLink.slots({
+                                content:
+                                  language.$(`listingPage.${slots.stringsKey}.file`, {
+                                    title: additionalFileFile,
+                                  }),
+                              })))),
+                    ])))))),
+    ]);
+  },
+};
diff --git a/src/content/dependencies/listAllAdditionalFiles.js b/src/content/dependencies/listAllAdditionalFiles.js
new file mode 100644
index 00000000..a6e34b9a
--- /dev/null
+++ b/src/content/dependencies/listAllAdditionalFiles.js
@@ -0,0 +1,9 @@
+export default {
+  contentDependencies: ['listAllAdditionalFilesTemplate'],
+
+  relations: (relation, spec) =>
+    ({page: relation('listAllAdditionalFilesTemplate', spec, 'additionalFiles')}),
+
+  generate: (relations) =>
+    relations.page.slot('stringsKey', 'other.allAdditionalFiles'),
+};
diff --git a/src/content/dependencies/listAllAdditionalFilesTemplate.js b/src/content/dependencies/listAllAdditionalFilesTemplate.js
new file mode 100644
index 00000000..258442f1
--- /dev/null
+++ b/src/content/dependencies/listAllAdditionalFilesTemplate.js
@@ -0,0 +1,206 @@
+import {empty, stitchArrays} from '../../util/sugar.js';
+import {filterMultipleArrays, sortChronologically} from '../../util/wiki-data.js';
+
+export default {
+  contentDependencies: [
+    'generateListingPage',
+    'generateListAllAdditionalFilesChunk',
+    'linkAlbum',
+    'linkTrack',
+    'linkAlbumAdditionalFile',
+  ],
+
+  extraDependencies: ['html', 'language', 'wikiData'],
+
+  sprawl: ({albumData}) => ({albumData}),
+
+  query(sprawl, spec, property) {
+    const albums =
+      sortChronologically(sprawl.albumData.slice());
+
+    const tracks =
+      albums
+        .map(album => album.tracks.slice());
+
+    // Get additional file objects from albums and their tracks.
+    // There's a possibility that albums and tracks don't both implement
+    // the same additional file fields - in this case, just treat them
+    // as though they do implement those fields, but don't have any
+    // additional files of that type.
+
+    const albumAdditionalFileObjects =
+      albums
+        .map(album => album[property] ?? []);
+
+    const trackAdditionalFileObjects =
+      tracks
+        .map(byAlbum => byAlbum
+          .map(track => track[property] ?? []));
+
+    // Filter out tracks that don't have any additional files.
+
+    stitchArrays({tracks, trackAdditionalFileObjects})
+      .forEach(({tracks, trackAdditionalFileObjects}) => {
+        filterMultipleArrays(tracks, trackAdditionalFileObjects,
+          (track, trackAdditionalFileObjects) => !empty(trackAdditionalFileObjects));
+      });
+
+    // Filter out albums that don't have any tracks,
+    // nor any additional files of their own.
+
+    filterMultipleArrays(albums, albumAdditionalFileObjects, tracks, trackAdditionalFileObjects,
+      (album, albumAdditionalFileObjects, tracks, trackAdditionalFileObjects) =>
+        !empty(albumAdditionalFileObjects) ||
+        !empty(trackAdditionalFileObjects));
+
+    // Map additional file objects into titles and lists of file names.
+
+    const albumAdditionalFileTitles =
+      albumAdditionalFileObjects
+        .map(byAlbum => byAlbum
+          .map(({title}) => title));
+
+    const albumAdditionalFileFiles =
+      albumAdditionalFileObjects
+        .map(byAlbum => byAlbum
+          .map(({files}) => files));
+
+    const trackAdditionalFileTitles =
+      trackAdditionalFileObjects
+        .map(byAlbum => byAlbum
+          .map(byTrack => byTrack
+            .map(({title}) => title)));
+
+    const trackAdditionalFileFiles =
+      trackAdditionalFileObjects
+        .map(byAlbum => byAlbum
+          .map(byTrack => byTrack
+            .map(({files}) => files)));
+
+    return {
+      spec,
+      albums,
+      tracks,
+      albumAdditionalFileTitles,
+      albumAdditionalFileFiles,
+      trackAdditionalFileTitles,
+      trackAdditionalFileFiles,
+    };
+  },
+
+  relations: (relation, query) => ({
+    page:
+      relation('generateListingPage', query.spec),
+
+    albumLinks:
+      query.albums
+        .map(album => relation('linkAlbum', album)),
+
+    trackLinks:
+      query.tracks
+        .map(byAlbum => byAlbum
+          .map(track => relation('linkTrack', track))),
+
+    albumChunks:
+      query.albums
+        .map(() => relation('generateListAllAdditionalFilesChunk')),
+
+    trackChunks:
+      query.tracks
+        .map(byAlbum => byAlbum
+          .map(() => relation('generateListAllAdditionalFilesChunk'))),
+
+    albumAdditionalFileLinks:
+      stitchArrays({
+        album: query.albums,
+        files: query.albumAdditionalFileFiles,
+      }).map(({album, files: byAlbum}) =>
+          byAlbum.map(files => files
+            .map(file =>
+              relation('linkAlbumAdditionalFile', album, file)))),
+
+    trackAdditionalFileLinks:
+      stitchArrays({
+        album: query.albums,
+        files: query.trackAdditionalFileFiles,
+      }).map(({album, files: byAlbum}) =>
+          byAlbum
+            .map(byTrack => byTrack
+              .map(files => files
+                .map(file => relation('linkAlbumAdditionalFile', album, file))))),
+  }),
+
+  data: (query) => ({
+    albumAdditionalFileTitles: query.albumAdditionalFileTitles,
+    trackAdditionalFileTitles: query.trackAdditionalFileTitles,
+    albumAdditionalFileFiles: query.albumAdditionalFileFiles,
+    trackAdditionalFileFiles: query.trackAdditionalFileFiles,
+  }),
+
+  slots: {
+    stringsKey: {type: 'string'},
+  },
+
+  generate: (data, relations, slots, {html, language}) =>
+    relations.page.slots({
+      type: 'custom',
+
+      content:
+        stitchArrays({
+          albumLink: relations.albumLinks,
+          trackLinks: relations.trackLinks,
+          albumChunk: relations.albumChunks,
+          trackChunks: relations.trackChunks,
+          albumAdditionalFileTitles: data.albumAdditionalFileTitles,
+          trackAdditionalFileTitles: data.trackAdditionalFileTitles,
+          albumAdditionalFileLinks: relations.albumAdditionalFileLinks,
+          trackAdditionalFileLinks: relations.trackAdditionalFileLinks,
+          albumAdditionalFileFiles: data.albumAdditionalFileFiles,
+          trackAdditionalFileFiles: data.trackAdditionalFileFiles,
+        }).map(({
+            albumLink,
+            trackLinks,
+            albumChunk,
+            trackChunks,
+            albumAdditionalFileTitles,
+            trackAdditionalFileTitles,
+            albumAdditionalFileLinks,
+            trackAdditionalFileLinks,
+            albumAdditionalFileFiles,
+            trackAdditionalFileFiles,
+          }) => [
+            html.tag('h3', {class: 'content-heading'}, albumLink),
+
+            html.tag('dl', [
+              albumChunk.slots({
+                title: language.$(`listingPage.${slots.stringsKey}.albumFiles`),
+                additionalFileTitles: albumAdditionalFileTitles,
+                additionalFileLinks: albumAdditionalFileLinks,
+                additionalFileFiles: albumAdditionalFileFiles,
+                stringsKey: slots.stringsKey,
+              }),
+
+              stitchArrays({
+                trackLink: trackLinks,
+                trackChunk: trackChunks,
+                trackAdditionalFileTitles,
+                trackAdditionalFileLinks,
+                trackAdditionalFileFiles,
+              }).map(({
+                  trackLink,
+                  trackChunk,
+                  trackAdditionalFileTitles,
+                  trackAdditionalFileLinks,
+                  trackAdditionalFileFiles,
+                }) =>
+                  trackChunk.slots({
+                    title: trackLink,
+                    additionalFileTitles: trackAdditionalFileTitles,
+                    additionalFileLinks: trackAdditionalFileLinks,
+                    additionalFileFiles: trackAdditionalFileFiles,
+                    stringsKey: slots.stringsKey,
+                  })),
+            ]),
+          ]),
+    }),
+};
diff --git a/src/content/dependencies/listAllMidiProjectFiles.js b/src/content/dependencies/listAllMidiProjectFiles.js
index 28a925ac..31a70ef0 100644
--- a/src/content/dependencies/listAllMidiProjectFiles.js
+++ b/src/content/dependencies/listAllMidiProjectFiles.js
@@ -1,28 +1,9 @@
 export default {
-  contentDependencies: ['generateListingPage'],
-  extraDependencies: ['html', 'wikiData'],
+  contentDependencies: ['listAllAdditionalFilesTemplate'],
 
-  sprawl() {
-    return {};
-  },
+  relations: (relation, spec) =>
+    ({page: relation('listAllAdditionalFilesTemplate', spec, 'midiProjectFiles')}),
 
-  query(sprawl, spec) {
-    return {
-      spec,
-    };
-  },
-
-  relations(relation, query) {
-    return {
-      page: relation('generateListingPage', query.spec),
-    };
-  },
-
-  generate(relations, {html}) {
-    return relations.page.slots({
-      type: 'custom',
-      content:
-        html.tag('p', `Alright alright, this is a stub page! Coming soon!`),
-    });
-  },
+  generate: (relations) =>
+    relations.page.slot('stringsKey', 'other.allMidiProjectFiles'),
 };
diff --git a/src/content/dependencies/listAllSheetMusicFiles.js b/src/content/dependencies/listAllSheetMusicFiles.js
index 28a925ac..166b2068 100644
--- a/src/content/dependencies/listAllSheetMusicFiles.js
+++ b/src/content/dependencies/listAllSheetMusicFiles.js
@@ -1,28 +1,9 @@
 export default {
-  contentDependencies: ['generateListingPage'],
-  extraDependencies: ['html', 'wikiData'],
+  contentDependencies: ['listAllAdditionalFilesTemplate'],
 
-  sprawl() {
-    return {};
-  },
+  relations: (relation, spec) =>
+    ({page: relation('listAllAdditionalFilesTemplate', spec, 'sheetMusicFiles')}),
 
-  query(sprawl, spec) {
-    return {
-      spec,
-    };
-  },
-
-  relations(relation, query) {
-    return {
-      page: relation('generateListingPage', query.spec),
-    };
-  },
-
-  generate(relations, {html}) {
-    return relations.page.slots({
-      type: 'custom',
-      content:
-        html.tag('p', `Alright alright, this is a stub page! Coming soon!`),
-    });
-  },
+  generate: (relations) =>
+    relations.page.slot('stringsKey', 'other.allSheetMusic'),
 };
diff --git a/src/listing-spec.js b/src/listing-spec.js
index 82503174..e19b90e8 100644
--- a/src/listing-spec.js
+++ b/src/listing-spec.js
@@ -212,71 +212,7 @@ listingSpec.push({
   featureFlag: 'enableArtTagUI',
 });
 
-/*
-function listAdditionalFilesInProperty(property, {
-  directory,
-  stringsKey,
-  seeAlso,
-}) {
-  return {
-    directory,
-    stringsKey,
-    seeAlso,
-    groupUnderOther: true,
-
-    data: ({wikiData: {albumData}}) =>
-      albumData
-        .map(album => ({
-          album,
-          tracks: album.tracks.filter(t => !empty(t[property])),
-        }))
-        .filter(({tracks}) => !empty(tracks)),
-
-    html: (data, {
-      html,
-      language,
-      link,
-    }) =>
-      data.flatMap(({album, tracks}) => [
-        html.tag('h3', {class: 'content-heading'},
-          link.album(album)),
-
-        html.tag('dl', tracks.flatMap(track => [
-          // No hash here since the full list of additional files is already visible
-          // below. The track link serves more as a way to quickly recall the track or
-          // to access listen links, all of which is positioned at the top of the page.
-          html.tag('dt', link.track(track)),
-          html.tag('dd',
-            // This page doesn't really look better with color-coded file links.
-            // Track links are still colored.
-            html.tag('ul', track[property].map(({title, files}) =>
-              html.tag('li',
-                {class: [files.length > 1 && 'has-details']},
-                (files.length === 1
-                  ? link.albumAdditionalFile(
-                      {album, file: files[0]},
-                      {
-                        text: language.$(`listingPage.${stringsKey}.file`, {title}),
-                      })
-                  : html.tag('details', [
-                      html.tag('summary',
-                        html.tag('span',
-                          language.$(`listingPage.${stringsKey}.file.withMultipleFiles`, {
-                            title: html.tag('span', {class: 'group-name'}, title),
-                            files: language.countAdditionalFiles(files.length, {unit: true}),
-                          }))),
-                      html.tag('ul', files.map(file =>
-                        html.tag('li',
-                          link.albumAdditionalFile({album, file})))),
-                    ])))))),
-        ])),
-      ]),
-  };
-}
-*/
-
 listingSpec.push({
-  /* listAdditionalFilesInProperty('sheetMusicFiles') */
   directory: 'all-sheet-music-files',
   stringsKey: 'other.allSheetMusic',
   contentFunction: 'listAllSheetMusicFiles',
@@ -285,7 +221,6 @@ listingSpec.push({
 });
 
 listingSpec.push({
-  /* listAdditionalFilesInProperty('midiProjectFiles') */
   directory: 'all-midi-project-files',
   stringsKey: 'other.allMidiProjectFiles',
   contentFunction: 'listAllMidiProjectFiles',
@@ -294,6 +229,13 @@ listingSpec.push({
 });
 
 listingSpec.push({
+  directory: 'all-additional-files',
+  stringsKey: 'other.allAdditionalFiles',
+  contentFunction: 'listAllAdditionalFiles',
+  groupUnderOther: true,
+});
+
+listingSpec.push({
   directory: 'random',
   stringsKey: 'other.randomPages',
   contentFunction: 'listRandomPageLinks',
diff --git a/src/strings-default.json b/src/strings-default.json
index 7987a9f6..82a046f5 100644
--- a/src/strings-default.json
+++ b/src/strings-default.json
@@ -454,12 +454,19 @@
   "listingPage.listTags.byUses.item": "{TAG} ({TIMES_USED})",
   "listingPage.other.allSheetMusic.title": "All Sheet Music",
   "listingPage.other.allSheetMusic.title.short": "All Sheet Music",
+  "listingPage.other.allSheetMusic.albumFiles": "Album sheet music:",
   "listingPage.other.allSheetMusic.file": "{TITLE}",
   "listingPage.other.allSheetMusic.file.withMultipleFiles": "{TITLE} ({FILES})",
   "listingPage.other.allMidiProjectFiles.title": "All MIDI/Project Files",
   "listingPage.other.allMidiProjectFiles.title.short": "All MIDI/Project Files",
+  "listingPage.other.allMidiProjectFiles.albumFiles": "Album MIDI/project files:",
   "listingPage.other.allMidiProjectFiles.file": "{TITLE}",
   "listingPage.other.allMidiProjectFiles.file.withMultipleFiles": "{TITLE} ({FILES})",
+  "listingPage.other.allAdditionalFiles.title": "All Additional Files",
+  "listingPage.other.allAdditionalFiles.title.short": "All Additional Files",
+  "listingPage.other.allAdditionalFiles.albumFiles": "Album additional files:",
+  "listingPage.other.allAdditionalFiles.file": "{TITLE}",
+  "listingPage.other.allAdditionalFiles.file.withMultipleFiles": "{TITLE} ({FILES})",
   "listingPage.other.randomPages.title": "Random Pages",
   "listingPage.other.randomPages.title.short": "Random Pages",
   "listingPage.misc.trackContributors": "Track Contributors",