« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data/things
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things')
-rw-r--r--src/data/things/art-tag.js66
-rw-r--r--src/data/things/language.js67
2 files changed, 108 insertions, 25 deletions
diff --git a/src/data/things/art-tag.js b/src/data/things/art-tag.js
index 1266a4e0..a530ba8c 100644
--- a/src/data/things/art-tag.js
+++ b/src/data/things/art-tag.js
@@ -1,17 +1,27 @@
 import {input} from '#composite';
-import {sortAlbumsTracksChronologically} from '#wiki-data';
+import find from '#find';
+import {unique} from '#sugar';
 import {isName} from '#validators';
+import {sortAlbumsTracksChronologically} from '#wiki-data';
 
-import {exposeUpdateValueOrContinue} from '#composite/control-flow';
+import {exitWithoutDependency, exposeDependency, exposeUpdateValueOrContinue}
+  from '#composite/control-flow';
 
 import {
   color,
   directory,
   flag,
+  referenceList,
+  reverseReferenceList,
+  simpleString,
   name,
+  urls,
   wikiData,
 } from '#composite/wiki-properties';
 
+import {withAllDescendantArtTags, withAncestorArtTagBaobabTree}
+  from '#composite/things/art-tag';
+
 import Thing from './thing.js';
 
 export class ArtTag extends Thing {
@@ -24,6 +34,7 @@ export class ArtTag extends Thing {
     directory: directory(),
     color: color(),
     isContentWarning: flag(false),
+    extraReadingURLs: urls(),
 
     nameShort: [
       exposeUpdateValueOrContinue({
@@ -37,14 +48,36 @@ export class ArtTag extends Thing {
       },
     ],
 
+    description: simpleString(),
+
+    directDescendantArtTags: referenceList({
+      class: input.value(ArtTag),
+      find: input.value(find.artTag),
+      data: 'artTagData',
+    }),
+
     // Update only
 
     albumData: wikiData(Album),
+    artTagData: wikiData(ArtTag),
     trackData: wikiData(Track),
 
     // Expose only
 
-    taggedInThings: {
+    descriptionShort: [
+      exitWithoutDependency({
+        dependency: 'description',
+        mode: input.value('falsy'),
+      }),
+
+      {
+        dependencies: ['description'],
+        compute: ({description}) =>
+          description.split('<hr class="split">')[0],
+      },
+    ],
+
+    directlyTaggedInThings: {
       flags: {expose: true},
 
       expose: {
@@ -56,5 +89,32 @@ export class ArtTag extends Thing {
             {getDate: o => o.coverArtDate}),
       },
     },
+
+    indirectlyTaggedInThings: [
+      withAllDescendantArtTags(),
+
+      {
+        dependencies: ['#allDescendantArtTags'],
+        compute: ({'#allDescendantArtTags': allDescendantArtTags}) =>
+          unique(
+            allDescendantArtTags
+              .flatMap(artTag => artTag.directlyTaggedInThings)),
+      },
+    ],
+
+    allDescendantArtTags: [
+      withAllDescendantArtTags(),
+      exposeDependency({dependency: '#allDescendantArtTags'}),
+    ],
+
+    directAncestorArtTags: reverseReferenceList({
+      data: 'artTagData',
+      list: input.value('directDescendantArtTags'),
+    }),
+
+    ancestorArtTagBaobabTree: [
+      withAncestorArtTagBaobabTree(),
+      exposeDependency({dependency: '#ancestorArtTagBaobabTree'}),
+    ],
   });
 }
diff --git a/src/data/things/language.js b/src/data/things/language.js
index fe74f7bf..f19a2a1a 100644
--- a/src/data/things/language.js
+++ b/src/data/things/language.js
@@ -101,6 +101,7 @@ export class Language extends Thing {
       },
     },
 
+    // TODO: This currently isn't used. Is it still needed?
     strings_htmlEscaped: {
       flags: {expose: true},
       expose: {
@@ -130,8 +131,8 @@ export class Language extends Thing {
     };
   }
 
-  $(key, args = {}) {
-    return this.formatString(key, args);
+  $(...args) {
+    return this.formatString(...args);
   }
 
   assertIntlAvailable(property) {
@@ -145,8 +146,20 @@ export class Language extends Thing {
     return this.intl_pluralCardinal.select(value);
   }
 
-  formatString(key, args = {}) {
-    const strings = this.strings_htmlEscaped;
+  formatString(...args) {
+    const hasOptions =
+      typeof args.at(-1) === 'object' &&
+      args.at(-1) !== null;
+
+    const key =
+      (hasOptions ? args.slice(0, -1) : args)
+        .filter(Boolean)
+        .join('.');
+
+    const options =
+      (hasOptions
+        ? args.at(-1)
+        : null);
 
     if (!this.strings) {
       throw new Error(`Strings unavailable`);
@@ -158,27 +171,36 @@ export class Language extends Thing {
 
     const template = this.strings[key];
 
-    // Convert the keys on the args dict from camelCase to CONSTANT_CASE.
-    // (This isn't an OUTRAGEOUSLY versatile algorithm for doing that, 8ut
-    // like, who cares, dude?) Also, this is an array, 8ecause it's handy
-    // for the iterating we're a8out to do. Also strip HTML from arguments
-    // that are literal strings - real HTML content should always be proper
-    // HTML objects (see html.js).
-    const processedArgs =
-      Object.entries(args).map(([k, v]) => [
-        k.replace(/[A-Z]/g, '_$&').toUpperCase(),
-        this.#sanitizeStringArg(v),
-      ]);
-
-    // Replacement time! Woot. Reduce comes in handy here!
-    const output =
-      processedArgs.reduce(
-        (x, [k, v]) => x.replaceAll(`{${k}}`, v),
-        template);
+    let output;
+
+    if (hasOptions) {
+      // Convert the keys on the options dict from camelCase to CONSTANT_CASE.
+      // (This isn't an OUTRAGEOUSLY versatile algorithm for doing that, 8ut
+      // like, who cares, dude?) Also, this is an array, 8ecause it's handy
+      // for the iterating we're a8out to do. Also strip HTML from arguments
+      // that are literal strings - real HTML content should always be proper
+      // HTML objects (see html.js).
+      const processedOptions =
+        Object.entries(options).map(([k, v]) => [
+          k.replace(/[A-Z]/g, '_$&').toUpperCase(),
+          this.#sanitizeStringArg(v),
+        ]);
+
+      // Replacement time! Woot. Reduce comes in handy here!
+      output =
+        processedOptions.reduce(
+          (x, [k, v]) => x.replaceAll(`{${k}}`, v),
+          template);
+    } else {
+      // Without any options provided, just use the template as-is. This will
+      // still error if the template expected arguments, and otherwise will be
+      // the right value.
+      output = template;
+    }
 
     // Post-processing: if any expected arguments *weren't* replaced, that
     // is almost definitely an error.
-    if (output.match(/\{[A-Z_]+\}/)) {
+    if (output.match(/\{[A-Z][A-Z0-9_]*\}/)) {
       throw new Error(`Args in ${key} were missing - output: ${output}`);
     }
 
@@ -376,6 +398,7 @@ Object.assign(Language.prototype, {
   countAdditionalFiles: countHelper('additionalFiles', 'files'),
   countAlbums: countHelper('albums'),
   countArtworks: countHelper('artworks'),
+  countArtTags: countHelper('artTags', 'tags'),
   countFlashes: countHelper('flashes'),
   countCommentaryEntries: countHelper('commentaryEntries', 'entries'),
   countContributions: countHelper('contributions'),