« get me outta code hell

content, external-links: [normal, compact] -> [platform, handle] - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2024-03-29 18:42:54 -0300
committer(quasar) nebula <qznebula@protonmail.com>2024-03-29 20:10:13 -0300
commitb0d20c958cf8ef1edd4ac3ce28beb9ef63d00bdb (patch)
tree2d9da8188a051a2b71b987d0a7efb0929fea400c /src
parentaaeffb2fe2d2e169cf92da5142037214ac341219 (diff)
content, external-links: [normal, compact] -> [platform, handle]
Diffstat (limited to 'src')
-rw-r--r--src/content/dependencies/generateAlbumReleaseInfo.js19
-rw-r--r--src/content/dependencies/generateAlbumSidebarGroupBox.js5
-rw-r--r--src/content/dependencies/generateArtistInfoPage.js7
-rw-r--r--src/content/dependencies/generateGroupInfoPage.js5
-rw-r--r--src/content/dependencies/linkExternal.js2
-rw-r--r--src/content/dependencies/linkExternalAsIcon.js8
-rw-r--r--src/data/things/language.js2
-rw-r--r--src/util/external-links.js295
8 files changed, 106 insertions, 237 deletions
diff --git a/src/content/dependencies/generateAlbumReleaseInfo.js b/src/content/dependencies/generateAlbumReleaseInfo.js
index 5128fbac..6fc1375b 100644
--- a/src/content/dependencies/generateAlbumReleaseInfo.js
+++ b/src/content/dependencies/generateAlbumReleaseInfo.js
@@ -96,17 +96,14 @@ export default {
               language.formatDisjunctionList(
                 relations.externalLinks
                   .map(link =>
-                    link.slots({
-                      context: [
-                        'album',
-                        (data.numTracks === 0
-                          ? 'albumNoTracks'
-                       : data.numTracks === 1
-                          ? 'albumOneTrack'
-                          : 'albumMultipleTracks'),
-                      ],
-                      style: 'normal',
-                    }))),
+                    link.slot('context', [
+                      'album',
+                      (data.numTracks === 0
+                        ? 'albumNoTracks'
+                     : data.numTracks === 1
+                        ? 'albumOneTrack'
+                        : 'albumMultipleTracks'),
+                    ]))),
           })),
     ]);
   },
diff --git a/src/content/dependencies/generateAlbumSidebarGroupBox.js b/src/content/dependencies/generateAlbumSidebarGroupBox.js
index 5b7e2e45..93ebf5d4 100644
--- a/src/content/dependencies/generateAlbumSidebarGroupBox.js
+++ b/src/content/dependencies/generateAlbumSidebarGroupBox.js
@@ -89,10 +89,7 @@ export default {
             links:
               language.formatDisjunctionList(
                 relations.externalLinks
-                  .map(link => link.slots({
-                    context: 'group',
-                    style: 'platform',
-                  }))),
+                  .map(link => link.slot('context', 'group'))),
           })),
 
       slots.mode === 'album' &&
diff --git a/src/content/dependencies/generateArtistInfoPage.js b/src/content/dependencies/generateArtistInfoPage.js
index 1b85680f..ac9209a7 100644
--- a/src/content/dependencies/generateArtistInfoPage.js
+++ b/src/content/dependencies/generateArtistInfoPage.js
@@ -163,11 +163,8 @@ export default {
               language.$('releaseInfo.visitOn', {
                 links:
                   language.formatDisjunctionList(
-                    sec.visit.externalLinks.map(link =>
-                      link.slots({
-                        context: 'artist',
-                        style: 'platform',
-                      }))),
+                    sec.visit.externalLinks
+                      .map(link => link.slot('context', 'artist'))),
               })),
 
           sec.artworks?.artistGalleryLink &&
