690b7e4b9c74d17ea3fc17c70435f5984f4c2f04
chmalee
  Mon Jan 6 16:43:13 2025 -0800
Fix links to hub.txt files to work correctly. Fix order of files returned from the server to sort on location first then uploadTime. Still working on making newly uploaded files sort to the right place in UI

diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js
index ae2fdba..73f3248 100644
--- src/hg/js/hgMyData.js
+++ src/hg/js/hgMyData.js
@@ -219,31 +219,36 @@
                         // TODO: this should probably raise an alert to click through
             let hubsAdded = {};
             _.forEach(data, (d) => {
                 if (!genome) {
                     genome = d.genome;
                     url += "&db=" + genome;
                 }
                 if (d.fileType in extensionMap) {
                     // TODO: tusd should return this location in it's response after
                     // uploading a file and then we can look it up somehow, the cgi can
                     // write the links directly into the html directly for prev uploaded files maybe?
                     if (!(d.parentDir in hubsAdded)) {
                         // NOTE: hubUrls get added regardless of whether they are on this assembly
                         // or not, because multiple genomes may have been requested. If this user
                         // switches to another genome we want this hub to be connected already
-                        url += "&hubUrl=" + uiState.userUrl + d.parentDir + "hub.txt";
+                        url += "&hubUrl=" + uiState.userUrl + d.parentDir;
+                        if (d.parentDir.endsWith("/")) {
+                            url += "hub.txt";
+                        } else {
+                            url += "/hub.txt";
+                        }
                     }
                     hubsAdded[d.parentDir] = true;
                     if (d.genome == genome) {
                         // turn the track on if its for this db
                         url += "&" + d.fileName + "=pack";
                     }
                 } else if (d.fileType === "hub.txt") {
                     url += "&hubUrl=" + uiState.userUrl + d.fullPath;
                 } 
             });
             window.location.assign(url);
             return false;
         }
     }
 
