« get me outta code hell

module-ify track pages - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/page
diff options
context:
space:
mode:
author(quasar) nebula <towerofnix@gmail.com>2021-06-01 19:57:57 -0300
committer(quasar) nebula <towerofnix@gmail.com>2021-06-01 19:57:57 -0300
commitca6613c8585b6a7a46a390960b31a377c57f4028 (patch)
treea165ffba0e6833071582eef792e3819667516e35 /src/page
parent234747721fec14e69fc75d6c0134ad4a5ae7736e (diff)
module-ify track pages
Diffstat (limited to 'src/page')
-rw-r--r--src/page/album.js20
-rw-r--r--src/page/index.js24
-rw-r--r--src/page/track.js331
3 files changed, 369 insertions, 6 deletions
diff --git a/src/page/album.js b/src/page/album.js
index f8597790..bc1668a0 100644
--- a/src/page/album.js
+++ b/src/page/album.js
@@ -5,6 +5,8 @@
 //   - generateAlbumNavLinks
 //   - generateAlbumChronologyLinks
 
+// Imports
+
 import fixWS from 'fix-whitespace';
 
 import {
@@ -20,6 +22,8 @@ import {
 
 import * as html from '../util/html.js';
 
+// Page exports
+
 export function targets({wikiData}) {
     return wikiData.albumData;
 }
@@ -225,7 +229,7 @@ export function write(album, {wikiData}) {
                     album.tracks.length > 1 &&
                     {
                         divider: false,
-                        html: generateAlbumNavLinks(album, null, {link, strings})
+                        html: generateAlbumNavLinks(album, null, {strings})
                     }
                 ],
                 content: html.tag('div', generateAlbumChronologyLinks(album, null, {chronologyLinks}))
@@ -236,6 +240,8 @@ export function write(album, {wikiData}) {
     return [page, data];
 }
 
+// Utility exports
+
 export function generateAlbumSidebar(album, currentTrack, {
     fancifyURL,
     link,
@@ -344,13 +350,15 @@ export function generateAlbumSidebar(album, currentTrack, {
     }
 }
 
-export function generateAlbumNavLinks(album, currentTrack, {link, strings}) {
+export function generateAlbumNavLinks(album, currentTrack, {
+    generatePreviousNextLinks,
+    strings
+}) {
     if (album.tracks.length <= 1) {
         return '';
     }
 
     const previousNextLinks = currentTrack && generatePreviousNextLinks(currentTrack, {
-        link, strings,
         data: album.tracks,
         linkKey: 'track'
     });
@@ -365,14 +373,14 @@ export function generateAlbumNavLinks(album, currentTrack, {link, strings}) {
         : `<span class="js-hide-until-data">(${randomLink})</span>`);
 }
 
-export function generateAlbumChronologyLinks(album, currentTrack, {chronologyLinks}) {
+export function generateAlbumChronologyLinks(album, currentTrack, {generateChronologyLinks}) {
     return [
-        currentTrack && chronologyLinks(currentTrack, {
+        currentTrack && generateChronologyLinks(currentTrack, {
             contribKey: 'artists',
             getThings: artist => [...artist.tracks.asArtist, ...artist.tracks.asContributor],
             headingString: 'misc.chronology.heading.track'
         }),
-        chronologyLinks(currentTrack || album, {
+        generateChronologyLinks(currentTrack || album, {
             contribKey: 'coverArtists',
             getThings: artist => [...artist.albums.asCoverArtist, ...artist.tracks.asCoverArtist],
             headingString: 'misc.chronology.heading.coverArt'
diff --git a/src/page/index.js b/src/page/index.js
index ca782389..a37f8a90 100644
--- a/src/page/index.js
+++ b/src/page/index.js
@@ -1,5 +1,29 @@
 // NB: This is the index for the page/ directory and contains exports for all
 // other modules here! It's not the page spec for the homepage - see
 // homepage.js for that.
+//
+// Each module published in this list should follow a particular format,
+// including the following exports:
+//
+// targets({wikiData})
+//     Gets the objects which this page's write() function should be called on.
+//     Usually this will simply mean returning the appropriate thingData array,
+//     but it may also apply filter/map/etc if useful.
+//
+// write(thing, {wikiData})
+//     Gets descriptors for any page and data writes associated with the given
+//     thing (which will be a value from the targets() array). This includes
+//     page (HTML) writes, data (JSON) writes, etc. Notably, this function does
+//     not perform any file operations itself; it only describes the operations
+//     which will be processed elsewhere, once for each translation language.
+//     The write function also immediately transforms any data which will be
+//     reused across writes of the same page, so that this data is effectively
+//     cached (rather than recalculated for each language/write).
+//
+// As these modules are effectively the HTML templates for all site layout,
+// common patterns may also be exported alongside the special exports above.
+// These functions should be referenced only from adjacent modules, as they
+// pertain only to site page generation.
 
 export * as album from './album.js';
+export * as track from './track.js';
diff --git a/src/page/track.js b/src/page/track.js
new file mode 100644
index 00000000..2dec9bd3
--- /dev/null
+++ b/src/page/track.js
@@ -0,0 +1,331 @@
+// Track page specification.
+
+// Imports
+
+import fixWS from 'fix-whitespace';
+
+import {
+    generateAlbumChronologyLinks,
+    generateAlbumNavLinks,
+    generateAlbumSidebar
+} from './album.js';
+
+import {
+    getThemeString
+} from '../util/colors.js';
+
+import * as html from '../util/html.js';
+
+import {
+    OFFICIAL_GROUP_DIRECTORY,
+    UNRELEASED_TRACKS_DIRECTORY
+} from '../util/magic-constants.js';
+
+import {
+    bindOpts
+} from '../util/sugar.js';
+
+import {
+    getTrackCover,
+    getAlbumListTag,
+    sortByDate
+} from '../util/wiki-data.js';
+
+// Page exports
+
+export function targets({wikiData}) {
+    return wikiData.trackData;
+}
+
+export function write(track, {wikiData}) {
+    const { groupData, wikiInfo } = wikiData;
+    const { album } = track;
+
+    const tracksThatReference = track.referencedBy;
+    const useDividedReferences = groupData.some(group => group.directory === OFFICIAL_GROUP_DIRECTORY);
+    const ttrFanon = (useDividedReferences &&
+        tracksThatReference.filter(t => t.album.groups.every(group => group.directory !== OFFICIAL_GROUP_DIRECTORY)));
+    const ttrOfficial = (useDividedReferences &&
+        tracksThatReference.filter(t => t.album.groups.some(group => group.directory === OFFICIAL_GROUP_DIRECTORY)));
+
+    const tracksReferenced = track.references;
+    const otherReleases = track.otherReleases;
+    const listTag = getAlbumListTag(album);
+
+    let flashesThatFeature;
+    if (wikiInfo.features.flashesAndGames) {
+        flashesThatFeature = sortByDate([track, ...otherReleases]
+            .flatMap(track => track.flashes.map(flash => ({flash, as: track}))));
+    }
+
+    const unbound_generateTrackList = (tracks, {getArtistString, link, strings}) => html.tag('ul',
+        tracks.map(track => {
+            const line = strings('trackList.item.withArtists', {
+                track: link.track(track),
+                by: `<span class="by">${strings('trackList.item.withArtists.by', {
+                    artists: getArtistString(track.artists)
+                })}</span>`
+            });
+            return (track.aka
+                ? `<li class="rerelease">${strings('trackList.item.rerelease', {track: line})}</li>`
+                : `<li>${line}</li>`);
+        })
+    );
+
+    const hasCommentary = track.commentary || otherReleases.some(t => t.commentary);
+    const generateCommentary = ({
+        link,
+        strings,
+        transformMultiline
+    }) => transformMultiline([
+        track.commentary,
+        ...otherReleases.map(track =>
+            (track.commentary?.split('\n')
+                .filter(line => line.replace(/<\/b>/g, '').includes(':</i>'))
+                .map(line => fixWS`
+                    ${line}
+                    ${strings('releaseInfo.artistCommentary.seeOriginalRelease', {
+                        original: link.track(track)
+                    })}
+                `)
+                .join('\n')))
+    ].filter(Boolean).join('\n'));
+
+    const data = {
+        type: 'data',
+        path: ['track', track.directory],
+        data: ({
+            serializeContribs,
+            serializeCover,
+            serializeGroupsForTrack,
+            serializeLink
+        }) => ({
+            name: track.name,
+            directory: track.directory,
+            dates: {
+                released: track.date,
+                originallyReleased: track.originalDate,
+                coverArtAdded: track.coverArtDate
+            },
+            duration: track.duration,
+            color: track.color,
+            cover: serializeCover(track, getTrackCover),
+            artists: serializeContribs(track.artists),
+            contributors: serializeContribs(track.contributors),
+            coverArtists: serializeContribs(track.coverArtists || []),
+            album: serializeLink(track.album),
+            groups: serializeGroupsForTrack(track),
+            references: track.references.map(serializeLink),
+            referencedBy: track.referencedBy.map(serializeLink),
+            alsoReleasedAs: otherReleases.map(track => ({
+                track: serializeLink(track),
+                album: serializeLink(track.album)
+            }))
+        })
+    };
+
+    const page = {
+        type: 'page',
+        path: ['track', track.directory],
+        page: ({
+            fancifyURL,
+            generateChronologyLinks,
+            generateCoverLink,
+            generatePreviousNextLinks,
+            getAlbumStylesheet,
+            getArtistString,
+            getTrackCover,
+            link,
+            strings,
+            transformInline,
+            transformLyrics,
+            transformMultiline,
+            to
+        }) => {
+            const generateTrackList = bindOpts(unbound_generateTrackList, {getArtistString, link, strings});
+
+            return {
+                title: strings('trackPage.title', {track: track.name}),
+                stylesheet: getAlbumStylesheet(album, {to}),
+                theme: getThemeString(track.color, [
+                    `--album-directory: ${album.directory}`,
+                    `--track-directory: ${track.directory}`
+                ]),
+
+                // disabled for now! shifting banner position per height of page is disorienting
+                /*
+                banner: album.bannerArtists && {
+                    classes: ['dim'],
+                    dimensions: album.bannerDimensions,
+                    path: ['media.albumBanner', album.directory],
+                    alt: strings('misc.alt.albumBanner'),
+                    position: 'bottom'
+                },
+                */
+
+                main: {
+                    content: fixWS`
+                        ${generateCoverLink({
+                            src: getTrackCover(track),
+                            alt: strings('misc.alt.trackCover'),
+                            tags: track.artTags
+                        })}
+                        <h1>${strings('trackPage.title', {track: track.name})}</h1>
+                        <p>
+                            ${[
+                                strings('releaseInfo.by', {
+                                    artists: getArtistString(track.artists, {
+                                        showContrib: true,
+                                        showIcons: true
+                                    })
+                                }),
+                                track.coverArtists && strings('releaseInfo.coverArtBy', {
+                                    artists: getArtistString(track.coverArtists, {
+                                        showContrib: true,
+                                        showIcons: true
+                                    })
+                                }),
+                                album.directory !== UNRELEASED_TRACKS_DIRECTORY && strings('releaseInfo.released', {
+                                    date: strings.count.date(track.date)
+                                }),
+                                +track.coverArtDate !== +track.date && strings('releaseInfo.artReleased', {
+                                    date: strings.count.date(track.coverArtDate)
+                                }),
+                                track.duration && strings('releaseInfo.duration', {
+                                    duration: strings.count.duration(track.duration)
+                                })
+                            ].filter(Boolean).join('<br>\n')}
+                        </p>
+                        <p>${
+                            (track.urls.length
+                                ? strings('releaseInfo.listenOn', {
+                                    links: strings.list.or(track.urls.map(url => fancifyURL(url, {strings})))
+                                })
+                                : strings('releaseInfo.listenOn.noLinks'))
+                        }</p>
+                        ${otherReleases.length && fixWS`
+                            <p>${strings('releaseInfo.alsoReleasedAs')}</p>
+                            <ul>
+                                ${otherReleases.map(track => fixWS`
+                                    <li>${strings('releaseInfo.alsoReleasedAs.item', {
+                                        track: link.track(track),
+                                        album: link.album(track.album)
+                                    })}</li>
+                                `).join('\n')}
+                            </ul>
+                        `}
+                        ${track.contributors.textContent && fixWS`
+                            <p>
+                                ${strings('releaseInfo.contributors')}
+                                <br>
+                                ${transformInline(track.contributors.textContent)}
+                            </p>
+                        `}
+                        ${track.contributors.length && fixWS`
+                            <p>${strings('releaseInfo.contributors')}</p>
+                            <ul>
+                                ${(track.contributors
+                                    .map(contrib => `<li>${getArtistString([contrib], {
+                                        showContrib: true,
+                                        showIcons: true
+                                    })}</li>`)
+                                    .join('\n'))}
+                            </ul>
+                        `}
+                        ${tracksReferenced.length && fixWS`
+                            <p>${strings('releaseInfo.tracksReferenced', {track: `<i>${track.name}</i>`})}</p>
+                            ${generateTrackList(tracksReferenced)}
+                        `}
+                        ${tracksThatReference.length && fixWS`
+                            <p>${strings('releaseInfo.tracksThatReference', {track: `<i>${track.name}</i>`})}</p>
+                            ${useDividedReferences && fixWS`
+                                <dl>
+                                    ${ttrOfficial.length && fixWS`
+                                        <dt>${strings('trackPage.referenceList.official')}</dt>
+                                        <dd>${generateTrackList(ttrOfficial)}</dd>
+                                    `}
+                                    ${ttrFanon.length && fixWS`
+                                        <dt>${strings('trackPage.referenceList.fandom')}</dt>
+                                        <dd>${generateTrackList(ttrFanon)}</dd>
+                                    `}
+                                </dl>
+                            `}
+                            ${!useDividedReferences && generateTrackList(tracksThatReference)}
+                        `}
+                        ${wikiInfo.features.flashesAndGames && flashesThatFeature.length && fixWS`
+                            <p>${strings('releaseInfo.flashesThatFeature', {track: `<i>${track.name}</i>`})}</p>
+                            <ul>
+                                ${flashesThatFeature.map(({ flash, as }) => html.tag('li',
+                                    {class: as !== track && 'rerelease'},
+                                    (as === track
+                                        ? strings('releaseInfo.flashesThatFeature.item', {
+                                            flash: link.flash(flash)
+                                        })
+                                        : strings('releaseInfo.flashesThatFeature.item.asDifferentRelease', {
+                                            flash: link.flash(flash),
+                                            track: link.track(as)
+                                        })))).join('\n')}
+                            </ul>
+                        `}
+                        ${track.lyrics && fixWS`
+                            <p>${strings('releaseInfo.lyrics')}</p>
+                            <blockquote>
+                                ${transformLyrics(track.lyrics)}
+                            </blockquote>
+                        `}
+                        ${hasCommentary && fixWS`
+                            <p>${strings('releaseInfo.artistCommentary')}</p>
+                            <blockquote>
+                                ${generateCommentary({link, strings, transformMultiline})}
+                            </blockquote>
+                        `}
+                    `
+                },
+
+                sidebarLeft: generateAlbumSidebar(album, track, {
+                    fancifyURL,
+                    link,
+                    strings,
+                    transformMultiline,
+                    wikiData
+                }),
+
+                nav: {
+                    links: [
+                        {toHome: true},
+                        {
+                            path: ['localized.album', album.directory],
+                            title: album.name
+                        },
+                        listTag === 'ol' ? {
+                            html: strings('trackPage.nav.track.withNumber', {
+                                number: album.tracks.indexOf(track) + 1,
+                                track: link.track(track, {class: 'current', to})
+                            })
+                        } : {
+                            html: strings('trackPage.nav.track', {
+                                track: link.track(track, {class: 'current', to})
+                            })
+                        },
+                        album.tracks.length > 1 &&
+                        {
+                            divider: false,
+                            html: generateAlbumNavLinks(album, track, {
+                                generatePreviousNextLinks,
+                                strings
+                            })
+                        }
+                    ].filter(Boolean),
+                    content: fixWS`
+                        <div>
+                            ${generateAlbumChronologyLinks(album, track, {generateChronologyLinks})}
+                        </div>
+                    `
+                }
+            };
+        }
+    };
+
+    return [data, page];
+}
+