diff --git a/src/content/dependencies/generateGroupInfoPage.js b/src/content/dependencies/generateGroupInfoPage.js
index 5cae730b..2e1d1688 100644
--- a/src/content/dependencies/generateGroupInfoPage.js
+++ b/src/content/dependencies/generateGroupInfoPage.js
@@ -137,10 +137,7 @@ export default {
                 links:
                   language.formatDisjunctionList(
                     sec.info.visitLinks
-                      .map(link => link.slots({
-                        context: 'group',
-                        style: 'platform',
-                      }))),
+                      .map(link => link.slot('context', 'group'))),
               })),
 
           html.tag('blockquote',
diff --git a/src/content/dependencies/linkExternal.js b/src/content/dependencies/linkExternal.js
index ba2dbf21..282fb76c 100644
--- a/src/content/dependencies/linkExternal.js
+++ b/src/content/dependencies/linkExternal.js
@@ -11,7 +11,7 @@ export default {
       // differentiate between a function that returns a validator (the usual
       // syntax) and a function that is itself a validator.
       validate: () => isExternalLinkStyle,
-      default: 'normal',
+      default: 'platform',
     },
 
     context: {
diff --git a/src/content/dependencies/linkExternalAsIcon.js b/src/content/dependencies/linkExternalAsIcon.js
index 3eb355a9..c363ad90 100644
--- a/src/content/dependencies/linkExternalAsIcon.js
+++ b/src/content/dependencies/linkExternalAsIcon.js
@@ -21,8 +21,8 @@ export default {
     const format = style =>
       language.formatExternalLink(data.url, {style, context: slots.context});
 
-    const normalText = format('normal');
-    const compactText = format('compact');
+    const platformText = format('platform');
+    const handleText = format('handle');
     const iconId = format('icon-id');
 
     return html.tag('a', {class: 'icon'},
@@ -34,7 +34,7 @@ export default {
       [
         html.tag('svg', [
           !slots.withText &&
-            html.tag('title', normalText),
+            html.tag('title', platformText),
 
           html.tag('use', {
             href: to('shared.staticIcon', iconId),
@@ -43,7 +43,7 @@ export default {
 
         slots.withText &&
           html.tag('span', {class: 'icon-text'},
-            compactText ?? normalText),
+            handleText ?? platformText),
       ]);
   },
 };
diff --git a/src/data/things/language.js b/src/data/things/language.js
index 93ed40b6..4079ee71 100644
--- a/src/data/things/language.js
+++ b/src/data/things/language.js
@@ -522,7 +522,7 @@ export class Language extends Thing {
   }
 
   formatExternalLink(url, {
-    style = 'normal',
+    style = 'platform',
     context = 'generic',
   } = {}) {
     if (!this.externalLinkSpec) {
diff --git a/src/util/external-links.js b/src/util/external-links.js
index 877ef8d4..585e28aa 100644
--- a/src/util/external-links.js
+++ b/src/util/external-links.js
@@ -1,8 +1,9 @@
-import {empty, stitchArrays} from '#sugar';
+import {empty, stitchArrays, withEntries} from '#sugar';
 
 import {
   anyOf,
   is,
+  isObject,
   isStringNonEmpty,
   looseArrayOf,
   optional,
@@ -13,9 +14,8 @@ import {
 } from '#validators';
 
 export const externalLinkStyles = [
-  'normal',
-  'compact',
   'platform',
+  'handle',
   'icon-id',
 ];
 
@@ -86,25 +86,24 @@ export const isExternalLinkSpec =
       }),
 
       platform: isStringNonEmpty,
-      substring: optional(isStringNonEmpty),
-
-      // TODO: Don't allow 'handle' or 'custom' options if the corresponding
-      // properties aren't provided
-      normal: optional(is('domain', 'handle', 'custom')),
-      compact: optional(is('domain', 'handle', 'custom')),
-      icon: optional(isStringNonEmpty),
 
       handle: optional(isExternalLinkExtractSpec),
 
-      // TODO: This should validate each value with isExternalLinkExtractSpec.
-      custom: optional(validateAllPropertyValues(isExternalLinkExtractSpec)),
+      detail:
+        optional(anyOf(
+          isStringNonEmpty,
+          validateProperties({
+            [validateProperties.validateOtherKeys]:
+              isExternalLinkExtractSpec,
+
+            substring: isStringNonEmpty,
+          }))),
+
+      icon: optional(isStringNonEmpty),
     }));
 
 export const fallbackDescriptor = {
   platform: 'external',
-
-  normal: 'domain',
-  compact: 'domain',
   icon: 'globe',
 };
 
@@ -120,7 +119,7 @@ export const externalLinkSpec = [
     },
 
     platform: 'youtube',
-    substring: 'playlist',
+    detail: 'playlist',
 
     icon: 'youtube',
   },
@@ -133,7 +132,7 @@ export const externalLinkSpec = [
     },
 
     platform: 'youtube',
-    substring: 'fullAlbum',
+    detail: 'fullAlbum',
 
     icon: 'youtube',
   },
@@ -145,7 +144,7 @@ export const externalLinkSpec = [
     },
 
     platform: 'youtube',
-    substring: 'fullAlbum',
+    detail: 'fullAlbum',
 
     icon: 'youtube',
   },
@@ -159,12 +158,9 @@ export const externalLinkSpec = [
     },
 
     platform: 'patreon',
+    handle: {pathname: /([^/]+)\/?$/},
 
-    normal: 'handle',
-    compact: 'handle',
     icon: 'globe',
-
-    handle: /([^/]*)\/?$/,
   },
 
   {
@@ -174,14 +170,9 @@ export const externalLinkSpec = [
     },
 
     platform: 'youtube',
+    handle: {pathname: /^@([^/]+)\/?$/},
 
-    normal: 'handle',
-    compact: 'handle',
     icon: 'youtube',
-
-    handle: {
-      pathname: /^(@.*?)\/?$/,
-    },
   },
 
   // Special handling for flash links
