« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/content/dependencies/listArtistsByLatestContribution.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/content/dependencies/listArtistsByLatestContribution.js')
-rw-r--r--src/content/dependencies/listArtistsByLatestContribution.js367
1 files changed, 367 insertions, 0 deletions
diff --git a/src/content/dependencies/listArtistsByLatestContribution.js b/src/content/dependencies/listArtistsByLatestContribution.js
new file mode 100644
index 00000000..3b9b3a51
--- /dev/null
+++ b/src/content/dependencies/listArtistsByLatestContribution.js
@@ -0,0 +1,367 @@
+import {transposeArrays, empty, stitchArrays} from '../../util/sugar.js';
+
+import {
+  chunkMultipleArrays,
+  compareCaseLessSensitive,
+  compareDates,
+  filterMultipleArrays,
+  reduceMultipleArrays,
+  sortAlphabetically,
+  sortMultipleArrays,
+} from '../../util/wiki-data.js';
+
+export default {
+  contentDependencies: [
+    'generateListingPage',
+    'linkAlbum',
+    'linkArtist',
+    'linkFlash',
+  ],
+
+  extraDependencies: ['html', 'language', 'wikiData'],
+
+  sprawl({artistData, wikiInfo}) {
+    return {
+      artistData,
+      enableFlashesAndGames: wikiInfo.enableFlashesAndGames,
+    };
+  },
+
+  query(sprawl, spec) {
+    const query = {
+      spec,
+      enableFlashesAndGames: sprawl.enableFlashesAndGames,
+    };
+
+    const queryContributionInfo = (
+      artistsKey,
+      chunkThingsKey,
+      datesKey,
+      datelessArtistsKey,
+      fn,
+    ) => {
+      const artists = sortAlphabetically(sprawl.artistData.slice());
+
+      // Each value stored in dateLists, corresponding to each artist,
+      // is going to be a list of dates and nulls. Any nulls represent
+      // a contribution which isn't associated with a particular date.
+      const [chunkThingLists, dateLists] =
+        transposeArrays(artists.map(artist => fn(artist)));
+
+      // Scrap artists who don't even have any relevant contributions.
+      // These artists may still have other contributions across the wiki, but
+      // they weren't returned by the callback and so aren't relevant to this
+      // list.
+      filterMultipleArrays(
+        artists,
+        chunkThingLists,
+        dateLists,
+        (artists, chunkThings, dates) => !empty(dates));
+
+      // Also exclude artists whose remaining contributions are all dateless.
+      // But keep track of the artists removed here, since they'll be displayed
+      // in an additional list in the final listing page.
+      const {removed: [datelessArtists]} =
+        filterMultipleArrays(
+          artists,
+          chunkThingLists,
+          dateLists,
+          (artist, chunkThings, dates) => !empty(dates.filter(Boolean)));
+
+      // Cut out dateless contributions. They're not relevant to finding the
+      // latest date.
+      for (const [chunkThings, dates] of transposeArrays([chunkThingLists, dateLists])) {
+        filterMultipleArrays(chunkThings, dates, (chunkThing, date) => date);
+      }
+
+      const [chunkThings, dates] =
+        transposeArrays(
+          transposeArrays([chunkThingLists, dateLists])
+            .map(([chunkThings, dates]) =>
+              reduceMultipleArrays(
+                chunkThings, dates,
+                (accChunkThing, accDate, chunkThing, date) =>
+                  (date && date > accDate
+                    ? [chunkThing, date]
+                    : [accChunkThing, accDate]))));
+
+      sortMultipleArrays(artists, dates, chunkThings,
+        (artistA, artistB, dateA, dateB, chunkThingA, chunkThingB) => {
+          const dateComparison = compareDates(dateA, dateB, {latestFirst: true});
+          if (dateComparison !== 0) {
+            return dateComparison;
+          }
+
+          // TODO: Compare alphabetically, not just by directory.
+          return compareCaseLessSensitive(chunkThingA.directory, chunkThingB.directory);
+        });
+
+      const chunks =
+        chunkMultipleArrays(artists, dates, chunkThings,
+          (artist, lastArtist, date, lastDate, chunkThing, lastChunkThing) =>
+            +date !== +lastDate || chunkThing !== lastChunkThing);
+
+      query[chunkThingsKey] =
+        chunks.map(([artists, dates, chunkThings]) => chunkThings[0]);
+
+      query[datesKey] =
+        chunks.map(([artists, dates, chunkThings]) => dates[0]);
+
+      query[artistsKey] =
+        chunks.map(([artists, dates, chunkThings]) => artists);
+
+      query[datelessArtistsKey] = datelessArtists;
+    };
+
+    queryContributionInfo(
+      'artistsByTrackContributions',
+      'albumsByTrackContributions',
+      'datesByTrackContributions',
+      'datelessArtistsByTrackContributions',
+      artist => {
+        const tracks =
+          [...artist.tracksAsArtist, ...artist.tracksAsContributor]
+            .filter(track => !track.originalReleaseTrack);
+
+        const albums = tracks.map(track => track.album);
+        const dates = tracks.map(track => track.date);
+
+        return [albums, dates];
+      });
+
+    queryContributionInfo(
+      'artistsByArtworkContributions',
+      'albumsByArtworkContributions',
+      'datesByArtworkContributions',
+      'datelessArtistsByArtworkContributions',
+      artist => [
+        [
+          ...artist.tracksAsCoverArtist.map(track => track.album),
+          ...artist.albumsAsCoverArtist,
+          ...artist.albumsAsWallpaperArtist,
+          ...artist.albumsAsBannerArtist,
+        ],
+        [
+          // TODO: Per-artwork dates, see #90.
+          ...artist.tracksAsCoverArtist.map(track => track.coverArtDate),
+          ...artist.albumsAsCoverArtist.map(album => album.coverArtDate),
+          ...artist.albumsAsWallpaperArtist.map(album => album.coverArtDate),
+          ...artist.albumsAsBannerArtist.map(album => album.coverArtDate),
+        ],
+      ]);
+
+    if (sprawl.enableFlashesAndGames) {
+      queryContributionInfo(
+        'artistsByFlashContributions',
+        'flashesByFlashContributions',
+        'datesByFlashContributions',
+        'datelessArtistsByFlashContributions',
+        artist => [
+          [
+            ...artist.flashesAsContributor,
+          ],
+          [
+            ...artist.flashesAsContributor.map(flash => flash.date),
+          ],
+        ]);
+    }
+
+    return query;
+  },
+
+  relations(relation, query) {
+    const relations = {};
+
+    relations.page =
+      relation('generateListingPage', query.spec);
+
+    // Track contributors
+
+    relations.albumLinksByTrackContributions =
+      query.albumsByTrackContributions
+        .map(album => relation('linkAlbum', album));
+
+    relations.artistLinksByTrackContributions =
+      query.artistsByTrackContributions
+        .map(artists =>
+          artists.map(artist => relation('linkArtist', artist)));
+
+    relations.datelessArtistLinksByTrackContributions =
+      query.datelessArtistsByTrackContributions
+        .map(artist => relation('linkArtist', artist));
+
+    // Artwork contributors
+
+    relations.albumLinksByArtworkContributions =
+      query.albumsByArtworkContributions
+        .map(album => relation('linkAlbum', album));
+
+    relations.artistLinksByArtworkContributions =
+      query.artistsByArtworkContributions
+        .map(artists =>
+          artists.map(artist => relation('linkArtist', artist)));
+
+    relations.datelessArtistLinksByArtworkContributions =
+      query.datelessArtistsByArtworkContributions
+        .map(artist => relation('linkArtist', artist));
+
+    // Flash contributors
+
+    if (query.enableFlashesAndGames) {
+      relations.flashLinksByFlashContributions =
+        query.flashesByFlashContributions
+          .map(flash => relation('linkFlash', flash));
+
+      relations.artistLinksByFlashContributions =
+        query.artistsByFlashContributions
+          .map(artists =>
+            artists.map(artist => relation('linkArtist', artist)));
+
+      relations.datelessArtistLinksByFlashContributions =
+        query.datelessArtistsByFlashContributions
+          .map(artist => relation('linkArtist', artist));
+    }
+
+    return relations;
+  },
+
+  data(query) {
+    const data = {};
+
+    data.enableFlashesAndGames = query.enableFlashesAndGames;
+
+    data.datesByTrackContributions = query.datesByTrackContributions;
+    data.datesByArtworkContributions = query.datesByArtworkContributions;
+
+    if (query.enableFlashesAndGames) {
+      data.datesByFlashContributions = query.datesByFlashContributions;
+    }
+
+    return data;
+  },
+
+  generate(data, relations, {html, language}) {
+    const chunkTitles = Object.fromEntries(
+      ([
+        ['tracks', [
+          'album',
+          relations.albumLinksByTrackContributions,
+          data.datesByTrackContributions,
+        ]],
+
+        ['artworks', [
+          'album',
+          relations.albumLinksByArtworkContributions,
+          data.datesByArtworkContributions,
+        ]],
+
+        data.enableFlashesAndGames &&
+          ['flashes', [
+            'flash',
+            relations.flashLinksByFlashContributions,
+            data.datesByFlashContributions,
+          ]],
+      ]).filter(Boolean)
+        .map(([key, [stringsKey, links, dates]]) => [
+          key,
+          stitchArrays({link: links, date: dates})
+            .map(({link, date}) =>
+              html.tag('dt',
+                language.$(`listingPage.listArtists.byLatest.chunk.title.${stringsKey}`, {
+                  [stringsKey]: link,
+                  date: language.formatDate(date),
+                }))),
+        ]));
+
+    const chunkItems = Object.fromEntries(
+      ([
+        ['tracks', relations.artistLinksByTrackContributions],
+        ['artworks', relations.artistLinksByArtworkContributions],
+        data.enableFlashesAndGames &&
+          ['flashes', relations.artistLinksByFlashContributions],
+      ]).filter(Boolean)
+        .map(([key, artistLinkLists]) => [
+          key,
+          artistLinkLists.map(artistLinks =>
+            html.tag('dd',
+              html.tag('ul',
+                artistLinks.map(artistLink =>
+                  html.tag('li',
+                    language.$('listingPage.listArtists.byLatest.chunk.item', {
+                      artist: artistLink,
+                    })))))),
+        ]));
+
+    const lists = Object.fromEntries(
+      ([
+        ['tracks', [
+          chunkTitles.tracks,
+          chunkItems.tracks,
+          relations.datelessArtistLinksByTrackContributions,
+        ]],
+
+        ['artworks', [
+          chunkTitles.artworks,
+          chunkItems.artworks,
+          relations.datelessArtistLinksByArtworkContributions,
+        ]],
+
+        data.enableFlashesAndGames &&
+          ['flashes', [
+            chunkTitles.flashes,
+            chunkItems.flashes,
+            relations.datelessArtistLinksByFlashContributions,
+          ]],
+      ]).filter(Boolean)
+        .map(([key, [titles, items, datelessArtistLinks]]) => [
+          key,
+          html.tags([
+            html.tag('dl',
+              stitchArrays({
+                title: titles,
+                items: items,
+              }).map(({title, items}) => [title, items])),
+
+            !empty(datelessArtistLinks) && [
+              html.tag('p',
+                language.$('listingPage.listArtists.byLatest.dateless.title')),
+
+              html.tag('ul',
+                datelessArtistLinks.map(artistLink =>
+                  html.tag('li',
+                    language.$('listingPage.listArtists.byLatest.dateless.item', {
+                      artist: artistLink,
+                    })))),
+            ],
+          ]),
+        ]));
+
+    return relations.page.slots({
+      type: 'custom',
+      content:
+        html.tag('div', {class: 'content-columns'}, [
+          html.tag('div', {class: 'column'}, [
+            html.tag('h2',
+              language.$('listingPage.misc.trackContributors')),
+
+            lists.tracks,
+          ]),
+
+          html.tag('div', {class: 'column'}, [
+            html.tag('h2',
+              language.$(
+                'listingPage.misc.artContributors')),
+
+            lists.artworks,
+
+            lists.flashes && [
+              html.tag('h2',
+                language.$('listingPage.misc.flashContributors')),
+
+              lists.flashes,
+            ],
+          ]),
+        ]),
+    });
+  },
+};