@@ -379,38 +384,37 @@
         console.log(`sending addToHub req for ${rowData.fileName} to `);
         cart.setCgi("hgHubConnect");
         cart.send({addToHub: {hubName: "", dataFile: ""}});
         cart.flush();
     }
 
 
     // hash of file paths to their objects, starts as userFiles
     let filesHash = {};
     function addNewUploadedFileToTable(req) {
         // req is an object with properties of an uploaded file, make a new row
         // for it in the filesTable
         if (!(req.fullPath in filesHash)) {
             if ($.fn.dataTable.isDataTable("#filesTable")) {
                 let table = $("#filesTable").DataTable();
-                table.row.add(req).order([{name: "uploadTime"}, {name: "fullpath"}]);
+                let rowObj = table.row.add(req);
                 rowVis[req.fullPath] = true;
                 table.search.fixed("defaultView", function(searchStr, data, rowIx) {
                     return rowVis[data.fileName] || rowVis[data.fullPath];
-                }).draw();
-                let newRowObj = table.row((idx,rowData) => rowData.fullPath === req.fullPath);
-                indentActionButton(newRowObj);
-                let newRow = newRowObj.node();
+                }).order([{name: "fileName", dir: 'desc'}]).draw(false);
+                indentActionButton(rowObj);
+                let newRow = rowObj.node();
                 $(newRow).css('color','red').animate({color: 'black'}, 1500);
             } else {
                 showExistingFiles([req]);
             }
             filesHash[req.fullPath] = req;
         }
     }
 
     function doRowSelect(ev, table, indexes) {
         let row = table.row(indexes);
         let rowTr = row.node();
         let rowCheckbox = rowTr.childNodes[0].firstChild;
         if (rowTr.classList.contains("parentRow")) {
             // we need to manually check the children
             table.rows((idx,rowData) => rowData.fullPath.startsWith(row.data().fullPath) && rowData.parentDir === row.data().fileName).every(function(rowIdx, tableLoop, rowLoop) {
@@ -506,51 +510,53 @@
                 }
             },
             {
                 targets: 5,
                 render: function(data, type, row) {
                     if (type === "display") {
                         return dataTablePrintGenome(data);
                     }
                     return data;
                 }
             },
             {
                 // The upload time column, not visible but we use it to sort on new uploads
                 targets: 8,
                 visible: false,
-                searchable: false
+                searchable: false,
+                orderable: true,
             },
             {
                 targets: 9,
                 visible: false,
                 searchable: false,
+                orderable: true,
             }
         ],
         columns: [
             {data: "", },
             {data: "", },
             {data: "fileName", title: "File name"},
             {data: "fileSize", title: "File size"},
             {data: "fileType", title: "File type"},
             {data: "genome", title: "Genome"},
             {data: "parentDir", title: "Hubs"},
             {data: "lastModified", title: "File Last Modified"},
             {data: "uploadTime", title: "Upload Time"},
             {data: "fullPath", title: "fullPath"},
         ],
-        order: [{name: "uploadTime"}, {name: "fullpath"}],
+        order: [{name: "fullPath"},{name: "uploadTime"}],
         drawCallback: function(settings) {
             console.log("table draw");
             if (isLoggedIn) {
                 settings.api.buttons(0).enable();
             }
         },
         rowCallback: function(row, data, displayNum, displayIndex, dataIndex) {
             // a row can represent one of three things:
             // a 'folder', with no parents, but with children
             // a folder with parents and children (can only come from hubtools
             // a 'file' with no children, but with parentDir
             // we assign the appropriate classes which are used later to
             // collapse/expand and select rows for viewing or deletion
             if (!data.parentDir) {
                 row.className = "topLevelRow";
@@ -588,30 +594,36 @@
         DataTable.feature.register('quota', function(settings, opts) {
             let options = Object.assign({option1: false, option2: false}, opts);
             let container = document.createElement("div");
             if (isLoggedIn) {
                 container.textContent = `Using ${prettyFileSize(userQuota)} of ${prettyFileSize(maxQuota)}`;
             }
             return container;
         });
         let table = new DataTable("#filesTable", tableInitOptions);
         table.on("select", function(e, dt, type, indexes) {
             doRowSelect(e, dt, indexes);
         });
         table.on("deselect", function(e, dt, type, indexes) {
             doRowSelect(e, dt, indexes);
         });
+        table.on('order', function() {
+            let order = table.order();
+            if (order.length > 0) {
+                console.log(`ordering table on column ${order[0].name}`);
+            }
+        });
         _.each(d, function(f) {
             filesHash[f.fullPath] = f;
         });
         return table;
     }
 
     function checkJsonData(jsonData, callerName) {
         // Return true if jsonData isn't empty and doesn't contain an error;
         // otherwise complain on behalf of caller.
         if (! jsonData) {
             alert(callerName + ': empty response from server');
         } else if (jsonData.error) {
             console.error(jsonData.error);
             alert(callerName + ': error from server: ' + jsonData.error);
         } else if (jsonData.warning) {
@@ -911,56 +923,57 @@
             closeModalOnClickOutside: true,
             closeAfterFinish: true,
             theme: 'auto',
         };
         let tusOptions = {
             endpoint: getTusdEndpoint(),
             withCredentials: true,
             retryDelays: null,
         };
         uppy.use(Uppy.Dashboard, uppyOptions);
         uppy.use(Uppy.Tus, tusOptions);
         uppy.use(BatchChangePlugin, {target: Uppy.Dashboard});
         uppy.on('upload-success', (file, response) => {
             const metadata = file.meta;
             const d = new Date(metadata.lastModified);
+            const now = new Date(Date.now());
             newReqObj = {
-                "uploadTime": Date.now(),
-                "lastModified": d.toLocaleString(),
                 "fileName": metadata.fileName,
                 "fileSize": metadata.fileSize,
                 "fileType": metadata.fileType,
                 "genome": metadata.genome,
                 "parentDir": metadata.parentDir,
+                "lastModified": d.toLocaleString(),
+                "uploadTime": now.toLocaleString(),
                 "fullPath": metadata.parentDir + "/" + metadata.fileName,
             };
             // from what I can tell, any response we would create in the pre-finish hook
             // is completely ignored for some reason, so we have to fake the other files
             // we would have created with this one file and add them to the table if they
             // weren't already there:
             hubTxtObj = {
-                "uploadTime": Date.now(),
+                "uploadTime": now.toLocaleString(),
                 "lastModified": d.toLocaleString(),
                 "fileName": "hub.txt",
                 "fileSize": 0,
                 "fileType": "hub.txt",
                 "genome": metadata.genome,
                 "parentDir": metadata.parentDir,
                 "fullPath": metadata.parentDir + "/hub.txt",
             };
             parentDirObj = {
-                "uploadTime": Date.now(),
+                "uploadTime": now.toLocaleString(),
                 "lastModified": d.toLocaleString(),
                 "fileName": metadata.parentDir,
                 "fileSize": 0,
                 "fileType": "dir",
                 "genome": metadata.genome,
                 "parentDir": "",
                 "fullPath": metadata.parentDir + "/",
             };
             addNewUploadedFileToTable(parentDirObj);
             addNewUploadedFileToTable(hubTxtObj);
             addNewUploadedFileToTable(newReqObj);
         });
     }
     return { init: init,
              uiState: uiState,