@@ -193,7 +184,7 @@ export const externalLinkSpec = [
     },
 
     platform: 'bgreco',
-    substring: 'flash',
+    detail: 'flash',
 
     icon: 'globe',
   },
@@ -207,16 +198,13 @@ export const externalLinkSpec = [
     },
 
     platform: 'homestuck',
-    substring: 'page',
-
-    normal: 'custom',
-    icon: 'globe',
 
-    custom: {
-      page: {
-        pathname: /[0-9]+/,
-      },
+    detail: {
+      substring: 'page',
+      page: {pathname: /[0-9]+/},
     },
+
+    icon: 'globe',
   },
 
   {
@@ -227,7 +215,7 @@ export const externalLinkSpec = [
     },
 
     platform: 'homestuck',
-    substring: 'secretPage',
+    detail: 'secretPage',
 
     icon: 'globe',
   },
@@ -239,7 +227,7 @@ export const externalLinkSpec = [
     },
 
     platform: 'youtube',
-    substring: 'flash',
+    detail: 'flash',
 
     icon: 'youtube',
   },
@@ -256,31 +244,26 @@ export const externalLinkSpec = [
     match: {domains: ['artstation.com']},
 
     platform: 'artstation',
+    handle: {pathname: /^[^/]+/},
 
-    compact: 'handle',
     icon: 'globe',
-
-    handle: {pathname: /^[^/]*/},
   },
 
   {
     match: {domains: ['.artstation.com']},
 
     platform: 'artstation',
+    handle: {domain: /^[^.]+/},
 
-    compact: 'handle',
     icon: 'globe',
-
-    handle: {domain: /^[^.]*/},
   },
 
   {
     match: {domains: ['bc.s3m.us', 'music.solatrus.com']},
 
     platform: 'bandcamp',
+    handle: {domain: /.+/},
 
-    normal: 'domain',
-    compact: 'domain',
     icon: 'bandcamp',
   },
 
