bd91a935679eda68e6bf59ee40e480f3cbd0f6e3
chmalee
  Fri Jun 5 12:48:09 2026 -0700
Fix hubSpace genome selection committing the wrong genome to hub.txt, refs #37713

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js
index ab94bbd7b77..2c2d84bd049 100644
--- src/hg/js/hgMyData.js
+++ src/hg/js/hgMyData.js
@@ -225,47 +225,51 @@
                 let ret = h('div', {
                         class: "uppy-Dashboard-FileCard-label",
                         style: "display: inline-block; width: 78%"
                         },
                     // first child of div
                     "Select from popular assemblies:",
                     // second div child
                     h('select', {
                         id: `${file.meta.name}DbSelect`,
                         style: "margin-left: 5px",
                         onChange: e => {
                             let val = e.target.value;
                             let label = e.target.selectedOptions[0].label;
                             let hub = hubCreate.assemblyHubByGenome(val);
                             let newParentDir = hub ? hub.fileName : hubCreate.uiState.hubNameDefault;
-                            // Uppy's file card keeps controlled-input
-                            // state per field and commits it to meta on
-                            // Upload, overwriting setFileMeta. Route
-                            // genome through the field-level onChange and
-                            // force the Hub Name input value to match.
+                            // we call onChange here, which will do an onChange with a potentially
+                            // stale metadata if the user has also edited parentDir. later we will
+                            // fix that up and use the genome name as the recommended parentDir
+                            // or a pre-existing hub if one exists
                             onChange(val);
-                            uppy.setFileMeta(file.id, {
-                                genomeLabel: label,
-                                hubType: hub ? "assemblyHub" : "trackHub",
-                                parentDir: newParentDir,
-                            });
+                            file.meta.genome = val;
+                            file.meta.genomeLabel = label;
+                            file.meta.hubType = hub ? "assemblyHub" : "trackHub";
+                            file.meta.parentDir = newParentDir;
+                            // Sync the Hub Name field in a later tick. In this
+                            // tick its onChange would spread the same stale
+                            // state as the genome onChange above and revert
+                            // genome; deferring lets genome flush first.
+                            setTimeout(function() {
                                 let pd = document.getElementById("uppy-Dashboard-FileCard-input-parentDir");
                                 if (pd) {
                                     pd.value = newParentDir;
                                     pd.dispatchEvent(new Event("input", {bubbles: true}));
                                     pd.dispatchEvent(new Event("change", {bubbles: true}));
                                 }
+                            }, 0);
                         }
                         },
                         hubCreate.makeGenomeSelectOptions(file.meta.genome, file.meta.genomeLabel).map( (genomeObj) => {
                             return h('option', {
                                 value: genomeObj.value,
                                 label: genomeObj.label,
                                 selected: file.meta.genome !== null ? genomeObj.value === file.meta.genome : genomeObj.value === hubCreate.defaultDb()
                             });
                         })
                     ),
                     h('p', {
                         class: "uppy-Dashboard-FileCard-label",
                         style: "display: block; width: 78%",
                         }, "or search for your genome:"),
                     // third div child
@@ -1371,58 +1375,56 @@
 
     function makeGenomeSelectOptions(value, label) {
         // Returns an array of options for genomes, if value and label exist, add that
         // as an additional option
         let ret = [];
         let cartChoice = {};
         cartChoice.id = cartDb;
         cartChoice.label = cartDb;
         cartChoice.value = cartDb.split(" ").slice(-1)[0];
         if (cartChoice.value.startsWith("hub_")) {
             cartChoice.label = cartDb.split(" ").slice(0,-1).join(" "); // take off the actual db value
         }
         cartChoice.selected = value && label ? false: true;
         defaultGenomeChoices[cartChoice.label] = cartChoice;
 
-        // next time around our value/label pair will be a default. this time around we
-        // want it selected because it was explicitly asked for, but it may not be next time
+        // Add an explicitly chosen genome (e.g. from the search box) before
+        // building the list so it is selectable on this render, not the next.
+        // Skip assembly-hub genomes, which the loop below adds with a suffix.
+        if (value && label && !(label in defaultGenomeChoices) &&
+            !genomeIsAssemblyHub(value)) {
+            defaultGenomeChoices[label] = {value: value, label: label};
+        }
         ret = Object.values(defaultGenomeChoices);
 
         // Include the user's uploaded assembly hubs as options. One entry per
         // assembly hub (dedupe by genome name), taken from the dir row in
         // filesHash. This lets users picking a dropdown genome target a hub
         // they already created.
         let seenAsmHub = {};
         for (let fullPath in uiState.filesHash) {
             let fd = uiState.filesHash[fullPath];
             if (fd.fileType === "dir" && fd.hubType === "assemblyHub" &&
                 fd.genome && !seenAsmHub[fd.genome]) {
                 seenAsmHub[fd.genome] = true;
                 ret.push({
                     value: fd.genome,
                     label: `${fd.genome} (your assembly hub)`,
                 });
             }
         }
 
-        // Cache the value/label pair so it's a default next time - but skip
-        // assembly-hub genomes, those are added by the loop above with the
-        // "(your assembly hub)" suffix and would otherwise show up twice.
-        if (value && label && !(label in defaultGenomeChoices) &&
-            !genomeIsAssemblyHub(value)) {
-            defaultGenomeChoices[label] = {value: value, label: label, selected: true};
-        }
         return ret;
     }
 
     function makeTypeSelectOptions() {
         let ret = [];
         let autoChoice = {};
         autoChoice.label = "Auto-detect from extension";
         autoChoice.value = "Auto-detect from extension";
         autoChoice.selected = true;
         ret.push(autoChoice);
         let choices = ["bigBed", "bam", "vcf", "vcf (bgzip or gzip compressed)", "bigWig", "hic", "cram", "bigBarChart", "bigGenePred", "bigMaf", "bigInteract", "bigPsl", "bigChain"];
         choices.forEach( (e) =>  {
             let choice = {};
             choice.id = e;
             choice.label = e;