16 Revize 226c316f21 ... e6aa957cc3

Autor SHA1 Zpráva Datum
  (quasar) nebula e6aa957cc3 data: raiseResolvedReferenceList: remove raiseOutputAbove misref před 3 měsíci
  (quasar) nebula 1b38561fa4 content: generate{Artist,Group}InfoPage: alias links před 3 měsíci
  (quasar) nebula 0d74914e3c data, content: closely linked artist annotations před 3 měsíci
  (quasar) nebula 6bcbe4f348 data: reverseAnnotatedReferenceList před 3 měsíci
  (quasar) nebula 89cf67d434 data: withResolvedAnnotatedReferenceList: availability checks před 3 měsíci
  (quasar) nebula 3528fb2088 data: withStretchedList před 3 měsíci
  (quasar) nebula 618004adc3 data: withReverseContributionList: use withPropertyFromList před 3 měsíci
  (quasar) nebula 35137cbe06 data: annotatedReferenceList, reference-list-helpers před 3 měsíci
  (quasar) nebula d28e046974 data: withResolvedArtworkReferenceList -> ...Annotated před 3 měsíci
  (quasar) nebula 65ff74446f data: raiseResolvedReferenceList před 3 měsíci
  (quasar) nebula f2464c6031 data: withAvailabilityFilter před 3 měsíci
  (quasar) nebula 6df70e89d2 data: factor out performAvailabilityCheck před 3 měsíci
  (quasar) nebula 98446f3f30 data: factor out inputNotFoundMode před 3 měsíci
  (quasar) nebula aa800e588c content: generateGroupInfoPage: wiki-color close artist links před 3 měsíci
  (quasar) nebula 0337711ed7 content: generate{Artist,Group}InfoPage: close group/artist links před 3 měsíci
  (quasar) nebula a618ec62bb data: Closely Linked Artists před 3 měsíci

+ 69 - 1
src/content/dependencies/generateArtistInfoPage.js