@@ -288,11 +271,9 @@ export const externalLinkSpec = [
     match: {domain: '.bandcamp.com'},
 
     platform: 'bandcamp',
+    handle: {domain: /^[^.]*/},
 
-    compact: 'handle',
     icon: 'bandcamp',
-
-    handle: {domain: /^[^.]*/},
   },
 
   {
@@ -302,22 +283,18 @@ export const externalLinkSpec = [
     },
 
     platform: 'bluesky',
+    handle: {pathname: /^profile\/([^/]+?)(?:\.bsky\.social)?\/?$/},
 
-    compact: 'handle',
     icon: 'bluesky',
-
-    handle: {pathname: /^profile\/([^/]+?)(?:\.bsky\.social)?\/?$/},
   },
 
   {
     match: {domain: '.carrd.co'},
 
     platform: 'carrd',
+    handle: {domain: /^[^.]*/},
 
-    compact: 'handle',
     icon: 'carrd',
-
-    handle: {domain: /^[^.]*/},
   },
 
   {
@@ -342,11 +319,9 @@ export const externalLinkSpec = [
     match: {domain: '.itch.io'},
 
     platform: 'itch',
+    handle: {domain: /^[^.]*/},
 
-    compact: 'handle',
     icon: 'itch',
-
-    handle: {domain: /^[^.]*/},
   },
 
   {
@@ -356,11 +331,9 @@ export const externalLinkSpec = [
     },
 
     platform: 'itch',
+    handle: {pathname: /^profile\/(.+)\/?$/},
 
-    compact: 'handle',
     icon: 'itch',
-
-    handle: {pathname: /^profile\/(.+)\/?$/}
   },
 
   {
@@ -370,11 +343,9 @@ export const externalLinkSpec = [
     },
 
     platform: 'kofi',
+    handle: {pathname: /^(.+)\/?/},
 
-    compact: 'handle',
     icon: 'kofi',
-
-    handle: {pathname: /^(.+)\/?/},
   },
 
   {
@@ -383,13 +354,10 @@ export const externalLinkSpec = [
       pathname: /^wiki\/.+\/?$/,
     },
 
-    platform: 'fandom',
-    substring: 'mspaintadventures.page',
-
-    normal: 'custom',
-    icon: 'globe',
+    platform: 'fandom.mspaintadventures',
 
-    custom: {
+    detail: {
+      substring: 'page',
       page: {
         pathname: /^wiki\/(.+)\/?$/,
         transform: [
@@ -398,13 +366,14 @@ export const externalLinkSpec = [
         ],
       },
     },
+
+    icon: 'globe',
   },
 
   {
     match: {domain: 'mspaintadventures.fandom.com'},
 
-    platform: 'fandom',
-    substring: 'mspaintadventures',
+    platform: 'fandom.mspaintadventures',
 
     icon: 'globe',
   },
@@ -437,20 +406,17 @@ export const externalLinkSpec = [
     match: {domains: ['tiktok.com']},
 
     platform: 'tiktok',
+    handle: {pathname: /^@?([a-zA-Z0-9_]*)\/?$/},
 
-    compact: 'handle',
     icon: 'tiktok',
-
-    handle: {pathname: /^@?([a-zA-Z0-9_]*)\/?$/},
   },
 
   {
     match: {domains: ['types.pl']},
 
     platform: 'mastodon',
+    handle: {domain: /.+/},
 
-    normal: 'domain',
-    compact: 'domain',
     icon: 'mastodon',
   },
 
@@ -458,9 +424,8 @@ export const externalLinkSpec = [
     match: {domain: '.neocities.org'},
 
     platform: 'neocities',
+    handle: {domain: /.+/},
 
-    normal: 'domain',
-    compact: 'domain',
     icon: 'globe',
   },
 
@@ -486,11 +451,9 @@ export const externalLinkSpec = [
     match: {domain: 'soundcloud.com'},
 
     platform: 'soundcloud',
+    handle: /([^/]*)\/?$/,
 
-    compact: 'handle',
     icon: 'soundcloud',
-
-    handle: /([^/]*)\/?$/,
   },
 
   {
@@ -503,33 +466,27 @@ export const externalLinkSpec = [
     match: {domain: '.tumblr.com'},
 
     platform: 'tumblr',
+    handle: {domain: /^[^.]*/},
 
-    compact: 'handle',
     icon: 'tumblr',
-
-    handle: {domain: /^[^.]*/},
   },
 
   {
     match: {domain: 'twitch.tv'},
 
     platform: 'twitch',
+    handle: {pathname: /^(.+)\/?/},
 
-    compact: 'handle',
     icon: 'twitch',
-
-    handle: {pathname: /^(.+)\/?/},
   },
 
   {
     match: {domain: 'twitter.com'},
 
     platform: 'twitter',
+    handle: {pathname: /^@?([a-zA-Z0-9_]*)\/?$/},
 
-    compact: 'handle',
     icon: 'twitter',
-
-    handle: {pathname: /^@?([a-zA-Z0-9_]*)\/?$/},
   },
 
   {
@@ -725,149 +682,73 @@ export function extractAllCustomPartsFromExternalLink(url, custom) {
 export function getExternalLinkStringOfStyleFromDescriptor(url, style, descriptor, {language}) {
   const prefix = 'misc.external';
 
-  function getPlatform() {
-    return language.$(prefix, descriptor.platform);
-  }
-
-  function getPlatformOrDomain() {
-    // The fallback descriptor has a "platform" which is just
-    // the word "External". This isn't really useful when you're
-    // looking for platform info! Compact mode shows the domain.
-    if (descriptor === fallbackDescriptor) {
-      return getCompactDomain();
-    } else {
-      return getPlatform();
-    }
-  }
-
-  function getDomain() {
-    return urlParts(url).domain;
-  }
-
-  function getCompactDomain() {
-    const domain = getDomain();
-
-    if (!domain) {
+  function getDetail() {
+    if (!descriptor.detail) {
       return null;
     }
 
-    return language.sanitize(domain.replace(/^www\./, ''));
-  }
-
-  function getCustom() {
-    if (!descriptor.custom) {
-      return null;
-    }
-
-    const customParts =
-      extractAllCustomPartsFromExternalLink(url, descriptor.custom);
-
-    if (!customParts) {
-      return null;
-    }
+    if (typeof descriptor.detail === 'string') {
+      return language.$(prefix, descriptor.platform, descriptor.detail);
+    } else {
+      const {substring, ...rest} = descriptor.detail;
 
-    return language.$(prefix, descriptor.platform, descriptor.substring, customParts);
-  }
+      const opts =
+        withEntries(rest, entries => entries
+          .map(([key, value]) => [
+            key,
+            extractPartFromExternalLink(url, value),
+          ]));
 
-  function getHandle() {
-    if (!descriptor.handle) {
-      return null;
+      return language.$(prefix, descriptor.platform, substring, opts);
     }
-
-    return extractPartFromExternalLink(url, descriptor.handle);
   }
 
-  function getNormal() {
-    if (descriptor.custom) {
-      if (descriptor.normal === 'custom') {
-        return getCustom();
+  switch (style) {
+    case 'platform': {
+      if (descriptor === fallbackDescriptor) {
+        // The fallback descriptor has a "platform" which is just
+        // the word "External". This isn't really useful when you're
+        // looking for platform info!
+        const domain = urlParts(url).domain;
+        if (domain) {
+          return language.sanitize(domain.replace(/^www\./, ''));
+        } else {
+          return language.$(prefix, descriptor.platform);
+        }
+      } else if (descriptor.detail) {
+        return getDetail();
       } else {
-        return null;
-      }
-    }
-
-    if (descriptor.normal === 'domain') {
-      const platform = getPlatform();
-      const domain = getDomain();
-
-      if (!platform || !domain) {
-        return null;
-      }
-
-      return language.$(prefix, 'withDomain', {platform, domain});
-    }
-
-    if (descriptor.normal === 'handle') {
-      const platform = getPlatform();
-      const handle = getHandle();
-
-      if (!platform || !handle) {
-        return null;
+        return language.$(prefix, descriptor.platform);
       }
-
-      return language.$(prefix, 'withHandle', {platform, handle});
     }
 
-    return language.$(prefix, descriptor.platform, descriptor.substring);
-  }
-
-  function getCompact() {
-    if (descriptor.custom) {
-      if (descriptor.compact === 'custom') {
-        return getCustom();
+    case 'handle': {
+      if (descriptor.handle) {
+        return extractPartFromExternalLink(url, descriptor.handle);
       } else {
         return null;
       }
     }
 
-    if (descriptor.compact === 'domain') {
-      return getCompactDomain();
-    }
-
-    if (descriptor.compact === 'handle') {
-      const handle = getHandle();
-
-      if (!handle) {
+    case 'icon-id': {
+      if (descriptor.icon) {
+        return descriptor.icon;
+      } else {
         return null;
       }
-
-      return language.sanitize(handle);
     }
   }
-
-  function getIconId() {
-    return descriptor.icon ?? null;
-  }
-
-  switch (style) {
-    case 'normal': return getNormal();
-    case 'compact': return getCompact();
-    case 'platform': return getPlatformOrDomain();
-    case 'icon-id': return getIconId();
-  }
 }
 
 export function couldDescriptorSupportStyle(descriptor, style) {
-  if (style === 'normal') {
-    if (descriptor.custom) {
-      return descriptor.normal === 'custom';
-    } else {
-      return true;
-    }
-  }
-
-  if (style === 'compact') {
-    if (descriptor.custom) {
-      return descriptor.compact === 'custom';
-    } else {
-      return !!descriptor.compact;
-    }
-  }
-
   if (style === 'platform') {
     return true;
   }
 
+  if (style === 'handle') {
+    return !!descriptor.handle;
+  }
+
   if (style === 'icon-id') {
     return !!descriptor.icon;
   }