diff options
-rw-r--r-- | src/page/homepage.js | 127 | ||||
-rw-r--r-- | src/page/index.js | 1 | ||||
-rwxr-xr-x | src/upd8.js | 227 | ||||
-rw-r--r-- | src/util/wiki-data.js | 116 |
4 files changed, 244 insertions, 227 deletions
diff --git a/src/page/homepage.js b/src/page/homepage.js new file mode 100644 index 00000000..d1dcc680 --- /dev/null +++ b/src/page/homepage.js @@ -0,0 +1,127 @@ +// Homepage specification. + +// Imports + +import fixWS from 'fix-whitespace'; + +import { + getLinkThemeString +} from '../util/colors.js'; + +import find from '../util/find.js'; + +import * as html from '../util/html.js'; + +import { + getNewAdditions, + getNewReleases +} from '../util/wiki-data.js'; + +// Page exports + +export function writeTargetless({wikiData}) { + const { newsData, staticPageData, homepageInfo, wikiInfo } = wikiData; + + const page = { + type: 'page', + path: ['home'], + page: ({ + getAlbumGridHTML, + link, + strings, + to, + transformInline, + transformMultiline + }) => ({ + title: wikiInfo.name, + + meta: { + description: wikiInfo.description + }, + + main: { + classes: ['top-index'], + content: fixWS` + <h1>${wikiInfo.name}</h1> + ${homepageInfo.rows.map((row, i) => fixWS` + <section class="row" style="${getLinkThemeString(row.color)}"> + <h2>${row.name}</h2> + ${row.type === 'albums' && fixWS` + <div class="grid-listing"> + ${getAlbumGridHTML({ + entries: ( + row.group === 'new-releases' ? getNewReleases(row.groupCount, {wikiData}) : + row.group === 'new-additions' ? getNewAdditions(row.groupCount, {wikiData}) : + ((find.group(row.group, {wikiData})?.albums || []) + .slice() + .reverse() + .slice(0, row.groupCount) + .map(album => ({item: album}))) + ).concat(row.albums + .map(album => find.album(album, {wikiData})) + .map(album => ({item: album})) + ), + lazy: i > 0 + })} + ${row.actions.length && fixWS` + <div class="grid-actions"> + ${row.actions.map(action => transformInline(action) + .replace('<a', '<a class="box grid-item"')).join('\n')} + </div> + `} + </div> + `} + </section> + `).join('\n')} + ` + }, + + sidebarLeft: homepageInfo.sidebar && { + wide: true, + collapse: false, + // This is a pretty filthy hack! 8ut otherwise, the [[news]] part + // gets treated like it's a reference to the track named "news", + // which o8viously isn't what we're going for. Gotta catch that + // 8efore we pass it to transformMultiline, 'cuz otherwise it'll + // get repl8ced with just the word "news" (or anything else that + // transformMultiline does with references it can't match) -- and + // we can't match that for replacing it with the news column! + // + // And no, I will not make [[news]] into part of transformMultiline + // (even though that would 8e hilarious). + content: (transformMultiline(homepageInfo.sidebar.replace('[[news]]', '__GENERATE_NEWS__')) + .replace('<p>__GENERATE_NEWS__</p>', wikiInfo.features.news ? fixWS` + <h1>${strings('homepage.news.title')}</h1> + ${newsData.slice(0, 3).map((entry, i) => html.tag('article', + {class: ['news-entry', i === 0 && 'first-news-entry']}, + fixWS` + <h2><time>${strings.count.date(entry.date)}</time> ${link.newsEntry(entry)}</h2> + ${transformMultiline(entry.bodyShort)} + ${entry.bodyShort !== entry.body && link.newsEntry(entry, { + text: strings('homepage.news.entry.viewRest') + })} + `)).join('\n')} + ` : `<p><i>News requested in content description but this feature isn't enabled</i></p>`)) + }, + + nav: { + content: fixWS` + <h2 class="dot-between-spans"> + ${[ + link.home('', {text: wikiInfo.shortName, class: 'current', to}), + wikiInfo.features.listings && + link.listingIndex('', {text: strings('listingIndex.title'), to}), + wikiInfo.features.news && + link.newsIndex('', {text: strings('newsIndex.title'), to}), + wikiInfo.features.flashesAndGames && + link.flashIndex('', {text: strings('flashIndex.title'), to}), + ...staticPageData.filter(page => page.listed).map(link.staticPage) + ].filter(Boolean).map(link => `<span>${link}</span>`).join('\n')} + </h2> + ` + } + }) + }; + + return [page]; +} diff --git a/src/page/index.js b/src/page/index.js index 42204947..c5e10631 100644 --- a/src/page/index.js +++ b/src/page/index.js @@ -43,6 +43,7 @@ export * as album from './album.js'; export * as artist from './artist.js'; export * as artistAlias from './artist-alias.js'; export * as group from './group.js'; +export * as homepage from './homepage.js'; export * as static from './static.js'; export * as news from './news.js'; export * as track from './track.js'; diff --git a/src/upd8.js b/src/upd8.js index a1fafb27..586afe95 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -2151,120 +2151,6 @@ function getFlashGridHTML({ }); } -function getNewReleases(numReleases, {wikiData}) { - const { albumData } = wikiData; - - const latestFirst = albumData.filter(album => album.isListedOnHomepage).reverse(); - const majorReleases = latestFirst.filter(album => album.isMajorRelease); - majorReleases.splice(1); - - const otherReleases = latestFirst - .filter(album => !majorReleases.includes(album)) - .slice(0, numReleases - majorReleases.length); - - return [ - ...majorReleases.map(album => ({large: true, item: album})), - ...otherReleases.map(album => ({large: false, item: album})) - ]; -} - -function getNewAdditions(numAlbums, {wikiData}) { - const { albumData } = wikiData; - - // Sort al8ums, in descending order of priority, 8y... - // - // * D8te of addition to the wiki (descending). - // * Major releases first. - // * D8te of release (descending). - // - // Major releases go first to 8etter ensure they show up in the list (and - // are usually at the start of the final output for a given d8 of release - // too). - const sortedAlbums = albumData.filter(album => album.isListedOnHomepage).sort((a, b) => { - if (a.dateAdded > b.dateAdded) return -1; - if (a.dateAdded < b.dateAdded) return 1; - if (a.isMajorRelease && !b.isMajorRelease) return -1; - if (!a.isMajorRelease && b.isMajorRelease) return 1; - if (a.date > b.date) return -1; - if (a.date < b.date) return 1; - }); - - // When multiple al8ums are added to the wiki at a time, we want to show - // all of them 8efore pulling al8ums from the next (earlier) date. We also - // want to show a diverse selection of al8ums - with limited space, we'd - // rather not show only the latest al8ums, if those happen to all 8e - // closely rel8ted! - // - // Specifically, we're concerned with avoiding too much overlap amongst - // the primary (first/top-most) group. We do this 8y collecting every - // primary group present amongst the al8ums for a given d8 into one - // (ordered) array, initially sorted (inherently) 8y latest al8um from - // the group. Then we cycle over the array, adding one al8um from each - // group until all the al8ums from that release d8 have 8een added (or - // we've met the total target num8er of al8ums). Once we've added all the - // al8ums for a given group, it's struck from the array (so the groups - // with the most additions on one d8 will have their oldest releases - // collected more towards the end of the list). - - const albums = []; - - let i = 0; - outerLoop: while (i < sortedAlbums.length) { - // 8uild up a list of groups and their al8ums 8y order of decending - // release, iter8ting until we're on a different d8. (We use a map for - // indexing so we don't have to iter8te through the entire array each - // time we access one of its entries. This is 8asically unnecessary - // since this will never 8e an expensive enough task for that to - // matter.... 8ut it's nicer code. BBBB) ) - const currentDate = sortedAlbums[i].dateAdded; - const groupMap = new Map(); - const groupArray = []; - for (let album; (album = sortedAlbums[i]) && +album.dateAdded === +currentDate; i++) { - const primaryGroup = album.groups[0]; - if (groupMap.has(primaryGroup)) { - groupMap.get(primaryGroup).push(album); - } else { - const entry = [album] - groupMap.set(primaryGroup, entry); - groupArray.push(entry); - } - } - - // Then cycle over that sorted array, adding one al8um from each to - // the main array until we've run out or have met the target num8er - // of al8ums. - while (groupArray.length) { - let j = 0; - while (j < groupArray.length) { - const entry = groupArray[j]; - const album = entry.shift(); - albums.push(album); - - - // This is the only time we ever add anything to the main al8um - // list, so it's also the only place we need to check if we've - // met the target length. - if (albums.length === numAlbums) { - // If we've met it, 8r8k out of the outer loop - we're done - // here! - break outerLoop; - } - - if (entry.length) { - j++; - } else { - groupArray.splice(j, 1); - } - } - } - } - - // Finally, do some quick mapping shenanigans to 8etter display the result - // in a grid. (This should pro8a8ly 8e a separ8te, shared function, 8ut - // whatevs.) - return albums.map(album => ({large: album.isMajorRelease, item: album})); -} - function writeSymlinks() { return progressPromiseAll('Writing site symlinks.', [ link(path.join(__dirname, UTILITY_DIRECTORY), 'shared.utilityRoot'), @@ -2316,119 +2202,6 @@ function writeSharedFilesAndPages({strings, wikiData}) { ].filter(Boolean)); } -function writeHomepage({wikiData}) { - const { newsData, staticPageData, homepageInfo, wikiInfo } = wikiData; - - const page = { - type: 'page', - path: ['home'], - page: ({ - getAlbumGridHTML, - link, - strings, - to, - transformInline, - transformMultiline, - }) => ({ - title: wikiInfo.name, - - meta: { - description: wikiInfo.description - }, - - main: { - classes: ['top-index'], - content: fixWS` - <h1>${wikiInfo.name}</h1> - ${homepageInfo.rows.map((row, i) => fixWS` - <section class="row" style="${getLinkThemeString(row.color)}"> - <h2>${row.name}</h2> - ${row.type === 'albums' && fixWS` - <div class="grid-listing"> - ${getAlbumGridHTML({ - entries: ( - row.group === 'new-releases' ? getNewReleases(row.groupCount, {wikiData}) : - row.group === 'new-additions' ? getNewAdditions(row.groupCount, {wikiData}) : - ((find.group(row.group, {wikiData})?.albums || []) - .slice() - .reverse() - .slice(0, row.groupCount) - .map(album => ({item: album}))) - ).concat(row.albums - .map(album => find.album(album, {wikiData})) - .map(album => ({item: album})) - ), - lazy: i > 0 - })} - ${row.actions.length && fixWS` - <div class="grid-actions"> - ${row.actions.map(action => transformInline(action) - .replace('<a', '<a class="box grid-item"')).join('\n')} - </div> - `} - </div> - `} - </section> - `).join('\n')} - ` - }, - - sidebarLeft: homepageInfo.sidebar && { - wide: true, - collapse: false, - // This is a pretty filthy hack! 8ut otherwise, the [[news]] part - // gets treated like it's a reference to the track named "news", - // which o8viously isn't what we're going for. Gotta catch that - // 8efore we pass it to transformMultiline, 'cuz otherwise it'll - // get repl8ced with just the word "news" (or anything else that - // transformMultiline does with references it can't match) -- and - // we can't match that for replacing it with the news column! - // - // And no, I will not make [[news]] into part of transformMultiline - // (even though that would 8e hilarious). - content: (transformMultiline(homepageInfo.sidebar.replace('[[news]]', '__GENERATE_NEWS__')) - .replace('<p>__GENERATE_NEWS__</p>', wikiInfo.features.news ? fixWS` - <h1>${strings('homepage.news.title')}</h1> - ${newsData.slice(0, 3).map((entry, i) => fixWS` - <article ${classes('news-entry', i === 0 && 'first-news-entry')}> - <h2><time>${strings.count.date(entry.date)}</time> ${link.newsEntry(entry)}</h2> - ${transformMultiline(entry.bodyShort)} - ${entry.bodyShort !== entry.body && link.newsEntry(entry, { - text: strings('homepage.news.entry.viewRest') - })} - </article> - `).join('\n')} - ` : `<p><i>News requested in content description but this feature isn't enabled</i></p>`)) - }, - - nav: { - content: fixWS` - <h2 class="dot-between-spans"> - ${[ - link.home('', {text: wikiInfo.shortName, class: 'current', to}), - wikiInfo.features.listings && - link.listingIndex('', {text: strings('listingIndex.title'), to}), - wikiInfo.features.news && - link.newsIndex('', {text: strings('newsIndex.title'), to}), - wikiInfo.features.flashesAndGames && - link.flashIndex('', {text: strings('flashIndex.title'), to}), - ...staticPageData.filter(page => page.listed).map(link.staticPage) - ].filter(Boolean).map(link => `<span>${link}</span>`).join('\n')} - </h2> - ` - } - }) - }; - - return [page]; -} - -function writeMiscellaneousPages({wikiData}) { - return [ - writeHomepage({wikiData}) - ]; -} - function getRevealStringFromWarnings(warnings, {strings}) { return strings('misc.contentWarnings', {warnings}) + `<br><span class="reveal-interaction">${strings('misc.contentWarnings.reveal')}</span>` } diff --git a/src/util/wiki-data.js b/src/util/wiki-data.js index e4142c85..2527a4b3 100644 --- a/src/util/wiki-data.js +++ b/src/util/wiki-data.js @@ -159,3 +159,119 @@ export function getTrackCover(track, {to}) { return to('media.trackCover', track.album.directory, track.directory); } } + +// Big-ass homepage row functions + +export function getNewAdditions(numAlbums, {wikiData}) { + const { albumData } = wikiData; + + // Sort al8ums, in descending order of priority, 8y... + // + // * D8te of addition to the wiki (descending). + // * Major releases first. + // * D8te of release (descending). + // + // Major releases go first to 8etter ensure they show up in the list (and + // are usually at the start of the final output for a given d8 of release + // too). + const sortedAlbums = albumData.filter(album => album.isListedOnHomepage).sort((a, b) => { + if (a.dateAdded > b.dateAdded) return -1; + if (a.dateAdded < b.dateAdded) return 1; + if (a.isMajorRelease && !b.isMajorRelease) return -1; + if (!a.isMajorRelease && b.isMajorRelease) return 1; + if (a.date > b.date) return -1; + if (a.date < b.date) return 1; + }); + + // When multiple al8ums are added to the wiki at a time, we want to show + // all of them 8efore pulling al8ums from the next (earlier) date. We also + // want to show a diverse selection of al8ums - with limited space, we'd + // rather not show only the latest al8ums, if those happen to all 8e + // closely rel8ted! + // + // Specifically, we're concerned with avoiding too much overlap amongst + // the primary (first/top-most) group. We do this 8y collecting every + // primary group present amongst the al8ums for a given d8 into one + // (ordered) array, initially sorted (inherently) 8y latest al8um from + // the group. Then we cycle over the array, adding one al8um from each + // group until all the al8ums from that release d8 have 8een added (or + // we've met the total target num8er of al8ums). Once we've added all the + // al8ums for a given group, it's struck from the array (so the groups + // with the most additions on one d8 will have their oldest releases + // collected more towards the end of the list). + + const albums = []; + + let i = 0; + outerLoop: while (i < sortedAlbums.length) { + // 8uild up a list of groups and their al8ums 8y order of decending + // release, iter8ting until we're on a different d8. (We use a map for + // indexing so we don't have to iter8te through the entire array each + // time we access one of its entries. This is 8asically unnecessary + // since this will never 8e an expensive enough task for that to + // matter.... 8ut it's nicer code. BBBB) ) + const currentDate = sortedAlbums[i].dateAdded; + const groupMap = new Map(); + const groupArray = []; + for (let album; (album = sortedAlbums[i]) && +album.dateAdded === +currentDate; i++) { + const primaryGroup = album.groups[0]; + if (groupMap.has(primaryGroup)) { + groupMap.get(primaryGroup).push(album); + } else { + const entry = [album] + groupMap.set(primaryGroup, entry); + groupArray.push(entry); + } + } + + // Then cycle over that sorted array, adding one al8um from each to + // the main array until we've run out or have met the target num8er + // of al8ums. + while (groupArray.length) { + let j = 0; + while (j < groupArray.length) { + const entry = groupArray[j]; + const album = entry.shift(); + albums.push(album); + + + // This is the only time we ever add anything to the main al8um + // list, so it's also the only place we need to check if we've + // met the target length. + if (albums.length === numAlbums) { + // If we've met it, 8r8k out of the outer loop - we're done + // here! + break outerLoop; + } + + if (entry.length) { + j++; + } else { + groupArray.splice(j, 1); + } + } + } + } + + // Finally, do some quick mapping shenanigans to 8etter display the result + // in a grid. (This should pro8a8ly 8e a separ8te, shared function, 8ut + // whatevs.) + return albums.map(album => ({large: album.isMajorRelease, item: album})); +} + +export function getNewReleases(numReleases, {wikiData}) { + const { albumData } = wikiData; + + const latestFirst = albumData.filter(album => album.isListedOnHomepage).reverse(); + const majorReleases = latestFirst.filter(album => album.isMajorRelease); + majorReleases.splice(1); + + const otherReleases = latestFirst + .filter(album => !majorReleases.includes(album)) + .slice(0, numReleases - majorReleases.length); + + return [ + ...majorReleases.map(album => ({large: true, item: album})), + ...otherReleases.map(album => ({large: false, item: album})) + ]; +} |