@@ -1,4 +1,4 @@
-import {empty, unique} from '#sugar';
+import {empty, stitchArrays, unique} from '#sugar';
 
 export default {
   contentDependencies: [
@@ -13,6 +13,7 @@ export default {
     'generatePageLayout',
     'linkArtistGallery',
     'linkExternal',
+    'linkGroup',
     'transformContent',
   ],
 
@@ -48,6 +49,16 @@ export default {
     hasGallery:
       !empty(artist.albumCoverArtistContributions) ||
       !empty(artist.trackCoverArtistContributions),
+
+    aliasLinkedGroups:
+      artist.closelyLinkedGroups
+        .filter(({annotation}) =>
+          annotation === 'alias'),
+
+    generalLinkedGroups:
+      artist.closelyLinkedGroups
+        .filter(({annotation}) =>
+          annotation !== 'alias'),
   }),
 
   relations: (relation, query, artist) => ({
@@ -68,6 +79,16 @@ export default {
     contextNotes:
       relation('transformContent', artist.contextNotes),
 
+    closeGroupLinks:
+      query.generalLinkedGroups
+        .map(({thing: group}) =>
+          relation('linkGroup', group)),
+
+    aliasGroupLinks:
+      query.aliasLinkedGroups
+        .map(({thing: group}) =>
+          relation('linkGroup', group)),
+
     visitLinks:
       artist.urls
         .map(url => relation('linkExternal', url)),
@@ -111,6 +132,10 @@ export default {
         ? artist.avatarFileExtension
         : null),
 
+    closeGroupAnnotations:
+      query.generalLinkedGroups
+        .map(({annotation}) => annotation),
+
     totalTrackCount:
       query.allTracks.length,
 
@@ -148,6 +173,49 @@ export default {
 
           html.tag('p',
             {[html.onlyIfContent]: true},
+            {[html.joinChildren]: html.tag('br')},
+
+            language.encapsulate(pageCapsule, 'closelyLinkedGroups', capsule => [
+              language.encapsulate(capsule, capsule => {
+                const [workingCapsule, option] =
+                  (relations.closeGroupLinks.length === 0
+                    ? [null, null]
+                 : relations.closeGroupLinks.length === 1
+                    ? [language.encapsulate(capsule, 'one'), 'group']
+                    : [language.encapsulate(capsule, 'multiple'), 'groups']);
+
+                if (!workingCapsule) return html.blank();
+
+                return language.$(workingCapsule, {
+                  [option]:
+                    language.formatUnitList(
+                      stitchArrays({
+                        link: relations.closeGroupLinks,
+                        annotation: data.closeGroupAnnotations,
+                      }).map(({link, annotation}) =>
+                          language.encapsulate(capsule, 'group', workingCapsule => {
+                            const workingOptions = {group: link};
+
+                            if (annotation) {
+                              workingCapsule += '.withAnnotation';
+                              workingOptions.annotation = annotation;
+                            }
+
+                            return language.$(workingCapsule, workingOptions);
+                          }))),
+                });
+              }),
+
+              language.$(capsule, 'alias', {
+                [language.onlyIfOptions]: ['groups'],
+
+                groups:
+                  language.formatConjunctionList(relations.aliasGroupLinks),
+              }),
+            ])),
+
+          html.tag('p',
+            {[html.onlyIfContent]: true},
 
             language.$('releaseInfo.visitOn', {
               [language.onlyIfOptions]: ['links'],

+ 90 - 2
src/content/dependencies/generateGroupInfoPage.js

@@ -1,10 +1,14 @@
+import {stitchArrays} from '#sugar';
+
 export default {
   contentDependencies: [
+    'generateColorStyleAttribute',
     'generateGroupInfoPageAlbumsSection',
     'generateGroupNavLinks',
     'generateGroupSecondaryNav',
     'generateGroupSidebar',
     'generatePageLayout',
+    'linkArtist',
     'linkExternal',
     'transformContent',
   ],
@@ -14,9 +18,24 @@ export default {
   sprawl: ({wikiInfo}) => ({
     enableGroupUI:
       wikiInfo.enableGroupUI,
+
+    wikiColor:
+      wikiInfo.color,
+  }),
+
+  query: (_sprawl, group) => ({
+    aliasLinkedArtists:
+      group.closelyLinkedArtists
+        .filter(({annotation}) =>
+          annotation === 'alias'),
+
+    generalLinkedArtists:
+      group.closelyLinkedArtists
+        .filter(({annotation}) =>
+          annotation !== 'alias'),
   }),
 
-  relations: (relation, sprawl, group) => ({
+  relations: (relation, query, sprawl, group) => ({
     layout:
       relation('generatePageLayout'),
 
@@ -33,6 +52,19 @@ export default {
         ? relation('generateGroupSidebar', group)
         : null),
 
+    wikiColorAttribute:
+      relation('generateColorStyleAttribute', sprawl.wikiColor),
+
+    closeArtistLinks:
+      query.generalLinkedArtists
+        .map(({thing: artist}) =>
+          relation('linkArtist', artist)),
+
+    aliasArtistLinks:
+      query.aliasLinkedArtists
+        .map(({thing: artist}) =>
+          relation('linkArtist', artist)),
+
     visitLinks:
       group.urls
         .map(url => relation('linkExternal', url)),
@@ -44,12 +76,16 @@ export default {
       relation('generateGroupInfoPageAlbumsSection', group),
   }),
 
-  data: (_sprawl, group) => ({
+  data: (query, _sprawl, group) => ({
     name:
       group.name,
 
     color:
       group.color,
+
+    closeArtistAnnotations:
+      query.generalLinkedArtists
+        .map(({annotation}) => annotation),
   }),
 
   generate: (data, relations, {html, language}) =>
@@ -62,6 +98,58 @@ export default {
         mainContent: [
           html.tag('p',
             {[html.onlyIfContent]: true},
+            {[html.joinChildren]: html.tag('br')},
+
+            language.encapsulate(pageCapsule, 'closelyLinkedArtists', capsule => [
+              language.encapsulate(capsule, capsule => {
+                const [workingCapsule, option] =
+                  (relations.closeArtistLinks.length === 0
+                    ? [null, null]
+                 : relations.closeArtistLinks.length === 1
+                    ? [language.encapsulate(capsule, 'one'), 'artist']
+                    : [language.encapsulate(capsule, 'multiple'), 'artists']);
+
+                if (!workingCapsule) return html.blank();
+
+                return language.$(workingCapsule, {
+                  [option]:
+                    language.formatUnitList(
+                      stitchArrays({
+                        link: relations.closeArtistLinks,
+                        annotation: data.closeArtistAnnotations,
+                      }).map(({link, annotation}) =>
+                          language.encapsulate(capsule, 'artist', workingCapsule => {
+                            const workingOptions = {};
+
+                            workingOptions.artist =
+                              link.slots({
+                                attributes: [relations.wikiColorAttribute],
+                              });
+
+                            if (annotation) {
+                              workingCapsule += '.withAnnotation';
+                              workingOptions.annotation = annotation;
+                            }
+
+                            return language.$(workingCapsule, workingOptions);
+                          }))),
+                });
+              }),
+
+              language.$(capsule, 'aliases', {
+                [language.onlyIfOptions]: ['aliases'],
+
+                aliases:
+                  language.formatConjunctionList(
+                    relations.aliasArtistLinks.map(link =>
+                      link.slots({
+                        attributes: [relations.wikiColorAttribute],
+                      }))),
+              }),
+            ])),
+
+          html.tag('p',
+            {[html.onlyIfContent]: true},
 
             language.$('releaseInfo.visitOn', {
               [language.onlyIfOptions]: ['links'],

+ 19 - 0
src/data/composite/control-flow/helpers/performAvailabilityCheck.js

@@ -0,0 +1,19 @@
+import {empty} from '#sugar';
+
+export default function performAvailabilityCheck(value, mode) {
+  switch (mode) {
+    case 'null':
+      return value !== undefined && value !== null;
+
+    case 'empty':
+      return value !== undefined && !empty(value);
+
+    case 'falsy':
+      return !!value && (!Array.isArray(value) || !empty(value));
+
+    case 'index':
+      return typeof value === 'number' && value >= 0;
+  }
+
+  return undefined;
+}

+ 1 - 0
src/data/composite/control-flow/index.js

@@ -12,4 +12,5 @@ export {default as exposeUpdateValueOrContinue} from './exposeUpdateValueOrConti
 export {default as exposeWhetherDependencyAvailable} from './exposeWhetherDependencyAvailable.js';
 export {default as raiseOutputWithoutDependency} from './raiseOutputWithoutDependency.js';
 export {default as raiseOutputWithoutUpdateValue} from './raiseOutputWithoutUpdateValue.js';
+export {default as withAvailabilityFilter} from './withAvailabilityFilter.js';
 export {default as withResultOfAvailabilityCheck} from './withResultOfAvailabilityCheck.js';

+ 40 - 0
src/data/composite/control-flow/withAvailabilityFilter.js

@@ -0,0 +1,40 @@
+// Performs the same availability check across all items of a list, providing
+// a list that's suitable anywhere a filter is expected.
+//
+// Accepts the same mode options as withResultOfAvailabilityCheck.
+//
+// See also:
+//  - withFilteredList
+//  - withResultOfAvailabilityCheck
+//
+
+import {input, templateCompositeFrom} from '#composite';
+
+import inputAvailabilityCheckMode from './inputAvailabilityCheckMode.js';
+
+import performAvailabilityCheck from './helpers/performAvailabilityCheck.js';
+
+export default templateCompositeFrom({
+  annotation: `withAvailabilityFilter`,
+
+  inputs: {
+    from: input({type: 'array'}),
+    mode: inputAvailabilityCheckMode(),
+  },
+
+  outputs: ['#availabilityFilter'],
+
+  steps: () => [
+    {
+      dependencies: [input('from'), input('mode')],
+      compute: (continuation, {
+        [input('from')]: list,
+        [input('mode')]: mode,
+      }) => continuation({
+        ['#availabilityFilter']:
+          list.map(value =>
+            performAvailabilityCheck(value, mode)),
+      }),
+    },
+  ],
+});

+ 7 - 25
src/data/composite/control-flow/withResultOfAvailabilityCheck.js

@@ -20,13 +20,15 @@
 //  - exposeWhetherDependencyAvailable
 //  - raiseOutputWithoutDependency
 //  - raiseOutputWithoutUpdateValue
+//  - withAvailabilityFilter
 //
 
 import {input, templateCompositeFrom} from '#composite';
-import {empty} from '#sugar';
 
 import inputAvailabilityCheckMode from './inputAvailabilityCheckMode.js';
 
+import performAvailabilityCheck from './helpers/performAvailabilityCheck.js';
+
 export default templateCompositeFrom({
   annotation: `withResultOfAvailabilityCheck`,
 
@@ -40,33 +42,13 @@ export default templateCompositeFrom({
   steps: () => [
     {
       dependencies: [input('from'), input('mode')],
-
       compute: (continuation, {
         [input('from')]: value,
         [input('mode')]: mode,
-      }) => {
-        let availability;
-
-        switch (mode) {
-          case 'null':
-            availability = value !== undefined && value !== null;
-            break;
-
-          case 'empty':
-            availability = value !== undefined && !empty(value);
-            break;
-
-          case 'falsy':
-            availability = !!value && (!Array.isArray(value) || !empty(value));
-            break;
-
-          case 'index':
-            availability = typeof value === 'number' && value >= 0;
-            break;
-        }
-
-        return continuation({'#availability': availability});
-      },
+      }) => continuation({
+        ['#availability']:
+          performAvailabilityCheck(value, mode),
+      }),
     },
   ],
 });

+ 1 - 0
src/data/composite/data/index.js

@@ -18,6 +18,7 @@ export {default as withUniqueItemsOnly} from './withUniqueItemsOnly.js';
 export {default as withFilteredList} from './withFilteredList.js';
 export {default as withMappedList} from './withMappedList.js';
 export {default as withSortedList} from './withSortedList.js';
+export {default as withStretchedList} from './withStretchedList.js';
 
 export {default as withPropertyFromList} from './withPropertyFromList.js';
 export {default as withPropertiesFromList} from './withPropertiesFromList.js';

+ 1 - 7
src/data/composite/data/withFilteredList.js

@@ -5,17 +5,11 @@
 // If the flip option is set, only items corresponding with a *falsy* value in
 // the filter are kept.
 //
-// TODO: It would be neat to apply an availability check here, e.g. to allow
-// not providing a filter at all and performing the check on the contents of
-// the list (though on the filter, if present, is fine too). But that's best
-// done by some shmancy-fancy mapping support in composite.js, so a bit out
-// of reach for now (apart from proving uses built on top of a more boring
-// implementation).
-//
 // TODO: There should be two outputs - one for the items included according to
 // the filter, and one for the items excluded.
 //
 // See also:
+//  - withAvailabilityFilter
 //  - withMappedList
 //  - withSortedList
 //

+ 36 - 0
src/data/composite/data/withStretchedList.js

@@ -0,0 +1,36 @@
+// Repeats each item in a list in-place by a corresponding length.
+
+import {input, templateCompositeFrom} from '#composite';
+import {repeat, stitchArrays} from '#sugar';
+import {isNumber, validateArrayItems} from '#validators';
+
+export default templateCompositeFrom({
+  annotation: `withStretchedList`,
+
+  inputs: {
+    list: input({type: 'array'}),
+
+    lengths: input({
+      validate: validateArrayItems(isNumber),
+    }),
+  },
+
+  outputs: ['#stretchedList'],
+
+  steps: () => [
+    {
+      dependencies: [input('list'), input('lengths')],
+      compute: (continuation, {
+        [input('list')]: list,
+        [input('lengths')]: lengths,
+      }) => continuation({
+        ['#stretchedList']:
+          stitchArrays({
+            item: list,
+            length: lengths,
+          }).map(({item, length}) => repeat(length, [item]))
+            .flat(),
+      }),
+    },
+  ],
+});

+ 0 - 0
src/data/composite/wiki-data/index.js


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů