« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js141
-rw-r--r--src/content/dependencies/generateContentEntry.js8
-rw-r--r--src/content/dependencies/generateContentEntryDate.js2
-rw-r--r--src/content/dependencies/generateLyricsEntry.js4
-rw-r--r--src/data/checks.js7
-rw-r--r--src/data/composite.js32
-rw-r--r--src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js29
-rw-r--r--src/data/things/content/ContentEntry.js53
-rw-r--r--src/static/css/search.css2
-rw-r--r--src/strings-default.yaml4
10 files changed, 186 insertions, 96 deletions
diff --git a/src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js b/src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js
index 572eb982..6b603375 100644
--- a/src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js
+++ b/src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js
@@ -30,6 +30,10 @@ export default {
         flashAct,
         flash,
 
+        quoted:
+         !entry.headingArtists.includes(artist) &&
+          entry.quotedArtists.includes(artist),
+
         annotation: entry.annotation,
         annotationParts: entry.annotationParts,
       },
@@ -191,6 +195,11 @@ export default {
       query.chunks
         .map(({chunk}) => chunk
           .map(({itemType}) => itemType)),
+
+    itemQuoted:
+      query.chunks
+        .map(({chunk}) => chunk
+          .map(({quoted}) => quoted)),
   }),
 
   generate: (data, relations, {html, language}) =>
@@ -206,6 +215,7 @@ export default {
         itemLinks: relations.itemLinks,
         itemAnnotations: relations.itemAnnotations,
         itemTypes: data.itemTypes,
+        itemQuoted: data.itemQuoted,
       }).map(({
           chunk,
           chunkLink,
@@ -215,64 +225,77 @@ export default {
           itemLinks,
           itemAnnotations,
           itemTypes,
+          itemQuoted,
         }) =>
-          language.encapsulate('artistPage.creditList.entry', capsule =>
-            (chunkType === 'album'
-              ? chunk.slots({
-                  mode: 'album',
-                  link: chunkLink,
-
-                  list:
-                    html.tag('ul',
-                      stitchArrays({
-                        item: items,
-                        link: itemLinks,
-                        annotation: itemAnnotations,
-                        type: itemTypes,
-                      }).map(({item, link, annotation, type}) =>
-                        item.slots({
-                          // The citation slot, instead of annotation, gives commentary
-                          // a specially custom look.
-                          citation:
-                            annotation.slots({
-                              mode: 'inline',
-                              absorbPunctuationFollowingExternalLinks: false,
-                            }),
-
-                          content:
-                            (type === 'album'
-                              ? html.tag('i',
-                                  language.$(capsule, 'album.commentary'))
-                              : language.$(capsule, 'track', {track: link})),
-                        }))),
-                })
-
-             : chunkType === 'flash-act'
-              ? chunk.slots({
-                  mode: 'flash',
-                  link: chunkLink,
-
-                  list:
-                    html.tag('ul',
-                      stitchArrays({
-                        item: items,
-                        link: itemLinks,
-                        annotation: itemAnnotations,
-                      }).map(({item, link, annotation}) =>
-                        item.slots({
-                          annotation:
-                            (annotation
-                              ? annotation.slots({
-                                  mode: 'inline',
-                                  absorbPunctuationFollowingExternalLinks: false,
-                                })
-                              : null),
-
-                          content:
-                            language.$(capsule, 'flash', {
-                              flash: link,
-                            }),
-                        }))),
-                })
-              : null)))),
+          language.encapsulate('artistPage.creditList.entry', capsule => {
+            // The citation slot, instead of annotation, gives commentary
+            // a specially custom look.
+            const citations =
+              stitchArrays({annotation: itemAnnotations, quoted: itemQuoted})
+                .map(({annotation, quoted}) =>
+                  language.encapsulate(capsule, workingCapsule => {
+                    const workingOptions = {};
+
+                    let any = false;
+
+                    annotation.setSlots({
+                      mode: 'inline',
+                      absorbPunctuationFollowingExternalLinks: false,
+                    });
+
+                    if (!html.isBlank(annotation)) {
+                      workingCapsule += '.citation';
+                      workingOptions.citation = annotation;
+                      any = true;
+                    }
+
+                    if (quoted) {
+                      workingCapsule += '.quoted';
+                      any = true;
+                    }
+
+                    if (any) {
+                      return language.$(workingCapsule, workingOptions);
+                    } else {
+                      return html.blank();
+                    }
+                  }));
+
+            let contents;
+
+            if (chunkType === 'album') {
+              chunk.setSlot('mode', 'album');
+              contents =
+                stitchArrays({link: itemLinks, type: itemTypes})
+                  .map(({link, type}) =>
+                    (type === 'album'
+                      ? html.tag('i',
+                          language.$(capsule, 'album.commentary'))
+                      : language.$(capsule, 'track', {track: link})));
+
+            } else if (chunkType === 'flash-act') {
+              chunk.setSlot('mode', 'flash');
+              contents =
+                itemLinks.map(link =>
+                  language.$(capsule, 'flash', {flash: link}));
+
+            } else {
+              throw new Error(`Gyeep!!`);
+            }
+
+            chunk.setSlots({
+              link: chunkLink,
+
+              list:
+                html.tag('ul',
+                  stitchArrays({
+                    item: items,
+                    citation: citations,
+                    content: contents,
+                  }).map(({item, citation, content}) =>
+                      item.slots({citation, content}))),
+            });
+
+            return chunk;
+          }))),
 };
