diff options
| -rw-r--r-- | src/content/dependencies/generateArtistInfoPageCommentaryChunkedList.js | 141 | ||||
| -rw-r--r-- | src/content/dependencies/generateContentEntry.js | 8 | ||||
| -rw-r--r-- | src/content/dependencies/generateContentEntryDate.js | 2 | ||||
| -rw-r--r-- | src/content/dependencies/generateLyricsEntry.js | 4 | ||||
| -rw-r--r-- | src/data/checks.js | 7 | ||||
| -rw-r--r-- | src/data/composite.js | 32 | ||||
| -rw-r--r-- | src/data/composite/things/content/withExpressedOrImplicitArtistReferences.js | 29 | ||||
| -rw-r--r-- | src/data/things/content/ContentEntry.js | 53 | ||||
| -rw-r--r-- | src/static/css/search.css | 2 | ||||
| -rw-r--r-- | src/strings-default.yaml | 4 |
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 |