1ab5d5566906c050e5bc8ce2bf2a21cf0d08e439 chmalee Mon Sep 21 15:23:53 2026 -0700 Fix hubspace UI race condition on file metadata editing, wait for the hub.txt parse to finish before offering file metadata edit, refs #38398 diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js index 787629ec271..6a217422587 100644 --- src/hg/js/hgMyData.js +++ src/hg/js/hgMyData.js @@ -1,3055 +1,3068 @@ /* jshint esversion: 8 */ /* This file contains all the code needed to get the HubSpace UI functioning. * There are some helper functions that are sort of general and could probably * be added to utils.js or similar as well as 3 main pieces: * - uppyOptions and uppy constructor: Uppy is a 3rd party library for handling * user uploads. The uppyOptions object and constructor are used to modify * the default behavior, including what to do when a file has been added * to the dashboard, verifying file name legality, etc. Code in these sections * also modifies the default preact/react rendering of elements, so it looks * a little different than normal kent javascript. * - BatchChangePlugin class: a custom class again used to extend the default Uppy * interface. This time to put some inputs at the bottom of the dashboard * that changes metadata for all the files a user has selected * - hubCreate: An IIFE that runs on document ready that sets up or controls the * whole UI. The UI is a combindation of Uppy for the actual file selection * and DataTables for showing the uploaded files. There are many helper functions * within this block that also could probably be moved to a lib, but haven't as * the code has evolved over time. * * TODO: most of this code could probably be modularized successfully, or split up * so it is easier to read. * * The API key generation and revocation used to be a fourth piece here. It is in * hubApiKey.js now: the Hub Development tab offers those controls on sites that do not * run hubSpace, and such a site never loads this file. */ var debugCartJson = true; function prettyFileSize(num) { if (!num) {return "0B";} if (num < (1024 * 1024)) { return `${(num/1024).toFixed(1)}KB`; } else if (num < (1024 * 1024 * 1024)) { return `${((num/1024)/1024).toFixed(1)}MB`; } else { return `${(((num/1024)/1024)/1024).toFixed(1)}GB`; } } function renderTimeCell(data, type) { // DataTables renderer for the two time columns. The server sends seconds since // the epoch, so the reader sees their own timezone rather than the server's, // while ordering stays on the number if (type !== "display") { return data; } if (!data) { return ""; } return new Date(data * 1000).toLocaleString(); } function cgiEncode(value) { // copy of cheapgi.c:cgiEncode except we are explicitly leaving '/' characters, and // space becomes '+': let splitVal = value.split('/'); splitVal.forEach((ele, ix) => { if (ele == " ") { splitVal[ix] = '+'; } else { splitVal[ix] = encodeURIComponent(ele); } }); return splitVal.join('/'); } function cgiDecode(value) { // decode an encoded value return decodeURIComponent(value); } function setDbSelectFromAutocomplete(selectEle, item) { // this has been bound to the