« get me outta code hell

data steps: album additional files list - 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-03-21 23:28:38 -0300
committer(quasar) nebula <qznebula@protonmail.com>2023-03-21 23:34:23 -0300
commitec0dd58271eabd0dd9fa12fbf51f5b46b8ceb014 (patch)
treea10b342ae97f0554eca9179734f27ceecf6e2f90
parent7783afa2eeba6eb3b876d325cd83c41fb96b4792 (diff)
data steps: album additional files list
This is WIP but seems to be working! Pretty big test of
the new html.template system, which needed some extension
here.
-rw-r--r--src/content-function.js57
-rw-r--r--src/content/dependencies/generateAdditionalFilesList.js46
-rw-r--r--src/content/dependencies/generateAlbumAdditionalFilesList.js55
-rw-r--r--src/content/dependencies/generateAlbumInfoPage.js8
-rw-r--r--src/content/dependencies/generateAlbumInfoPageContent.js221
-rw-r--r--src/content/dependencies/generateAlbumSocialEmbed.js2
-rw-r--r--src/content/dependencies/generateContentHeading.js16
-rw-r--r--src/content/dependencies/linkAlbumAdditionalFile.js12
-rw-r--r--src/misc-templates.js51
-rw-r--r--src/page/album.js23
-rw-r--r--src/util/html.js20
11 files changed, 301 insertions, 210 deletions
diff --git a/src/content-function.js b/src/content-function.js
index 93bef6b..5cd5392 100644
--- a/src/content-function.js
+++ b/src/content-function.js
@@ -34,9 +34,8 @@ export function expectDependencies({
     throw new Error(`Expected generate function`);
   }
 
-  if (!data) {
-    throw new Error(`Expected data function`);
-  }
+  const hasDataFunction = !!data;
+  const hasRelationsFunction = !!relations;
 
   const fulfilledDependencyKeys = Object.keys(fulfilledDependencies);
 
@@ -63,10 +62,22 @@ export function expectDependencies({
 
   if (empty(missingContentDependencyKeys) && empty(missingExtraDependencyKeys)) {
     wrappedGenerate ??= function(data, relations) {
-      if (relations) {
+      if (hasDataFunction && !data) {
+        throw new Error(`Expected data`);
+      }
+
+      if (hasRelationsFunction && !relations) {
+        throw new Error(`Expected relations`);
+      }
+
+      if (hasDataFunction && hasRelationsFunction) {
         return generate(data, relations, fulfilledDependencies);
-      } else {
+      } else if (hasDataFunction) {
         return generate(data, fulfilledDependencies);
+      } else if (hasRelationsFunction) {
+        return generate(relations, fulfilledDependencies);
+      } else {
+        return generate(fulfilledDependencies);
       }
     };
 
@@ -89,40 +100,22 @@ export function expectDependencies({
 
   wrappedGenerate[contentFunction.identifyingSymbol] = true;
 
-  if (empty(missingContentDependencyKeys)) {
-    /*
-    const dataDependencies = {};
-
-    for (const key of expectedContentDependencyKeys) {
-      const wrappedDependency = function() {
-        throw new Error(`Expected call to this dependency's .data()`);
+  if (hasDataFunction) {
+    if (empty(missingContentDependencyKeys)) {
+      wrappedGenerate.data = data;
+    } else {
+      wrappedGenerate.data = function() {
+        throw new Error(`Dependencies still needed: ${missingContentDependencyKeys.join(', ')}`);
       };
 
-      annotateFunction(wrappedGenerate, {name: fulfilledDependencies[key], description: 'data only'});
-      wrappedDependency.data = fulfilledDependencies[key].data;
-      dataDependencies[key] = wrappedDependency;
+      annotateFunction(wrappedGenerate.data, {name: data, trait: 'unfulfilled'});
     }
-
-    wrappedGenerate.data = function(...args) {
-      return data(...args, dataDependencies);
-    };
-
-    annotateFunction(wrappedGenerate.data, {name: data, trait: 'fulfilled'});
-    */
-
-    wrappedGenerate.data = data;
   }
 
-  if (!wrappedGenerate.data) {
-    wrappedGenerate.data = function() {
-      throw new Error(`Dependencies still needed: ${missingContentDependencyKeys.join(', ')}`);
-    };
-
-    annotateFunction(wrappedGenerate.data, {name: data, trait: 'unfulfilled'});
+  if (hasRelationsFunction) {
+    wrappedGenerate.relations = relations;
   }
 
-  wrappedGenerate.relations = relations;
-
   wrappedGenerate.fulfill ??= function fulfill(dependencies) {
     return expectDependencies({
       data,
diff --git a/src/content/dependencies/generateAdditionalFilesList.js b/src/content/dependencies/generateAdditionalFilesList.js
new file mode 100644
index 0000000..7f257e4
--- /dev/null
+++ b/src/content/dependencies/generateAdditionalFilesList.js
@@ -0,0 +1,46 @@
+export default {
+  extraDependencies: [
+    'html',
+    'language',
+  ],
+
+  data(additionalFiles, {fileSize = true} = {}) {
+    return {
+      // Additional files are already a serializable format.
+      additionalFiles,
+      showFileSizes: fileSize,
+    };
+  },
+
+  generate(data, {
+    html,
+    language,
+  }) {
+    return html.template(slot =>
+      html.tag('dl',
+        data.additionalFiles.flatMap(({title, description, files}) => [
+          html.tag('dt',
+            (description
+              ? language.$('releaseInfo.additionalFiles.entry.withDescription', {
+                  title,
+                  description,
+                })
+              : language.$('releaseInfo.additionalFiles.entry', {title}))),
+
+          slot('additionalFileLinks', ([fileLinks]) =>
+          slot('additionalFileSizes', ([fileSizes]) =>
+            html.tag('dd',
+              html.tag('ul',
+                files.map(file =>
+                  html.tag('li',
+                    (fileSizes[file]
+                      ? language.$('releaseInfo.additionalFiles.file.withSize', {
+                          file: fileLinks[file],
+                          size: language.formatFileSize(fileSizes[file]),
+                        })
+                      : language.$('releaseInfo.additionalFiles.file', {
+                          file: fileLinks[file],
+                        })))))))),
+        ])));
+  },
+};
diff --git a/src/content/dependencies/generateAlbumAdditionalFilesList.js b/src/content/dependencies/generateAlbumAdditionalFilesList.js
new file mode 100644
index 0000000..d45fb58
--- /dev/null
+++ b/src/content/dependencies/generateAlbumAdditionalFilesList.js
@@ -0,0 +1,55 @@
+export default {
+  contentDependencies: [
+    'generateAdditionalFilesList',
+    'linkAlbumAdditionalFile',
+  ],
+
+  extraDependencies: [
+    'getSizeOfAdditionalFile',
+    'urls',
+  ],
+
+  data(album, {fileSize = true} = {}) {
+    return {
+      albumDirectory: album.directory,
+      fileLocations: album.additionalFiles.flatMap(({files}) => files),
+      showFileSizes: fileSize,
+    };
+  },
+
+  relations(relation, album, {fileSize = true} = {}) {
+    return {
+      additionalFilesList:
+        relation('generateAdditionalFilesList', album.additionalFiles, {
+          fileSize,
+        }),
+
+      additionalFileLinks:
+        Object.fromEntries(
+          album.additionalFiles
+            .flatMap(({files}) => files)
+            .map(file => [
+              file,
+              relation('linkAlbumAdditionalFile', album, file),
+            ])),
+    };
+  },
+
+  generate(data, relations, {
+    getSizeOfAdditionalFile,
+    urls,
+  }) {
+    return relations.additionalFilesList
+      .slot('additionalFileLinks', relations.additionalFileLinks)
+      .slot('additionalFileSizes',
+        Object.fromEntries(data.fileLocations.map(file => [
+          file,
+          (data.showFileSizes
+            ? getSizeOfAdditionalFile(
+                urls
+                  .from('media.root')
+                  .to('media.albumAdditionalFile', data.albumDirectory, file))
+            : 0),
+        ])));
+  },
+};
diff --git a/src/content/dependencies/generateAlbumInfoPage.js b/src/content/dependencies/generateAlbumInfoPage.js
index 8bbb320..bc4ac1d 100644
--- a/src/content/dependencies/generateAlbumInfoPage.js
+++ b/src/content/dependencies/generateAlbumInfoPage.js
@@ -20,13 +20,7 @@ export default {
     return relations;
   },
 
-  data(album) {
-    const data = {};
-
-    return data;
-  },
-
-  generate(data, relations, {
+  generate(relations, {
     language,
   }) {
     const page = {};
diff --git a/src/content/dependencies/generateAlbumInfoPageContent.js b/src/content/dependencies/generateAlbumInfoPageContent.js
index a9e51c0..013ab3f 100644
--- a/src/content/dependencies/generateAlbumInfoPageContent.js
+++ b/src/content/dependencies/generateAlbumInfoPageContent.js
@@ -2,12 +2,15 @@ import {accumulateSum, empty} from '../../util/sugar.js';
 
 export default {
   contentDependencies: [
+    'generateAlbumAdditionalFilesList',
     'generateContributionLinks',
+    'generateContentHeading',
   ],
 
   extraDependencies: [
     'html',
     'language',
+    'transformMultiline',
   ],
 
   relations(relation, album) {
@@ -31,6 +34,20 @@ export default {
     relations.bannerArtistLinks =
       contributionLinksRelation(album.bannerArtistContribs);
 
+    const contentHeadingRelation = () =>
+      relation('generateContentHeading');
+
+    if (!empty(album.additionalFiles)) {
+      relations.additionalFilesHeading =
+        contentHeadingRelation();
+
+      relations.additionalFilesList =
+        relation('generateAlbumAdditionalFilesList', album);
+    }
+
+    relations.artistCommentaryHeading =
+      contentHeadingRelation();
+
     return relations;
   },
 
@@ -49,12 +66,19 @@ export default {
       data.coverArtDate = album.coverArtDate;
     }
 
+    if (!empty(album.additionalFiles)) {
+      data.numAdditionalFiles = album.additionalFiles.length;
+    }
+
+    data.artistCommentary = album.commentary;
+
     return data;
   },
 
   generate(data, relations, {
     html,
     language,
+    transformMultiline,
   }) {
     const content = {};
 
@@ -107,109 +131,120 @@ export default {
           ]),
 
         /*
-        html.tag('p',
-          {
-            [html.onlyIfContent]: true,
-            [html.joinChildren]: '<br>',
-          },
-          [
-            hasAdditionalFiles &&
-              generateAdditionalFilesShortcut(album.additionalFiles),
-
-            checkGalleryPage(album) &&
-              language.$('releaseInfo.viewGallery', {
-                link: link.albumGallery(album, {
-                  text: language.$('releaseInfo.viewGallery.link'),
-                }),
-              }),
-
-            checkCommentaryPage(album) &&
-              language.$('releaseInfo.viewCommentary', {
-                link: link.albumCommentary(album, {
-                  text: language.$('releaseInfo.viewCommentary.link'),
+          html.tag('p',
+            {
+              [html.onlyIfContent]: true,
+              [html.joinChildren]: '<br>',
+            },
+            [
+              hasAdditionalFiles &&
+                generateAdditionalFilesShortcut(album.additionalFiles),
+
+              checkGalleryPage(album) &&
+                language.$('releaseInfo.viewGallery', {
+                  link: link.albumGallery(album, {
+                    text: language.$('releaseInfo.viewGallery.link'),
+                  }),
                 }),
-              }),
-          ]),
 
-        !empty(album.urls) &&
-          html.tag('p',
-            language.$('releaseInfo.listenOn', {
-              links: language.formatDisjunctionList(
-                album.urls.map(url => fancifyURL(url, {album: true}))
-              ),
-            })),
-
-        displayTrackSections &&
-        !empty(album.trackSections) &&
-          html.tag('dl',
-            {class: 'album-group-list'},
-            album.trackSections.flatMap(({
-              name,
-              startIndex,
-              tracks,
-            }) => [
-              html.tag('dt',
-                {class: ['content-heading']},
-                language.$('trackList.section.withDuration', {
-                  duration: language.formatDuration(getTotalDuration(tracks), {
-                    approximate: tracks.length > 1,
+              checkCommentaryPage(album) &&
+                language.$('releaseInfo.viewCommentary', {
+                  link: link.albumCommentary(album, {
+                    text: language.$('releaseInfo.viewCommentary.link'),
                   }),
-                  section: name,
-                })),
-              html.tag('dd',
-                html.tag(listTag,
-                  listTag === 'ol' ? {start: startIndex + 1} : {},
-                  tracks.map(trackToListItem))),
-            ])),
-
-        !displayTrackSections &&
-        !empty(album.tracks) &&
-          html.tag(listTag,
-            album.tracks.map(trackToListItem)),
+                }),
+            ]),
 
-        html.tag('p',
-          {
-            [html.onlyIfContent]: true,
-            [html.joinChildren]: '<br>',
-          },
-          [
-            album.dateAddedToWiki &&
-              language.$('releaseInfo.addedToWiki', {
-                date: language.formatDate(
-                  album.dateAddedToWiki
+          !empty(album.urls) &&
+            html.tag('p',
+              language.$('releaseInfo.listenOn', {
+                links: language.formatDisjunctionList(
+                  album.urls.map(url => fancifyURL(url, {album: true}))
                 ),
-              })
-          ]),
+              })),
+
+          displayTrackSections &&
+          !empty(album.trackSections) &&
+            html.tag('dl',
+              {class: 'album-group-list'},
+              album.trackSections.flatMap(({
+                name,
+                startIndex,
+                tracks,
+              }) => [
+                html.tag('dt',
+                  {class: ['content-heading']},
+                  language.$('trackList.section.withDuration', {
+                    duration: language.formatDuration(getTotalDuration(tracks), {
+                      approximate: tracks.length > 1,
+                    }),
+                    section: name,
+                  })),
+                html.tag('dd',
+                  html.tag(listTag,
+                    listTag === 'ol' ? {start: startIndex + 1} : {},
+                    tracks.map(trackToListItem))),
+              ])),
+
+          !displayTrackSections &&
+          !empty(album.tracks) &&
+            html.tag(listTag,
+              album.tracks.map(trackToListItem)),
 
-        ...html.fragment(
-          hasAdditionalFiles && [
-            generateContentHeading({
-              id: 'additional-files',
-              title: language.$('releaseInfo.additionalFiles.heading', {
-                additionalFiles: language.countAdditionalFiles(numAdditionalFiles, {
-                  unit: true,
+          html.tag('p',
+            {
+              [html.onlyIfContent]: true,
+              [html.joinChildren]: '<br>',
+            },
+            [
+              album.dateAddedToWiki &&
+                language.$('releaseInfo.addedToWiki', {
+                  date: language.formatDate(
+                    album.dateAddedToWiki
+                  ),
+                })
+            ]),
+
+          ...html.fragment(
+            hasAdditionalFiles && [
+              generateContentHeading({
+                id: 'additional-files',
+                title: language.$('releaseInfo.additionalFiles.heading', {
+                  additionalFiles: language.countAdditionalFiles(numAdditionalFiles, {
+                    unit: true,
+                  }),
                 }),
               }),
-            }),
-
-            generateAlbumAdditionalFilesList(album, album.additionalFiles, {
-              generateAdditionalFilesList,
-              getSizeOfAdditionalFile,
-              link,
-              urls,
-            }),
-          ]),
-
-        ...html.fragment(
-          album.commentary && [
-            generateContentHeading({
-              id: 'artist-commentary',
-              title: language.$('releaseInfo.artistCommentary'),
-            }),
 
-            html.tag('blockquote', transformMultiline(album.commentary)),
-          ])
+              generateAlbumAdditionalFilesList(album, album.additionalFiles, {
+                generateAdditionalFilesList,
+                getSizeOfAdditionalFile,
+                link,
+                urls,
+              }),
+            ]),
         */
+
+        relations.additionalFilesList && [
+          relations.additionalFilesHeading
+            .slot('id', 'additional-files')
+            .slot('title',
+              language.$('releaseInfo.additionalFiles.heading', {
+                additionalFiles:
+                  language.countAdditionalFiles(data.numAdditionalFiles, {unit: true}),
+              })),
+
+          relations.additionalFilesList,
+        ],
+
+        data.artistCommentary && [
+          relations.artistCommentaryHeading
+            .slot('id', 'artist-commentary')
+            .slot('title', language.$('releaseDate.artistCommentary')),
+
+          html.tag('blockquote',
+            transformMultiline(data.artistCommentary)),
+        ],
       ]
     };
 
diff --git a/src/content/dependencies/generateAlbumSocialEmbed.js b/src/content/dependencies/generateAlbumSocialEmbed.js
index 87d8eed..8786a33 100644
--- a/src/content/dependencies/generateAlbumSocialEmbed.js
+++ b/src/content/dependencies/generateAlbumSocialEmbed.js
@@ -1,4 +1,4 @@
-import {accumulateSum, empty} from '../../util/sugar.js';
+import {empty} from '../../util/sugar.js';
 
 export default {
   contentDependencies: [
diff --git a/src/content/dependencies/generateContentHeading.js b/src/content/dependencies/generateContentHeading.js
new file mode 100644
index 0000000..baa5208
--- /dev/null
+++ b/src/content/dependencies/generateContentHeading.js
@@ -0,0 +1,16 @@
+export default {
+  extraDependencies: [
+    'html',
+  ],
+
+  generate({html}) {
+    return html.template(slot =>
+      html.tag('p',
+        {
+          class: 'content-heading',
+          id: slot('id'),
+          tabindex: '0',
+        },
+        slot('title')));
+  }
+}
diff --git a/src/content/dependencies/linkAlbumAdditionalFile.js b/src/content/dependencies/linkAlbumAdditionalFile.js
new file mode 100644
index 0000000..6c47edc
--- /dev/null
+++ b/src/content/dependencies/linkAlbumAdditionalFile.js
@@ -0,0 +1,12 @@
+export default {
+  data(album, file) {
+    return {
+      albumDirectory: album.directory,
+      file,
+    };
+  },
+
+  generate(data) {
+    return `(stub album additional file link: ${data.albumDirectory}/${data.file})`;
+  },
+};
diff --git a/src/misc-templates.js b/src/misc-templates.js
index afcb9c3..11a95c7 100644
--- a/src/misc-templates.js
+++ b/src/misc-templates.js
@@ -40,42 +40,6 @@ function unbound_generateAdditionalFilesShortcut(additionalFiles, {
   });
 }
 
-function unbound_generateAdditionalFilesList(additionalFiles, {
-  html,
-  language,
-
-  getFileSize,
-  linkFile,
-}) {
-  if (empty(additionalFiles)) return [];
-
-  return html.tag('dl',
-    additionalFiles.flatMap(({title, description, files}) => [
-      html.tag('dt',
-        (description
-          ? language.$('releaseInfo.additionalFiles.entry.withDescription', {
-              title,
-              description,
-            })
-          : language.$('releaseInfo.additionalFiles.entry', {title}))),
-
-      html.tag('dd',
-        html.tag('ul',
-          files.map((file) => {
-            const size = (getFileSize && getFileSize(file));
-            return html.tag('li',
-              (size
-                ? language.$('releaseInfo.additionalFiles.file.withSize', {
-                    file: linkFile(file),
-                    size: language.formatFileSize(size),
-                  })
-                : language.$('releaseInfo.additionalFiles.file', {
-                    file: linkFile(file),
-                  })))
-          }))),
-    ]));
-}
-
 // Chronology links
 
 function unbound_generateChronologyLinks(currentThing, {
@@ -795,21 +759,6 @@ function unbound_generateNavigationLinks(current, {
 
 // Sticky heading, ooooo
 
-function unbound_generateContentHeading({
-  html,
-
-  id,
-  title,
-}) {
-  return html.tag('p',
-    {
-      class: 'content-heading',
-      id,
-      tabindex: '0',
-    },
-    title);
-}
-
 function unbound_generateStickyHeadingContainer({
   html,
   img,
diff --git a/src/page/album.js b/src/page/album.js
index eeb6dc7..4ed4dfc 100644
--- a/src/page/album.js
+++ b/src/page/album.js
@@ -695,27 +695,4 @@ export function generateAlbumChronologyLinks(album, currentTrack, {
         })),
     ]);
 }
-
-export function generateAlbumAdditionalFilesList(album, additionalFiles, {
-  fileSize = true,
-
-  generateAdditionalFilesList,
-  getSizeOfAdditionalFile,
-  link,
-  urls,
-}) {
-  return generateAdditionalFilesList(additionalFiles, {
-    getFileSize:
-      (fileSize
-        ? (file) =>
-            // TODO: Kinda near the metal here...
-            getSizeOfAdditionalFile(
-              urls
-                .from('media.root')
-                .to('media.albumAdditionalFile', album.directory, file))
-        : () => null),
-    linkFile: (file) =>
-      link.albumAdditionalFile({album, file}),
-  });
-}
 */
diff --git a/src/util/html.js b/src/util/html.js
index 63d7c1c..e808eef 100644
--- a/src/util/html.js
+++ b/src/util/html.js
@@ -411,8 +411,9 @@ export class Template {
 
 export class Slot {
   #defaultTag = new Tag();
+  #handleContent = null;
 
-  constructor(template, slotName, defaultContent) {
+  constructor(template, slotName, defaultContentOrHandleContent) {
     if (!template) {
       throw new Error(`Expected template`);
     }
@@ -423,7 +424,12 @@ export class Slot {
 
     this.template = template;
     this.slotName = slotName;
-    this.defaultContent = defaultContent;
+
+    if (typeof defaultContentOrHandleContent === 'function') {
+      this.#handleContent = defaultContentOrHandleContent;
+    } else {
+      this.defaultContent = defaultContentOrHandleContent;
+    }
   }
 
   set defaultContent(value) {
@@ -447,6 +453,14 @@ export class Slot {
   }
 
   toString() {
-    return this.content.toString();
+    return this.valueOf().toString();
+  }
+
+  valueOf() {
+    if (this.#handleContent) {
+      return this.#handleContent(this.content);
+    } else {
+      return this.content;
+    }
   }
 }