From ec0dd58271eabd0dd9fa12fbf51f5b46b8ceb014 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 21 Mar 2023 23:28:38 -0300 Subject: 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. --- src/content-function.js | 57 +++--- .../dependencies/generateAdditionalFilesList.js | 46 +++++ .../generateAlbumAdditionalFilesList.js | 55 +++++ src/content/dependencies/generateAlbumInfoPage.js | 8 +- .../dependencies/generateAlbumInfoPageContent.js | 221 ++++++++++++--------- .../dependencies/generateAlbumSocialEmbed.js | 2 +- src/content/dependencies/generateContentHeading.js | 16 ++ .../dependencies/linkAlbumAdditionalFile.js | 12 ++ src/misc-templates.js | 51 ----- src/page/album.js | 23 --- src/util/html.js | 20 +- 11 files changed, 301 insertions(+), 210 deletions(-) create mode 100644 src/content/dependencies/generateAdditionalFilesList.js create mode 100644 src/content/dependencies/generateAlbumAdditionalFilesList.js create mode 100644 src/content/dependencies/generateContentHeading.js create mode 100644 src/content/dependencies/linkAlbumAdditionalFile.js diff --git a/src/content-function.js b/src/content-function.js index 93bef6bd..5cd5392c 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 00000000..7f257e44 --- /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 00000000..d45fb583 --- /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 8bbb320f..bc4ac1dc 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 a9e51c02..013ab3f4 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]: '
', - }, - [ - 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]: '
', + }, + [ + 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]: '
', - }, - [ - 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]: '
', + }, + [ + 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 87d8eed2..8786a336 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 00000000..baa52080 --- /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 00000000..6c47edca --- /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 afcb9c3b..11a95c7c 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 eeb6dc7c..4ed4dfcb 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 63d7c1cf..e808eefa 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; + } } } -- cgit 1.3.0-6-gf8a5