« get me outta code hell

data: homepage layout sections - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2025-02-11 10:33:25 -0400
committer(quasar) nebula <qznebula@protonmail.com>2025-02-12 07:43:58 -0400
commitf6b1f4d7213b953c10a6d196ec04e7e18271f3e2 (patch)
tree5b859549e768a793c6a3700ad7f7f3326ee7caa7 /src/data
parent1cd87253681074907db8e6751fd787e37b7d17b5 (diff)
data: homepage layout sections
Diffstat (limited to 'src/data')
-rw-r--r--src/data/checks.js2
-rw-r--r--src/data/things/homepage-layout.js138
-rw-r--r--src/data/yaml.js2
3 files changed, 104 insertions, 38 deletions
diff --git a/src/data/checks.js b/src/data/checks.js
index 74d0d9f9..6a890f34 100644
--- a/src/data/checks.js
+++ b/src/data/checks.js
@@ -196,7 +196,7 @@ export function filterReferenceErrors(wikiData, {
       groups: 'group',
     }],
 
-    ['homepageLayout.rows', {
+    ['homepageLayout.sections.rows', {
       sourceGroup: '_homepageSourceGroup',
       sourceAlbums: 'album',
     }],
diff --git a/src/data/things/homepage-layout.js b/src/data/things/homepage-layout.js
index c43d0c48..b4b31f4f 100644
--- a/src/data/things/homepage-layout.js
+++ b/src/data/things/homepage-layout.js
@@ -2,6 +2,7 @@ export const HOMEPAGE_LAYOUT_DATA_FILE = 'homepage.yaml';
 
 import {input} from '#composite';
 import Thing from '#thing';
+import {empty} from '#sugar';
 
 import {
   anyOf,
@@ -10,19 +11,26 @@ import {
   isString,
   isStringNonEmpty,
   validateArrayItems,
-  validateInstanceOf,
   validateReference,
 } from '#validators';
 
 import {exposeDependency} from '#composite/control-flow';
 import {withResolvedReference} from '#composite/wiki-data';
-import {color, contentString, name, referenceList, soupyFind}
-  from '#composite/wiki-properties';
+
+import {
+  color,
+  contentString,
+  name,
+  referenceList,
+  soupyFind,
+  thing,
+  thingList,
+} from '#composite/wiki-properties';
 
 export class HomepageLayout extends Thing {
   static [Thing.friendlyName] = `Homepage Layout`;
 
-  static [Thing.getPropertyDescriptors] = ({HomepageLayoutRow}) => ({
+  static [Thing.getPropertyDescriptors] = ({HomepageLayoutSection}) => ({
     // Update & expose
 
     sidebarContent: contentString(),
@@ -32,13 +40,9 @@ export class HomepageLayout extends Thing {
       update: {validate: validateArrayItems(isStringNonEmpty)},
     },
 
-    rows: {
-      flags: {update: true, expose: true},
-
-      update: {
-        validate: validateArrayItems(validateInstanceOf(HomepageLayoutRow)),
-      },
-    },
+    sections: thingList({
+      class: input.value(HomepageLayoutSection),
+    }),
   });
 
   static [Thing.yamlDocumentSpec] = {
@@ -51,50 +55,112 @@ export class HomepageLayout extends Thing {
   };
 
   static [Thing.getYamlLoadingSpec] = ({
-    documentModes: {headerAndEntries}, // Kludge, see below
+    documentModes: {allInOne},
     thingConstructors: {
       HomepageLayout,
+      HomepageLayoutSection,
       HomepageLayoutAlbumsRow,
     },
   }) => ({
     title: `Process homepage layout file`,
+    file: HOMEPAGE_LAYOUT_DATA_FILE,
+
+    documentMode: allInOne,
+    documentThing: document => {
+      if (document['Homepage']) {
+        return HomepageLayout;
+      }
 
-    // Kludge: This benefits from the same headerAndEntries style messaging as
-    // albums and tracks (for example), but that document mode is designed to
-    // support multiple files, and only one is actually getting processed here.
-    files: [HOMEPAGE_LAYOUT_DATA_FILE],
-
-    documentMode: headerAndEntries,
-    headerDocumentThing: HomepageLayout,
-    entryDocumentThing: document => {
-      switch (document['Type']) {
-        case 'albums':
-          return HomepageLayoutAlbumsRow;
-        default:
-          throw new TypeError(`No processDocument function for row type ${document['Type']}!`);
+      if (document['Section']) {
+        return HomepageLayoutSection;
       }
+
+      if (document['Row']) {
+        switch (document['Row']) {
+          case 'albums':
+            return HomepageLayoutAlbumsRow;
+          default:
+            throw new TypeError(`Unrecognized row type ${document['Row']}`);
+        }
+      }
+
+      return null;
     },
 
     save(results) {
-      if (!results[0]) {
-        return;
+      if (!empty(results) && !(results[0] instanceof HomepageLayout)) {
+        throw new Error(`Expected 'Homepage' document at top of homepage layout file`);
       }
 
-      const {header: homepageLayout, entries: rows} = results[0];
-      Object.assign(homepageLayout, {rows});
+      const homepageLayout = results[0];
+      const sections = [];
+
+      let currentSection = null;
+      let currentSectionRows = [];
+
+      const closeCurrentSection = () => {
+        if (currentSection) {
+          for (const row of currentSectionRows) {
+            row.section = currentSection;
+          }
+
+          currentSection.rows = currentSectionRows;
+          sections.push(currentSection);
+
+          currentSection = null;
+          currentSectionRows = [];
+        }
+      };
+
+      for (const entry of results.slice(1)) {
+        if (entry instanceof HomepageLayout) {
+          throw new Error(`Expected only one 'Homepage' document in total`);
+        } else if (entry instanceof HomepageLayoutSection) {
+          closeCurrentSection();
+          currentSection = entry;
+        } else if (entry instanceof HomepageLayoutRow) {
+          currentSectionRows.push(entry);
+        }
+      }
+
+      closeCurrentSection();
+
+      homepageLayout.sections = sections;
+
       return {homepageLayout};
     },
   });
 }
 
+export class HomepageLayoutSection extends Thing {
+  static [Thing.friendlyName] = `Homepage Section`;
+
+  static [Thing.getPropertyDescriptors] = ({HomepageLayoutRow}) => ({
+    // Update & expose
+
+    name: name(`Unnamed Homepage Section`),
+
+    color: color(),
+
+    rows: thingList({
+      class: input.value(HomepageLayoutRow),
+    }),
+  });
+
+  static [Thing.yamlDocumentSpec] = {
+    fields: {
+      'Section': {property: 'name'},
+      'Color': {property: 'color'},
+    },
+  };
+}
+
 export class HomepageLayoutRow extends Thing {
   static [Thing.friendlyName] = `Homepage Row`;
 
-  static [Thing.getPropertyDescriptors] = () => ({
+  static [Thing.getPropertyDescriptors] = ({HomepageLayoutSection}) => ({
     // Update & expose
 
-    name: name('Unnamed Homepage Row'),
-
     type: {
       flags: {update: true, expose: true},
 
@@ -105,7 +171,9 @@ export class HomepageLayoutRow extends Thing {
       },
     },
 
-    color: color(),
+    section: thing({
+      class: input.value(HomepageLayoutSection),
+    }),
 
     // Update only
 
@@ -114,9 +182,7 @@ export class HomepageLayoutRow extends Thing {
 
   static [Thing.yamlDocumentSpec] = {
     fields: {
-      'Row': {property: 'name'},
-      'Color': {property: 'color'},
-      'Type': {property: 'type'},
+      'Row': {property: 'type'},
     },
   };
 }
diff --git a/src/data/yaml.js b/src/data/yaml.js
index fbe5b7d4..ee65eb7f 100644
--- a/src/data/yaml.js
+++ b/src/data/yaml.js
@@ -1253,7 +1253,7 @@ export function linkWikiDataArrays(wikiData, {bindFind, bindReverse}) {
 
     ['groupCategoryData', [/* find */]],
 
-    ['homepageLayout.rows', [/* find */]],
+    ['homepageLayout.sections.rows', [/* find */]],
 
     ['trackData', [
       'albumData',