diff options
4 files changed, 145 insertions, 111 deletions
diff --git a/src/content/dependencies/generateAdditionalFilesList.js b/src/content/dependencies/generateAdditionalFilesList.js index fc24ee52..2737a410 100644 --- a/src/content/dependencies/generateAdditionalFilesList.js +++ b/src/content/dependencies/generateAdditionalFilesList.js @@ -1,95 +1,24 @@ -import {empty} from '#sugar'; - -function validateFileMapping(v, validateValue) { - return value => { - v.isObject(value); - - const valueErrors = []; - for (const [fileKey, fileValue] of Object.entries(value)) { - if (fileValue === null) { - continue; - } - - try { - validateValue(fileValue); - } catch (error) { - error.message = `(${fileKey}) ` + error.message; - valueErrors.push(error); - } - } - - if (!empty(valueErrors)) { - throw new AggregateError(valueErrors, `Errors validating values`); - } - }; -} +import {stitchArrays} from '#sugar'; export default { - extraDependencies: ['html', 'language'], - - data: (additionalFiles) => ({ - // Additional files are already a serializable format. - additionalFiles, - }), + extraDependencies: ['html'], slots: { - fileLinks: { - validate: v => validateFileMapping(v, v.isHTML), + chunks: { + validate: v => v.strictArrayOf(v.isHTML), }, - fileSizes: { - validate: v => validateFileMapping(v, v.isWholeNumber), + chunkItems: { + validate: v => v.strictArrayOf(v.isHTML), }, }, - generate(data, slots, {html, language}) { - if (!slots.fileLinks) { - return html.blank(); - } - - const filesWithLinks = new Set( - Object.entries(slots.fileLinks) - .filter(([key, value]) => value) - .map(([key]) => key)); - - if (empty(filesWithLinks)) { - return html.blank(); - } - - const filteredFileGroups = data.additionalFiles - .map(({title, description, files}) => ({ - title, - description, - files: files.filter(f => filesWithLinks.has(f)), - })) - .filter(({files}) => !empty(files)); - - if (empty(filteredFileGroups)) { - return html.blank(); - } - - return html.tag('dl', - filteredFileGroups.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 => - html.tag('li', - (slots.fileSizes?.[file] - ? language.$('releaseInfo.additionalFiles.file.withSize', { - file: slots.fileLinks[file], - size: language.formatFileSize(slots.fileSizes[file]), - }) - : language.$('releaseInfo.additionalFiles.file', { - file: slots.fileLinks[file], - })))))), - ])); - }, + generate: (slots, {html}) => + html.tag('dl', + stitchArrays({ + chunk: slots.chunks, + items: slots.chunkItems, + }).map(({chunk, items}) => + chunk.clone() + .slot('items', items))), }; diff --git a/src/content/dependencies/generateAdditionalFilesListChunk.js b/src/content/dependencies/generateAdditionalFilesListChunk.js new file mode 100644 index 00000000..bb16b778 --- /dev/null +++ b/src/content/dependencies/generateAdditionalFilesListChunk.js @@ -0,0 +1,39 @@ +export default { + extraDependencies: ['html', 'language'], + + slots: { + title: { + type: 'html', + mutable: false, + }, + + description: { + type: 'html', + mutable: false, + }, + + items: { + validate: v => v.looseArrayOf(v.isHTML), + }, + }, + + generate(slots, {html, language}) { + const titleParts = ['releaseInfo.additionalFiles.entry']; + const titleOptions = {title: slots.title}; + + if (!html.isBlank(slots.description)) { + titleParts.push('withDescription'); + titleOptions.description = slots.description; + } + + const dt = + html.tag('dt', + language.$(...titleParts, titleOptions)); + + const dd = + html.tag('dd', + html.tag('ul', slots.items)); + + return html.tags([dt, dd]); + }, +}; diff --git a/src/content/dependencies/generateAdditionalFilesListChunkItem.js b/src/content/dependencies/generateAdditionalFilesListChunkItem.js new file mode 100644 index 00000000..c37d6bb2 --- /dev/null +++ b/src/content/dependencies/generateAdditionalFilesListChunkItem.js @@ -0,0 +1,30 @@ +export default { + extraDependencies: ['html', 'language'], + + slots: { + fileLink: { + type: 'html', + mutable: false, + }, + + fileSize: { + validate: v => v.isWholeNumber, + }, + }, + + generate(slots, {html, language}) { + const itemParts = ['releaseInfo.additionalFiles.file']; + const itemOptions = {file: slots.fileLink}; + + if (slots.fileSize) { + itemParts.push('withSize'); + itemOptions.size = language.formatFileSize(slots.fileSize); + } + + const li = + html.tag('li', + language.$(...itemParts, itemOptions)); + + return li; + }, +}; diff --git a/src/content/dependencies/generateAlbumAdditionalFilesList.js b/src/content/dependencies/generateAlbumAdditionalFilesList.js index 06694c95..3ab0e27a 100644 --- a/src/content/dependencies/generateAlbumAdditionalFilesList.js +++ b/src/content/dependencies/generateAlbumAdditionalFilesList.js @@ -1,30 +1,48 @@ +import {stitchArrays} from '#sugar'; + export default { contentDependencies: [ 'generateAdditionalFilesList', + 'generateAdditionalFilesListChunk', + 'generateAdditionalFilesListChunkItem', 'linkAlbumAdditionalFile', ], extraDependencies: ['getSizeOfAdditionalFile', 'html', 'urls'], + relations: (relation, album, additionalFiles) => ({ + list: + relation('generateAdditionalFilesList', additionalFiles), + + chunks: + additionalFiles + .map(() => relation('generateAdditionalFilesListChunk')), + + chunkItems: + additionalFiles + .map(({files}) => files + .map(() => relation('generateAdditionalFilesListChunkItem'))), + + chunkItemFileLinks: + additionalFiles + .map(({files}) => files + .map(file => relation('linkAlbumAdditionalFile', album, file))), + }), + data: (album, additionalFiles) => ({ albumDirectory: album.directory, - fileLocations: - additionalFiles.flatMap(({files}) => files), - }), + chunkTitles: + additionalFiles + .map(({title}) => title), - relations: (relation, album, additionalFiles) => ({ - additionalFilesList: - relation('generateAdditionalFilesList', additionalFiles), + chunkDescriptions: + additionalFiles + .map(({description}) => description), - additionalFileLinks: - Object.fromEntries( - additionalFiles - .flatMap(({files}) => files) - .map(file => [ - file, - relation('linkAlbumAdditionalFile', album, file), - ])), + chunkItemLocations: + additionalFiles + .map(({files}) => files), }), slots: { @@ -32,17 +50,35 @@ export default { }, generate: (data, relations, slots, {getSizeOfAdditionalFile, urls}) => - relations.additionalFilesList.slots({ - fileLinks: relations.additionalFileLinks, - fileSizes: - Object.fromEntries(data.fileLocations.map(file => [ - file, - (slots.showFileSizes - ? getSizeOfAdditionalFile( - urls - .from('media.root') - .to('media.albumAdditionalFile', data.albumDirectory, file)) - : 0), - ])), + relations.list.slots({ + chunks: + stitchArrays({ + chunk: relations.chunks, + title: data.chunkTitles, + description: data.chunkDescriptions, + }).map(({chunk, title, description}) => + chunk.slots({title, description})), + + chunkItems: + stitchArrays({ + items: relations.chunkItems, + fileLinks: relations.chunkItemFileLinks, + locations: data.chunkItemLocations, + }).map(({items, fileLinks, locations}) => + stitchArrays({ + item: items, + fileLink: fileLinks, + location: locations, + }).map(({item, fileLink, location}) => + item.slots({ + fileLink: fileLink, + fileSize: + (slots.showFileSizes + ? getSizeOfAdditionalFile( + urls + .from('media.root') + .to('media.albumAdditionalFile', data.albumDirectory, location)) + : 0), + }))), }), }; |