« get me outta code hell

content: group info + gallery pages - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/content
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2023-06-22 16:37:36 -0300
committer(quasar) nebula <qznebula@protonmail.com>2023-06-22 16:37:36 -0300
commit53395fa815cdf6f912e78f80bef8908005186270 (patch)
tree47a5a37c0505e8c10ffba949f5d1a34e44524598 /src/content
parenta5e6bb93032ee32f2fa19689455e6cb8dd9f3da9 (diff)
content: group info + gallery pages
These are good to go! Only thing missing is carousels on gallery pages.
Diffstat (limited to 'src/content')
-rw-r--r--src/content/dependencies/generateGroupGalleryPage.js169
-rw-r--r--src/content/dependencies/generateGroupInfoPage.js170
-rw-r--r--src/content/dependencies/generateGroupNavLinks.js142
-rw-r--r--src/content/dependencies/generateGroupSidebar.js35
-rw-r--r--src/content/dependencies/generateGroupSidebarCategoryDetails.js77
-rw-r--r--src/content/dependencies/linkGroupExtra.js34
6 files changed, 627 insertions, 0 deletions
diff --git a/src/content/dependencies/generateGroupGalleryPage.js b/src/content/dependencies/generateGroupGalleryPage.js
new file mode 100644
index 00000000..ab8a06ae
--- /dev/null
+++ b/src/content/dependencies/generateGroupGalleryPage.js
@@ -0,0 +1,169 @@
+import {
+  getTotalDuration,
+  sortChronologically,
+} from '../../util/wiki-data.js';
+
+export default {
+  contentDependencies: [
+    'generateColorStyleRules',
+    'generateCoverGrid',
+    'generateGroupNavLinks',
+    'generateGroupSidebar',
+    'generatePageLayout',
+    'image',
+    'linkAlbum',
+    'linkListing',
+  ],
+
+  extraDependencies: ['html', 'language', 'wikiData'],
+
+  sprawl({listingSpec, wikiInfo}) {
+    const sprawl = {};
+    sprawl.enableGroupUI = wikiInfo.enableGroupUI;
+
+    if (wikiInfo.enableListings && wikiInfo.enableGroupUI) {
+      sprawl.groupsByCategoryListing =
+        listingSpec
+          .find(l => l.directory === 'groups/by-category');
+    }
+
+    return sprawl;
+  },
+
+  relations(relation, sprawl, group) {
+    const relations = {};
+
+    const albums =
+      sortChronologically(group.albums.slice(), {latestFirst: true});
+
+    relations.layout =
+      relation('generatePageLayout');
+
+    relations.navLinks =
+      relation('generateGroupNavLinks', group);
+
+    if (sprawl.enableGroupUI) {
+      relations.sidebar =
+        relation('generateGroupSidebar', group);
+    }
+
+    relations.colorStyleRules =
+      relation('generateColorStyleRules', group.color);
+
+    if (sprawl.groupsByCategoryListing) {
+      relations.groupListingLink =
+        relation('linkListing', sprawl.groupsByCategoryListing);
+    }
+
+    relations.coverGrid =
+      relation('generateCoverGrid');
+
+    relations.links =
+      albums
+        .map(album => relation('linkAlbum', album));
+
+    relations.images =
+      albums.map(album =>
+        (album.hasCoverArt
+          ? relation('image', album.artTags)
+          : relation('iamge')));
+
+    return relations;
+  },
+
+  data(sprawl, group) {
+    const albums =
+      sortChronologically(group.albums.slice(), {latestFirst: true});
+
+    const tracks = albums.flatMap((album) => album.tracks);
+    const totalDuration = getTotalDuration(tracks, {originalReleasesOnly: true});
+
+    return {
+      name: group.name,
+
+      numAlbums: albums.length,
+      numTracks: tracks.length,
+      totalDuration,
+
+      names: albums.map(album => album.name),
+      paths: albums.map(album =>
+        (album.hasCoverArt
+          ? ['media.albumCover', album.directory, album.coverArtFileExtension]
+          : null)),
+    };
+  },
+
+  generate(data, relations, {html, language}) {
+    return relations.layout
+      .slots({
+        title: language.$('groupGalleryPage.title', {group: data.name}),
+        headingMode: 'static',
+
+        colorStyleRules: [relations.colorStyleRules],
+
+        mainClasses: ['top-index'],
+        mainContent: [
+          /*
+          getCarouselHTML({
+            items: group.featuredAlbums.slice(0, 12 + 1),
+            srcFn: getAlbumCover,
+            linkFn: link.album,
+          }),
+          */
+
+          html.tag('p',
+            {class: 'quick-info'},
+            language.$('groupGalleryPage.infoLine', {
+              tracks: html.tag('b',
+                language.countTracks(data.numTracks, {
+                  unit: true,
+                })),
+              albums: html.tag('b',
+                language.countAlbums(data.numAlbums, {
+                  unit: true,
+                })),
+              time: html.tag('b',
+                language.formatDuration(data.totalDuration, {
+                  unit: true,
+                })),
+            })),
+
+          relations.groupListingLink &&
+            html.tag('p',
+              {class: 'quick-info'},
+              language.$('groupGalleryPage.anotherGroupLine', {
+                link:
+                  relations.groupListingLink
+                    .slot('content', language.$('groupGalleryPage.anotherGroupLine.link')),
+              })),
+
+          relations.coverGrid
+            .slots({
+              links: relations.links,
+              names: data.names,
+              images:
+                relations.images.map((image, i) =>
+                  image.slots({
+                    path: data.paths[i],
+                    missingSourceContent:
+                      language.$('misc.albumGrid.noCoverArt', {
+                        album: data.names[i],
+                      }),
+                  })),
+            }),
+        ],
+
+        ...(
+          relations.sidebar
+            ?.slot('currentExtra', 'gallery')
+            ?.content
+          ?? {}),
+
+        navLinkStyle: 'hierarchical',
+        navLinks:
+          relations.navLinks
+            .slot('currentExtra', 'gallery')
+            .content,
+      });
+  },
+};
diff --git a/src/content/dependencies/generateGroupInfoPage.js b/src/content/dependencies/generateGroupInfoPage.js
new file mode 100644
index 00000000..3cffb748
--- /dev/null
+++ b/src/content/dependencies/generateGroupInfoPage.js
@@ -0,0 +1,170 @@
+import {empty} from '../../util/sugar.js';
+
+export default {
+  contentDependencies: [
+    'generateColorStyleRules',
+    'generateContentHeading',
+    'generateGroupNavLinks',
+    'generateGroupSidebar',
+    'generatePageLayout',
+    'linkAlbum',
+    'linkExternal',
+    'linkGroupGallery',
+    'linkGroup',
+    'transformContent',
+  ],
+
+  extraDependencies: ['html', 'language', 'wikiData'],
+
+  sprawl({wikiInfo}) {
+    return {
+      enableGroupUI: wikiInfo.enableGroupUI,
+    };
+  },
+
+  relations(relation, sprawl, group) {
+    const relations = {};
+    const sec = relations.sections = {};
+
+    relations.layout =
+      relation('generatePageLayout');
+
+    relations.navLinks =
+      relation('generateGroupNavLinks', group);
+
+    if (sprawl.enableGroupUI) {
+      relations.sidebar =
+        relation('generateGroupSidebar', group);
+    }
+
+    relations.colorStyleRules =
+      relation('generateColorStyleRules', group.color);
+
+    sec.info = {};
+
+    if (!empty(group.urls)) {
+      sec.info.visitLinks =
+        group.urls
+          .map(url => relation('linkExternal', url));
+    }
+
+    if (group.description) {
+      sec.info.description =
+        relation('transformContent', group.description);
+    }
+
+    if (!empty(group.albums)) {
+      sec.albums = {};
+
+      sec.albums.heading =
+        relation('generateContentHeading');
+
+      sec.albums.galleryLink =
+        relation('linkGroupGallery', group);
+
+      sec.albums.entries =
+        group.albums.map(album => {
+          const links = {};
+          links.albumLink = relation('linkAlbum', album);
+
+          const otherGroup = album.groups.find(g => g !== group);
+          if (otherGroup) {
+            links.groupLink = relation('linkGroup', otherGroup);
+          }
+
+          return links;
+        });
+    }
+
+    return relations;
+  },
+
+  data(sprawl, group) {
+    const data = {};
+
+    data.name = group.name;
+
+    if (!empty(group.albums)) {
+      data.albumYears =
+        group.albums
+          .map(album => album.date?.getFullYear());
+    }
+
+    return data;
+  },
+
+  generate(data, relations, {html, language}) {
+    const {sections: sec} = relations;
+
+    return relations.layout
+      .slots({
+        title: language.$('groupInfoPage.title', {group: data.name}),
+        headingMode: 'sticky',
+
+        colorStyleRules: [relations.colorStyleRules],
+
+        mainContent: [
+          sec.info.visitLinks &&
+            html.tag('p',
+              language.$('releaseInfo.visitOn', {
+                links: language.formatDisjunctionList(sec.info.visitLinks),
+              })),
+
+          html.tag('blockquote',
+            {[html.onlyIfContent]: true},
+            sec.info.description
+              ?.slot('mode', 'multiline')),
+
+          sec.albums && [
+            sec.albums.heading
+              .slots({
+                tag: 'h2',
+                title: language.$('groupInfoPage.albumList.title'),
+              }),
+
+            html.tag('p',
+              language.$('groupInfoPage.viewAlbumGallery', {
+                link:
+                  sec.albums.galleryLink
+                    .slot('content', language.$('groupInfoPage.viewAlbumGallery.link')),
+              })),
+
+            html.tag('ul',
+              sec.albums.entries.map(({albumLink, groupLink}, index) => {
+                // All these strings are really jank, and should probably
+                // be implemented with the same 'const parts = [], opts = {}'
+                // form used elsewhere...
+                const year = data.albumYears[index];
+                const item =
+                  (year
+                    ? language.$('groupInfoPage.albumList.item', {
+                        year,
+                        album: albumLink,
+                      })
+                    : language.$('groupInfoPage.albumList.item.withoutYear', {
+                        album: albumLink,
+                      }));
+
+                return html.tag('li',
+                  (groupLink
+                    ? language.$('groupInfoPage.albumList.item.withAccent', {
+                        item,
+                        accent:
+                          html.tag('span', {class: 'other-group-accent'},
+                            language.$('groupInfoPage.albumList.item.otherGroupAccent', {
+                              group:
+                                groupLink.slot('color', false),
+                            })),
+                      })
+                    : item));
+              })),
+          ],
+        ],
+
+        ...relations.sidebar?.content ?? {},
+
+        navLinkStyle: 'hierarchical',
+        navLinks: relations.navLinks.content,
+      });
+  },
+};
diff --git a/src/content/dependencies/generateGroupNavLinks.js b/src/content/dependencies/generateGroupNavLinks.js
new file mode 100644
index 00000000..0b525363
--- /dev/null
+++ b/src/content/dependencies/generateGroupNavLinks.js
@@ -0,0 +1,142 @@
+import {empty} from '../../util/sugar.js';
+
+export default {
+  contentDependencies: [
+    'generatePreviousNextLinks',
+    'linkGroup',
+    'linkGroupGallery',
+    'linkGroupExtra',
+  ],
+
+  extraDependencies: ['html', 'language', 'wikiData'],
+
+  sprawl({groupCategoryData, wikiInfo}) {
+    return {
+      groupCategoryData,
+      enableGroupUI: wikiInfo.enableGroupUI,
+      enableListings: wikiInfo.enableListings,
+    };
+  },
+
+  relations(relation, sprawl, group) {
+    if (!sprawl.enableGroupUI) {
+      return {};
+    }
+
+    const relations = {};
+
+    relations.mainLink =
+      relation('linkGroup', group);
+
+    relations.previousNextLinks =
+      relation('generatePreviousNextLinks');
+
+    const groups = sprawl.groupCategoryData
+      .flatMap(category => category.groups);
+
+    const index = groups.indexOf(group);
+
+    if (index > 0) {
+      relations.previousLink =
+        relation('linkGroupExtra', groups[index - 1]);
+    }
+
+    if (index < groups.length - 1) {
+      relations.nextLink =
+        relation('linkGroupExtra', groups[index + 1]);
+    }
+
+    relations.infoLink =
+      relation('linkGroup', group);
+
+    if (!empty(group.albums)) {
+      relations.galleryLink =
+        relation('linkGroupGallery', group);
+    }
+
+    return relations;
+  },
+
+  data(sprawl) {
+    return {
+      enableGroupUI: sprawl.enableGroupUI,
+      enableListings: sprawl.enableListings,
+    };
+  },
+
+  slots: {
+    showExtraLinks: {type: 'boolean', default: false},
+
+    currentExtra: {
+      validate: v => v.is('gallery'),
+    },
+  },
+
+  generate(data, relations, slots, {language}) {
+    if (!data.enableGroupUI) {
+      return [
+        {auto: 'home'},
+        {auto: 'current'},
+      ];
+    }
+
+    const previousNextLinks =
+      (relations.previousLink || relations.nextLink) &&
+        relations.previousNextLinks.slots({
+          previousLink:
+            relations.previousLink
+              ?.slot('extra', slots.currentExtra)
+              ?.content
+            ?? null,
+          nextLink:
+            relations.nextLink
+              ?.slot('extra', slots.currentExtra)
+              ?.content
+            ?? null,
+        });
+
+    const previousNextPart =
+      previousNextLinks &&
+        language.formatUnitList(
+          previousNextLinks.content.filter(Boolean));
+
+    const infoLink =
+      relations.infoLink.slots({
+        attributes: {class: slots.currentExtra === null && 'current'},
+        content: language.$('misc.nav.info'),
+      });
+
+    const extraLinks = [
+      relations.galleryLink?.slots({
+        attributes: {class: slots.currentExtra === 'gallery' && 'current'},
+        content: language.$('misc.nav.gallery'),
+      }),
+    ];
+
+    const extrasPart =
+      (empty(extraLinks)
+        ? ''
+        : language.formatUnitList([infoLink, ...extraLinks]));
+
+    const accent =
+      `(${[extrasPart, previousNextPart].filter(Boolean).join('; ')})`;
+
+    return [
+      {auto: 'home'},
+
+      data.enableListings &&
+        {
+          path: ['localized.listingIndex'],
+          title: language.$('listingIndex.title'),
+        },
+
+      {
+        accent,
+        html:
+          language.$('groupPage.nav.group', {
+            group: relations.mainLink,
+          }),
+      },
+    ].filter(Boolean);
+  },
+};
diff --git a/src/content/dependencies/generateGroupSidebar.js b/src/content/dependencies/generateGroupSidebar.js
new file mode 100644
index 00000000..6baf37f4
--- /dev/null
+++ b/src/content/dependencies/generateGroupSidebar.js
@@ -0,0 +1,35 @@
+export default {
+  contentDependencies: ['generateGroupSidebarCategoryDetails'],
+  extraDependencies: ['html', 'language', 'wikiData'],
+
+  sprawl({groupCategoryData}) {
+    return {groupCategoryData};
+  },
+
+  relations(relation, sprawl, group) {
+    return {
+      categoryDetails:
+        sprawl.groupCategoryData.map(category =>
+          relation('generateGroupSidebarCategoryDetails', category, group)),
+    };
+  },
+
+  slots: {
+    currentExtra: {
+      validate: v => v.is('gallery'),
+    },
+  },
+
+  generate(relations, slots, {html, language}) {
+    return {
+      leftSidebarContent: [
+        html.tag('h1',
+          language.$('groupSidebar.title')),
+
+        relations.categoryDetails
+          .map(details =>
+            details.slot('currentExtra', slots.currentExtra)),
+      ],
+    };
+  },
+};
diff --git a/src/content/dependencies/generateGroupSidebarCategoryDetails.js b/src/content/dependencies/generateGroupSidebarCategoryDetails.js
new file mode 100644
index 00000000..ec707e39
--- /dev/null
+++ b/src/content/dependencies/generateGroupSidebarCategoryDetails.js
@@ -0,0 +1,77 @@
+import {empty} from '../../util/sugar.js';
+
+export default {
+  contentDependencies: [
+    'generateColorStyleVariables',
+    'linkGroup',
+    'linkGroupGallery',
+  ],
+
+  extraDependencies: ['html', 'language'],
+
+  relations(relation, category) {
+    return {
+      colorVariables: relation('generateColorStyleVariables', category.color),
+
+      // Which of these is used depends on the currentExtra slot, so all
+      // available links are included here.
+      groupLinks: category.groups.map(group => {
+        const links = {};
+        links.info = relation('linkGroup', group);
+
+        if (!empty(group.albums)) {
+          links.gallery = relation('linkGroupGallery', group);
+        }
+
+        return links;
+      }),
+    };
+  },
+
+  data(category, group) {
+    const data = {};
+
+    data.name = category.name;
+    data.isCurrentCategory = category === group.category;
+
+    if (data.isCurrentCategory) {
+      data.currentGroupIndex = category.groups.indexOf(group);
+    }
+
+    return data;
+  },
+
+  slots: {
+    currentExtra: {
+      validate: v => v.is('gallery'),
+    },
+  },
+
+  generate(data, relations, slots, {html, language}) {
+    return html.tag('details',
+      {
+        open: data.isCurrentCategory,
+        class: data.isCurrentCategory && 'current',
+      },
+      [
+        html.tag('summary',
+          {style: relations.colorVariables},
+          html.tag('span',
+            language.$('groupSidebar.groupList.category', {
+              category:
+                html.tag('span', {class: 'group-name'},
+                  data.name),
+            }))),
+
+        html.tag('ul',
+          relations.groupLinks.map((links, index) =>
+            html.tag('li',
+              {class: index === data.currentGroupIndex && 'current'},
+              language.$('groupSidebar.groupList.item', {
+                group:
+                  links[slots.currentExtra ?? 'info'] ??
+                  links.info,
+              })))),
+      ]);
+  },
+};
diff --git a/src/content/dependencies/linkGroupExtra.js b/src/content/dependencies/linkGroupExtra.js
new file mode 100644
index 00000000..ee6a3b1d
--- /dev/null
+++ b/src/content/dependencies/linkGroupExtra.js
@@ -0,0 +1,34 @@
+import {empty} from '../../util/sugar.js';
+
+export default {
+  contentDependencies: [
+    'linkGroup',
+    'linkGroupGallery',
+  ],
+
+  extraDependencies: ['html'],
+
+  relations(relation, group) {
+    const relations = {};
+
+    relations.info =
+      relation('linkGroup', group);
+
+    if (!empty(group.albums)) {
+      relations.gallery =
+        relation('linkGroupGallery', group);
+    }
+
+    return relations;
+  },
+
+  slots: {
+    extra: {
+      validate: v => v.is('gallery'),
+    },
+  },
+
+  generate(relations, slots) {
+    return relations[slots.extra ?? 'info'] ?? relations.info;
+  },
+};