From 7eda6868381e9e145d503294837f6f60de7e38e0 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Tue, 18 Apr 2023 21:09:10 -0300 Subject: content: misc. changes, groups divided by tracks, relation sections --- src/content/dependencies/generateTrackInfoPage.js | 4 +- .../dependencies/generateTrackInfoPageContent.js | 188 ++++++++++++++++----- .../generateTrackListDividedByGroups.js | 87 ++++++++++ src/content/util/groupTracksByGroup.js | 23 +++ src/page/track.js | 6 +- src/strings-default.json | 4 +- src/write/build-modes/live-dev-server.js | 2 +- 7 files changed, 262 insertions(+), 52 deletions(-) create mode 100644 src/content/dependencies/generateTrackListDividedByGroups.js create mode 100644 src/content/util/groupTracksByGroup.js (limited to 'src') diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js index 5900b27e..61b2b165 100644 --- a/src/content/dependencies/generateTrackInfoPage.js +++ b/src/content/dependencies/generateTrackInfoPage.js @@ -16,7 +16,7 @@ export default { extraDependencies: ['language'], - relations(relation, track) { + relations(relation, track, {topLevelGroups}) { return { layout: relation('generatePageLayout'), @@ -57,7 +57,7 @@ export default { albumNavLinks: relation('generateAlbumNavLinks', track.album, track), chronologyLinks: relation('generateChronologyLinks'), - content: relation('generateTrackInfoPageContent', track), + content: relation('generateTrackInfoPageContent', track, {topLevelGroups}), sidebar: relation('generateAlbumSidebar', track.album, track), albumStyleRules: relation('generateAlbumStyleRules', track.album), colorStyleRules: relation('generateColorStyleRules', track.color), diff --git a/src/content/dependencies/generateTrackInfoPageContent.js b/src/content/dependencies/generateTrackInfoPageContent.js index 3f37a0c9..eea387bb 100644 --- a/src/content/dependencies/generateTrackInfoPageContent.js +++ b/src/content/dependencies/generateTrackInfoPageContent.js @@ -4,6 +4,7 @@ export default { contentDependencies: [ 'generateContentHeading', 'generateCoverArtwork', + 'generateTrackListDividedByGroups', 'linkAlbum', 'linkContribution', 'linkExternal', @@ -16,52 +17,104 @@ export default { 'transformMultiline', ], - relations(relation, track) { - const relations = {}; - + relations(relation, track, {topLevelGroups}) { const {album} = track; + const relations = {}; + const sections = relations.sections = {}; + const contributionLinksRelation = contribs => contribs.map(contrib => relation('linkContribution', contrib.who, contrib.what)); + // Section: Release info + + const releaseInfo = sections.releaseInfo = {}; + + releaseInfo.artistContributionLinks = + contributionLinksRelation(track.artistContribs); + if (track.hasUniqueCoverArt) { relations.cover = relation('generateCoverArtwork', track.artTags); - relations.coverArtistLinks = + releaseInfo.coverArtistContributionLinks = contributionLinksRelation(track.coverArtistContribs); } else if (album.hasCoverArt) { relations.cover = relation('generateCoverArtwork', album.artTags); - relations.coverArtistLinks = null; } else { relations.cover = null; - relations.coverArtistLinks = null; } - relations.artistLinks = - contributionLinksRelation(track.artistContribs); + // Section: Listen on - relations.externalLinks = - track.urls.map(url => - relation('linkExternal', url)); + const listen = sections.listen = {}; - relations.otherReleasesHeading = + listen.heading = relation('generateContentHeading'); - relations.otherReleases = - track.otherReleases.map(track => ({ - trackLink: relation('linkTrack', track), - albumLink: relation('linkAlbum', track.album), - })); + if (!empty(track.urls)) { + listen.externalLinks = + track.urls.map(url => + relation('linkExternal', url)); + } + + // Section: Other releases + + if (!empty(track.otherReleases)) { + const otherReleases = sections.otherReleases = {}; + + otherReleases.heading = + relation('generateContentHeading'); + + otherReleases.items = + track.otherReleases.map(track => ({ + trackLink: relation('linkTrack', track), + albumLink: relation('linkAlbum', track.album), + })); + } + + // Section: Contributors if (!empty(track.contributorContribs)) { - relations.contributorsHeading = + const contributors = sections.contributors = {}; + + contributors.heading = relation('generateContentHeading'); - relations.contributorLinks = + + contributors.contributionLinks = contributionLinksRelation(track.contributorContribs); } + // Section: Referenced tracks + + if (!empty(track.referencedTracks)) { + const references = sections.references = {}; + + references.heading = + relation('generateContentHeading'); + + references.items = + track.referencedTracks.map(track => ({ + trackLink: relation('linkTrack', track), + contributionLinks: contributionLinksRelation(track.artistContribs), + })); + } + + // Section: Tracks that reference + + if (!empty(track.referencedByTracks)) { + const referencedBy = sections.referencedBy = {}; + + referencedBy.heading = + relation('generateContentHeading'); + + referencedBy.list = + relation('generateTrackListDividedByGroups', + track.referencedByTracks, + topLevelGroups); + } + return relations; }, @@ -70,6 +123,7 @@ export default { const {album} = track; + data.name = track.name; data.date = track.date; data.duration = track.duration; @@ -99,14 +153,28 @@ export default { }) { const content = {}; - const formatContributions = contributionLinks => - language.formatConjunctionList( - contributionLinks.map(link => - link - .slots({ - showContribution: true, - showIcons: true, - }))); + const {sections: sec} = relations; + + const formatContributions = + (contributionLinks, {showContribution = true, showIcons = true} = {}) => + language.formatConjunctionList( + contributionLinks.map(link => + link.slots({showContribution, showIcons}))); + + const formatTrackItem = ({trackLink, contributionLinks}) => + html.tag('li', + language.$('trackList.item.withArtists', { + track: trackLink, + by: + html.tag('span', {class: 'by'}, + language.$('trackList.item.withArtists.by', { + artists: + formatContributions(contributionLinks, { + showContribution: false, + showIcons: false, + }), + })), + })); if (data.hasUniqueCoverArt) { content.cover = relations.cover @@ -139,14 +207,14 @@ export default { [html.onlyIfContent]: true, [html.joinChildren]: html.tag('br'), }, [ - !empty(relations.artistLinks) && + sec.releaseInfo.artistContributionLinks && language.$('releaseInfo.by', { - artists: formatContributions(relations.artistLinks), + artists: formatContributions(sec.releaseInfo.artistContributionLinks), }), - !empty(relations.coverArtistLinks) && + sec.releaseInfo.coverArtistContributionLinks && language.$('releaseInfo.coverArtBy', { - artists: formatContributions(relations.coverArtistLinks), + artists: formatContributions(sec.releaseInfo.coverArtistContributionLinks), }), data.date && @@ -191,22 +259,25 @@ export default { ]), */ - html.tag('p', - (empty(relations.externalLinks) - ? language.$('releaseInfo.listenOn.noLinks') - : language.$('releaseInfo.listenOn', { - links: language.formatDisjunctionList(relations.externalLinks), - }))), - - !empty(relations.otherReleases) && [ - relations.otherReleasesHeading + sec.listen.heading.slots({ + id: 'listen-on', + title: + (sec.listen.externalLinks + ? language.$('releaseInfo.listenOn', { + links: language.formatDisjunctionList(sec.listen.externalLinks), + }) + : language.$('releaseInfo.listenOn.noLinks')), + }), + + sec.otherReleases && [ + sec.otherReleases.heading .slots({ id: 'also-released-as', title: language.$('releaseInfo.alsoReleasedAs'), }), html.tag('ul', - relations.otherReleases.map(({trackLink, albumLink}) => + sec.otherReleases.items.map(({trackLink, albumLink}) => html.tag('li', language.$('releaseInfo.alsoReleasedAs.item', { track: trackLink, @@ -214,21 +285,48 @@ export default { })))), ], - relations.contributorLinks && [ - relations.contributorsHeading + sec.contributors && [ + sec.contributors.heading .slots({ id: 'contributors', title: language.$('releaseInfo.contributors'), }), - html.tag('ul', relations.contributorLinks.map(contributorLink => + html.tag('ul', sec.contributors.contributionLinks.map(contributionLink => html.tag('li', - contributorLink + contributionLink .slots({ showIcons: true, showContribution: true, })))), ], + + sec.references && [ + sec.references.heading + .slots({ + id: 'references', + title: + language.$('releaseInfo.tracksReferenced', { + track: html.tag('i', data.name), + }), + }), + + html.tag('ul', + sec.references.items.map(formatTrackItem)), + ], + + sec.referencedBy && [ + sec.referencedBy.heading + .slots({ + id: 'referenced-by', + title: + language.$('releaseInfo.tracksThatReference', { + track: html.tag('i', data.name), + }), + }), + + sec.referencedBy.list, + ], ]), }; diff --git a/src/content/dependencies/generateTrackListDividedByGroups.js b/src/content/dependencies/generateTrackListDividedByGroups.js new file mode 100644 index 00000000..69fedb28 --- /dev/null +++ b/src/content/dependencies/generateTrackListDividedByGroups.js @@ -0,0 +1,87 @@ +import {empty} from '../../util/sugar.js'; + +import groupTracksByGroup from '../util/groupTracksByGroup.js'; + +export default { + contentDependencies: ['linkTrack', 'linkContribution'], + + extraDependencies: ['html', 'language'], + + relations(relation, tracks, groups) { + if (empty(tracks)) { + return {}; + } + + const trackRelations = track => ({ + trackLink: + relation('linkTrack', track), + + contributionLinks: + track.artistContribs.map(contrib => + relation('linkContribution', contrib.who, contrib.what)), + }); + + if (empty(groups)) { + return { + flatItems: tracks.map(trackRelations), + }; + } + + const lists = groupTracksByGroup(tracks, groups); + + return { + groupedItems: + Array.from(lists.entries()).map(([groupOrOther, tracks]) => ({ + ...(groupOrOther === 'other' + ? {other: true} + : {groupLink: relation('linkGroup', groupOrOther)}), + + items: tracks.map(trackRelations), + })), + }; + }, + + generate(relations, {html, language}) { + // TODO: This is copy-pasted from generateTrackInfoPageContent, seems bad + + const formatContributions = + (contributionLinks, {showContribution = true, showIcons = true} = {}) => + language.formatConjunctionList( + contributionLinks.map(link => + link.slots({showContribution, showIcons}))); + + const formatTrackItem = ({trackLink, contributionLinks}) => + html.tag('li', + language.$('trackList.item.withArtists', { + track: trackLink, + by: + html.tag('span', {class: 'by'}, + language.$('trackList.item.withArtists.by', { + artists: + formatContributions(contributionLinks, { + showContribution: false, + showIcons: false, + }), + })), + })); + + if (relations.flatItems) { + return html.tag('ul', + relations.flatItems.map(formatTrackItem)); + } + + return html.tag('dl', + relations.groupedItems.map(({other, groupLink, items}) => [ + html.tag('dt', + (other + ? language.$('trackList.group.fromOther') + : language.$('trackList.group', { + group: groupLink + }))), + + html.tag('dd', + html.tag('ul', + items.map(formatTrackItem))), + ])); + }, +}; diff --git a/src/content/util/groupTracksByGroup.js b/src/content/util/groupTracksByGroup.js new file mode 100644 index 00000000..bae2c8c5 --- /dev/null +++ b/src/content/util/groupTracksByGroup.js @@ -0,0 +1,23 @@ +import {empty} from '../../util/sugar.js'; + +export default function groupTracksByGroup(tracks, groups) { + const lists = new Map(groups.map(group => [group, []])); + lists.set('other', []); + + for (const track of tracks) { + const group = groups.find(group => group.albums.includes(track.album)); + if (group) { + lists.get(group).push(track); + } else { + other.get('other').push(track); + } + } + + for (const [key, tracks] of lists.entries()) { + if (empty(tracks)) { + lists.delete(key); + } + } + + return lists; +} diff --git a/src/page/track.js b/src/page/track.js index e75b6958..9b3867c9 100644 --- a/src/page/track.js +++ b/src/page/track.js @@ -6,7 +6,7 @@ export function targets({wikiData}) { return wikiData.trackData; } -export function pathsForTarget(track) { +export function pathsForTarget(track, {wikiInfo}) { return [ { type: 'page', @@ -14,7 +14,9 @@ export function pathsForTarget(track) { contentFunction: { name: 'generateTrackInfoPage', - args: [track], + args: [track, { + topLevelGroups: wikiInfo.divideTrackListsByGroups, + }], }, }, ]; diff --git a/src/strings-default.json b/src/strings-default.json index a075f445..b7151f16 100644 --- a/src/strings-default.json +++ b/src/strings-default.json @@ -135,8 +135,8 @@ "releaseInfo.midiProjectFiles.heading": "Download MIDI/project files:", "releaseInfo.note": "Note:", "trackList.section.withDuration": "{SECTION} ({DURATION}):", - "trackList.group": "{GROUP}:", - "trackList.group.other": "Other", + "trackList.group": "From {GROUP}:", + "trackList.group.fromOther": "From somewhere else:", "trackList.item.withDuration": "({DURATION}) {TRACK}", "trackList.item.withDuration.withArtists": "({DURATION}) {TRACK} {BY}", "trackList.item.withArtists": "{TRACK} {BY}", diff --git a/src/write/build-modes/live-dev-server.js b/src/write/build-modes/live-dev-server.js index 50caafe9..c15fc465 100644 --- a/src/write/build-modes/live-dev-server.js +++ b/src/write/build-modes/live-dev-server.js @@ -102,7 +102,7 @@ export async function go({ }) => () => targetless ? [pageSpec.writeTargetless({wikiData})] - : pageSpec.pathsForTarget(target))).flat(); + : pageSpec.pathsForTarget(target, {wikiInfo: wikiData.wikiInfo}))).flat(); logInfo`Will be serving a total of ${pages.length} pages.`; -- cgit 1.3.0-6-gf8a5