« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/static/js
diff options
context:
space:
mode:
Diffstat (limited to 'src/static/js')
-rw-r--r--src/static/js/client/index.js85
-rw-r--r--src/static/js/client/sidebar-search.js17
2 files changed, 97 insertions, 5 deletions
diff --git a/src/static/js/client/index.js b/src/static/js/client/index.js
index 53432a91..16ebe89f 100644
--- a/src/static/js/client/index.js
+++ b/src/static/js/client/index.js
@@ -75,6 +75,10 @@ const situationalSteps = {
 
 const stepInfoSymbol = Symbol();
 
+const boundSessionStorage =
+  window.hsmusicBoundSessionStorage =
+  Object.create(null);
+
 for (const module of modules) {
   const {info} = module;
 
@@ -159,12 +163,47 @@ for (const module of modules) {
 
       const storageKey = `hsmusic.${infoKey}.${key}`;
 
+      // There are two storage systems besides actual session storage in play.
+      //
+      // "Fallback" is for if session storage is not available, which may
+      // suddenly become the case, i.e. access is temporarily revoked or fails.
+      // The fallback value is controlled completely internally i.e. in this
+      // infrastructure, in this lexical scope.
+      //
+      // "Bound" is for if the value kept in session storage was saved to
+      // the page when the page was initially loaded, rather than a living
+      // window on session storage (which may be affected by pages later in
+      // the history stack). Whether or not bound storage is in effect is
+      // controlled at page load (of course), by each module's own logic.
+      //
+      // Asterisk: Bound storage can't work miracles and if the page is
+      // actually deloaded with its JavaScript state discarded, the bound
+      // values are lost, even if the browser recreates on-page form state.
+
       let fallbackValue = defaultValue;
+      let boundValue = undefined;
+
+      const updateBoundValue = (givenValue = undefined) => {
+        if (givenValue) {
+          if (
+            infoKey in boundSessionStorage &&
+            key in boundSessionStorage[infoKey]
+          ) {
+            boundSessionStorage[infoKey][key] = givenValue;
+          }
+        } else {
+          boundValue = boundSessionStorage[infoKey]?.[key];
+        }
+      };
 
       Object.defineProperty(info.session, key, {
         get: () => {
+          updateBoundValue();
+
           let value;
-          try {
+          if (boundValue !== undefined) {
+            value = boundValue ?? defaultValue;
+          } else try {
             value = sessionStorage.getItem(storageKey) ?? defaultValue;
           } catch (error) {
             if (error instanceof DOMException) {
@@ -200,21 +239,23 @@ for (const module of modules) {
             return;
           }
 
-          let operation;
+          let sessionOperation;
           if (value === '') {
             fallbackValue = null;
-            operation = () => {
+            updateBoundValue(null);
+            sessionOperation = () => {
               sessionStorage.removeItem(storageKey);
             };
           } else {
             fallbackValue = value;
-            operation = () => {
+            updateBoundValue(value);
+            sessionOperation = () => {
               sessionStorage.setItem(storageKey, value);
             };
           }
 
           try {
-            operation();
+            sessionOperation();
           } catch (error) {
             if (!(error instanceof DOMException)) {
               throw error;
@@ -244,6 +285,40 @@ for (const module of modules) {
   }
 }
 
+function evaluateBindSessionStorageStep(bindSessionStorage) {
+  const {id: infoKey, session: moduleExposedSessionObject} =
+    bindSessionStorage[stepInfoSymbol];
+
+  const generator = bindSessionStorage();
+
+  let lastBoundValue;
+  while (true) {
+    const {value: key, done} = generator.next(lastBoundValue);
+    const storageKey = `hsmusic.${infoKey}.${key}`;
+
+    let value = undefined;
+    try {
+      value = sessionStorage.getItem(storageKey);
+    } catch (error) {
+      if (!(error instanceof DOMException)) {
+        throw error;
+      }
+    }
+
+    if (value === undefined) {
+      // This effectively gets the default value.
+      value = moduleExposedSessionObject[key];
+    }
+
+    boundSessionStorage[infoKey] ??= Object.create(null);
+    boundSessionStorage[infoKey][key] = value;
+
+    lastBoundValue = value;
+
+    if (done) break;
+  }
+}
+
 function evaluateStep(stepsObject, key) {
   for (const step of stepsObject[key]) {
     try {
diff --git a/src/static/js/client/sidebar-search.js b/src/static/js/client/sidebar-search.js
index 3cc3d218..0b905948 100644
--- a/src/static/js/client/sidebar-search.js
+++ b/src/static/js/client/sidebar-search.js
@@ -158,6 +158,17 @@ export const info = {
   },
 };
 
+export function* bindSessionStorage() {
+  if (yield 'activeQuery') {
+    yield 'activeQueryContextPageName';
+    yield 'activeQueryContextPagePathname';
+    yield 'activeQueryContextPageColor';
+    yield 'activeQueryResults';
+    yield 'activeFilterType';
+    yield 'resultsScrollOffset';
+  }
+}
+
 export function getPageReferences() {
   info.pageContainer =
     document.getElementById('page-container');
@@ -443,6 +454,12 @@ export function mutatePageContent() {
 
   info.searchBox.appendChild(info.endSearchRule);
   info.searchBox.appendChild(info.endSearchLine);
+
+  // Accommodate the web browser reconstructing the search input with a value
+  // that was previously entered (or restored after recall), i.e. because
+  // the user is traversing very far back in history and yet the browser is
+  // trying to rebuild the page as-was anyway, by telling it "no don't".
+  info.searchInput.value = '';
 }
 
 export function addPageListeners() {