diff --git a/src/content/dependencies/generateContentEntry.js b/src/content/dependencies/generateContentEntry.js
index c77f744a..40c637a3 100644
--- a/src/content/dependencies/generateContentEntry.js
+++ b/src/content/dependencies/generateContentEntry.js
@@ -3,14 +3,14 @@ import {empty} from '#sugar';
 export default {
   relations: (relation, entry) => ({
     artistLinks:
-      (!empty(entry.artists) && !entry.artistText
-        ? entry.artists
+      (!empty(entry.headingArtists) && !entry.headingArtistText
+        ? entry.headingArtists
             .map(artist => relation('linkArtist', artist))
         : null),
 
     artistsContent:
-      (entry.artistText
-        ? relation('transformContent', entry.artistText)
+      (entry.headingArtistText
+        ? relation('transformContent', entry.headingArtistText)
         : null),
 
     annotationContent:
diff --git a/src/content/dependencies/generateContentEntryDate.js b/src/content/dependencies/generateContentEntryDate.js
index 845cb5ed..5255c7ea 100644
--- a/src/content/dependencies/generateContentEntryDate.js
+++ b/src/content/dependencies/generateContentEntryDate.js
@@ -35,7 +35,7 @@ export default {
 
      : entry.thing.isTrack &&
        entry.thing.date === entry.thing.album.date &&
-       entry.thing.style === 'single'
+       entry.thing.album.style === 'single'
         ? 'single'
 
      : entry.thing.isTrack &&
diff --git a/src/content/dependencies/generateLyricsEntry.js b/src/content/dependencies/generateLyricsEntry.js
index 15f84b27..0ecf319f 100644
--- a/src/content/dependencies/generateLyricsEntry.js
+++ b/src/content/dependencies/generateLyricsEntry.js
@@ -4,10 +4,10 @@ export default {
       relation('transformContent', entry.body),
 
     artistText:
-      relation('transformContent', entry.artistText),
+      relation('transformContent', entry.headingArtistText),
 
     artistLinks:
-      entry.artists
+      entry.headingArtists
         .filter(artist => artist.name !== 'HSMusic Wiki') // smh
         .map(artist => relation('linkArtist', artist)),
 
diff --git a/src/data/checks.js b/src/data/checks.js
index 0a0e7f52..01b5cf9e 100644
--- a/src/data/checks.js
+++ b/src/data/checks.js
@@ -352,6 +352,9 @@ export function filterReferenceErrors(wikiData, {
 
             switch (findFnKey) {
               case '_content':
+                // note: quoted artists ("Quoted Artists" field)`) currently
+                // not handled here at all. limitation of current code! oops!
+
                 if (value) {
                   value =
                     value.map(entry =>
@@ -721,13 +724,13 @@ export function reportContentTextErrors(wikiData, {
 
   const commentaryShape = {
     body: 'commentary body',
-    artistText: 'commentary artist text',
+    headingArtistText: 'commentary artist text',
     annotation: 'commentary annotation',
   };
 
   const lyricsShape = {
     body: 'lyrics body',
-    artistText: 'lyrics artist text',
+    headingArtistText: 'lyrics artist text',
     annotation: 'lyrics annotation',
   };
 
diff --git a/src/data/composite.js b/src/data/composite.js
index 8ac906c7..3b462ef5 100644
--- a/src/data/composite.js
+++ b/src/data/composite.js
@@ -10,17 +10,27 @@ import {TupleMap} from '#wiki-data';
 
 const globalCompositeCache = {};
 
-const _valueIntoToken = shape =>
-  (value = null) =>
-    (value === null
-      ? Symbol.for(`hsmusic.composite.${shape}`)
-   : typeof value === 'string'
-      ? Symbol.for(`hsmusic.composite.${shape}:${value}`)
-      : {
-          symbol: Symbol.for(`hsmusic.composite.${shape.split('.')[0]}`),
-          shape,
-          value,
-        });
+const _valueIntoToken = shape => (value = null) => {
+  if (value === null) {
+    return Symbol.for(`hsmusic.composite.${shape}`);
+  }
+
+  if (typeof value === 'string') {
+    return Symbol.for(`hsmusic.composite.${shape}:${value}`);
+  }
+
+  if (typeof value === 'object') {
+    if (Object.values(value).some(isInputToken)) {
+      throw new TypeError(`Don't nest input tokens inside ${shape}()`);
+    }
+  }
+
+  return {
+    symbol: Symbol.for(`hsmusic.composite.${shape.split('.')[0]}`),
+    shape,
+    value,
+  };
+};
 
 export const input = _valueIntoToken('input');
 input.symbol = Symbol.for('hsmusic.composite.input');
diff --git a/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js b/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
index 69da8c75..a6200ee8 100644
--- a/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
+++ b/src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js
@@ -8,16 +8,19 @@ export default templateCompositeFrom({
   annotation: `withExpressedOrImplicitArtistReferences`,
 
   inputs: {
-    from: input({type: 'array', acceptsNull: true}),
+    fromExpressed: input({type: 'array', acceptsNull: true}),
+    fromContent: input({type: 'string', acceptsNull: true}),
+
+    filterArtistTags: input({type: 'function', defaultValue: () => true}),
   },
 
   outputs: ['#artistReferences'],
 
   steps: () => [
     {
-      dependencies: [input('from')],
+      dependencies: [input('fromExpressed')],
       compute: (continuation, {
-        [input('from')]: expressedArtistReferences,
+        [input('fromExpressed')]: expressedArtistReferences,
       }) =>
         (expressedArtistReferences
           ? continuation.raiseOutput({'#artistReferences': expressedArtistReferences})
@@ -25,12 +28,12 @@ export default templateCompositeFrom({
     },
 
     raiseOutputWithoutDependency({
-      dependency: 'artistText',
-      output: input.value({'#artistReferences': null}),
+      dependency: input('fromContent'),
+      output: input.value({'#artistReferences': []}),
     }),
 
     withContentNodes({
-      from: 'artistText',
+      from: input('fromContent'),
     }),
 
     withMappedList({
@@ -42,13 +45,19 @@ export default templateCompositeFrom({
       '#mappedList': '#artistTagFilter',
     }),
 
-    withFilteredList({
-      list: '#contentNodes',
-      filter: '#artistTagFilter',
+    withFilteredList('#contentNodes', '#artistTagFilter')
+      .outputs({'#filteredList': '#artistTags'}),
+
+    withMappedList({
+      list: '#artistTags',
+      map: input('filterArtistTags'),
     }).outputs({
-      '#filteredList': '#artistTags',
+      '#mappedList': '#customFilter',
     }),
 
+    withFilteredList({list: '#artistTags', filter: '#customFilter'})
+      .outputs({'#filteredList': '#artistTags'}),
+
     withMappedList({
       list: '#artistTags',
       map: input.value(node =>
diff --git a/src/data/things/content/ContentEntry.js b/src/data/things/content/ContentEntry.js
index 04df303f..47f86622 100644
--- a/src/data/things/content/ContentEntry.js
+++ b/src/data/things/content/ContentEntry.js
@@ -1,5 +1,5 @@
 import {input, V} from '#composite';
-import {transposeArrays} from '#sugar';
+import {transposeArrays, unique} from '#sugar';
 import Thing from '#thing';
 import {is, isDate, validateReferenceList} from '#validators';
 import {parseDate} from '#yaml';
@@ -33,14 +33,43 @@ export class ContentEntry extends Thing {
 
     thing: thing(),
 
-    artists: [
+    headingArtists: [
       withExpressedOrImplicitArtistReferences({
-        from: input.updateValue({
+        fromExpressed: input.updateValue({
           validate: validateReferenceList('artist'),
         }),
+
+        fromContent: 'headingArtistText',
+      }),
+
+      withResolvedReferenceList({
+        list: '#artistReferences',
+        find: soupyFind.input('artist'),
       }),
 
-      exitWithoutDependency('#artistReferences', V([])),
+      exposeDependency('#resolvedReferenceList'),
+    ],
+
+    quotedArtists: [
+      exitWithoutDependency('body', V([])),
+
+      {
+        dependencies: ['body'],
+        compute: (continuation, {body}) => continuation({
+          ['#filterArtistTags']: node =>
+            /(\n|^)> <i>$/.test(body.slice(0, node.i)) &&
+            /^:<\/i>/.test(body.slice(node.iEnd)),
+        }),
+      },
+
+      withExpressedOrImplicitArtistReferences({
+        fromExpressed: input.updateValue({
+          validate: validateReferenceList('artist'),
+        }),
+
+        fromContent: 'body',
+        filterArtistTags: '#filterArtistTags',
+      }),
 
       withResolvedReferenceList({
         list: '#artistReferences',
@@ -50,7 +79,7 @@ export class ContentEntry extends Thing {
       exposeDependency('#resolvedReferenceList'),
     ],
 
-    artistText: contentString(),
+    headingArtistText: contentString(),
 
     annotation: contentString(),
 
@@ -119,6 +148,14 @@ export class ContentEntry extends Thing {
 
     isContentEntry: exposeConstant(V(true)),
 
+    artists: [
+      {
+        dependencies: ['headingArtists', 'quotedArtists'],
+        compute: ({headingArtists, quotedArtists}) =>
+          unique([...headingArtists, ...quotedArtists]),
+      },
+    ],
+
     annotationParts: [
       withAnnotationPartNodeLists(),
 
@@ -230,8 +267,10 @@ export class ContentEntry extends Thing {
 
   static [Thing.yamlDocumentSpec] = {
     fields: {
-      'Artists': {property: 'artists'},
-      'Artist Text': {property: 'artistText'},
+      'Artists': {property: 'headingArtists'},
+      'Artist Text': {property: 'headingArtistText'},
+
+      'Quoted Artists': {property: 'quotedArtists'},
 
       'Annotation': {property: 'annotation'},
 
diff --git a/src/static/css/search.css b/src/static/css/search.css
index f421803b..a79fb20a 100644
--- a/src/static/css/search.css
+++ b/src/static/css/search.css
@@ -191,6 +191,8 @@
 @layer layout {
   .wiki-search-context-container {
     padding: 2px 12px 4px;
+    padding-left: calc(12px + 1.2ch);
+    text-indent: -1.2ch;
   }
 }
 
diff --git a/src/strings-default.yaml b/src/strings-default.yaml
index 54741239..bade35ac 100644
--- a/src/strings-default.yaml
+++ b/src/strings-default.yaml
@@ -1395,6 +1395,10 @@ artistPage:
 
       withCitation: "{ENTRY} ({CITATION})"
 
+      citation: "{CITATION}"
+      citation.quoted: "quoted: {CITATION}"
+      quoted: "quoted"
+
       # rerelease:
       #   Tracks which aren't the original release don't display co-
       #   artists or contributors, and get dimmed a little compared