« 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/util
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/util
parentaaeffb2fe2d2e169cf92da5142037214ac341219 (diff)
content, external-links: [normal, compact] -> [platform, handle]
Diffstat (limited to 'src/util')
-rw-r--r--src/util/external-links.js295
1 files changed, 88 insertions, 207 deletions
diff --git a/src/util/external-links.js b/src/util/external-links.js
index 877ef8d..585e28a 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;
   }