a4c8987ac13d72f85a7c3adde3f867456a85516f
chmalee
  Thu Sep 26 15:52:46 2024 -0700
Save newly created hubs to file table, return hubs when loading the page

diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js
index e015aeb..1aa42f2 100644
--- src/hg/js/hgMyData.js
+++ src/hg/js/hgMyData.js
@@ -1,36 +1,39 @@
 /* jshint esnext: true */
 debugCartJson = true;
 
 function prettyFileSize(num) {
+    if (!num) {return "n/a";}
     if (num < (1000 * 1024)) {
         return `${(num/1000).toFixed(1)}kb`;
     } else if (num < (1000 * 1000 * 1024)) {
         return `${((num/1000)/1000).toFixed(1)}mb`;
     } else {
         return `${(((num/1000)/1000)/1000).toFixed(1)}gb`;
     }
 }
 
 var hubCreate = (function() {
     let uiState = { // our object for keeping track of the current UI and what to do
         toUpload: {}, // set of file objects keyed by name
         input: null, // the hidden input element
         pickedList: null, // the <div> for displaying files in toUpload
         pendingQueue: [], // our queue of pending [tus.Upload, file], kind of like the toUpload object
         fileList: [], // the files this user has uploaded, initially populated by the server
                         // on page load, but gets updated as the user uploades/deletes files
+        hubList: [], // the hubs this user has created/uploaded, initially populated by server
+                        // on page load, but gets updated as the user creates/deletes hubs
         userUrl: "", // the web accesible path where the uploads are stored for this user
     };
 
     // We can use XMLHttpRequest if necessary or a mirror can't use tus
     var useTus = tus.isSupported && true;
 
     function getTusdEndpoint() {
         // return the port and basepath of the tusd server
         // NOTE: the port and basepath are specified in hg.conf
         //let currUrl = parseUrl(window.location.href);
         return "https://hgwdev-hubspace.gi.ucsc.edu/files";
     }
 
     function togglePickStateMessage(showMsg = false) {
         if (showMsg) {
@@ -424,30 +427,38 @@
     function addNewUploadedFileToTable(req) {
         // req is an object with properties of an uploaded file, make a new row
         // for it in the filesTable
         let table = null;
         if ($.fn.dataTable.isDataTable("#filesTable")) {
             table = $("#filesTable").DataTable();
             let newRow = table.row.add(req).draw();
         } else {
             showExistingFiles([req]);
         }
     }
 
     function createHubSuccess(jqXhr, textStatus) {
         console.log(jqXhr);
         $("#newTrackHubDialog").dialog("close");
+        addNewUploadedFileToTable({
+            createTime: jqXhr.creationTime,
+            fileType: "hub",
+            fileName: jqXhr.hubName,
+            genome: jqXhr.db,
+            fileSize: null,
+            hub: jqXhr.hubName
+        });
     }
 
     function createHub(db, hubName) {
         // send a request to hgHubConnect to create a hub for this user
         cart.setCgi("hgHubConnect");
         cart.send({createHub: {db: db, name: hubName}}, createHubSuccess, null);
         cart.flush();
     }
 
     function startHubCreate() {
         // put up a dialog to walk a user through setting up a track hub
         console.log("create a hub button clicked!");
         $("#newTrackHubDialog").dialog({
             minWidth: $("#newTrackHubDialog").width(),
         });
@@ -471,85 +482,146 @@
                 ]
             }
         },
         columnDefs: [
             {
                 orderable: false, targets: 0,
                 title: "<input type=\"checkbox\"></input>",
                 render: function(data, type, row) {
                     return "<input type=\"checkbox\"></input>";
                 }
             },
             {
                 orderable: false, targets: 1,
                 data: "action", title: "Action",
                 render: function(data, type, row) {
+                    /* Return a node for rendering the actions column */
+                    // all of our actions will be buttons in this div:
+                    let container = document.createElement("div");
+
                     // click to call hgHubDelete file
-                    return "<button class='deleteFileBtn'>Delete</button><button class='viewInBtn'>View In GB</button>";
+                    let delBtn = document.createElement("button");
+                    delBtn.textContent = "Delete";
+                    delBtn.type = 'button';
+                    delBtn.addEventListener("click", function() {
+                        deleteFile(0, row.fileName);
+                    });
+
+                    // click to view hub/file in gb:
+                    let viewBtn = document.createElement("button");
+                    viewBtn.textContent = "View in Genome Browser";
+                    viewBtn.type = 'button';
+                    viewBtn.addEventListener("click", function() {
+                        viewInGenomeBrowser(row.fileName, row.genome);
+                    });
+
+                    // click to rename file or hub:
+                    let renameBtn = document.createElement("button");
+                    renameBtn.textContent = "Rename";
+                    renameBtn.type = 'button';
+                    renameBtn.addEventListener("click", function() {
+                        console.log("rename btn clicked!");
+                    });
+
+                    // click to associate this track to a hub
+                    let addToHubBtn = document.createElement("button");
+                    addToHubBtn.textContent = "Add to hub";
+                    addToHubBtn.type = 'button';
+                    addToHubBtn.addEventListener("click", function() {
+                        console.log("add to hub button clicked!");
+                    });
+
+                    container.appendChild(delBtn);
+                    container.appendChild(viewBtn);
+                    container.appendChild(renameBtn);
+                    container.appendChild(addToHubBtn);
+                    
+                    return container;
                 }
             },
             {
                 targets: 3,
                 render: function(data, type, row) {
                     return dataTablePrintSize(data);
                 }
             }
         ],
         columns: [
             {data: "", },
             {data: "", },
             {data: "fileName", title: "File name"},
             {data: "fileSize", title: "File size", render: dataTablePrintSize},
             {data: "fileType", title: "File type"},
             {data: "genome", title: "Genome"},
             {data: "hub", title: "Hubs"},
             {data: "createTime", title: "Creation Time"},
         ],
         order: [[6, 'desc']],
+        /*
         drawCallback: function(settings) {
+            // every time we draw we need to update event handlers if we've added/deleted a row
             let table = this.DataTable();
             btns = document.querySelectorAll('.deleteFileBtn');
             let i, numRows= table.rows().data().length;
             for (i = 0; i < numRows; i++) {
                 let fname = table.cell(i, 2).data();
                 btns[i].addEventListener("click", (e) => {
                     deleteFile(i, fname);
                 });
             }
             btns = document.querySelectorAll('.viewInBtn');
             for (i = 0; i < numRows; i++) {
                 let fname = table.cell(i, 2).data();
                 let genome = table.cell(i, 5).data();
                 btns[i].addEventListener("click", (e) => {
                     viewInGenomeBrowser(fname, genome);
                 });
             }
         },
+        */
     };
 
     function showExistingFiles(d) {
         // Make the DataTable for each file
         // make buttons have the same style as other buttons
         $.fn.dataTable.Buttons.defaults.dom.button.className = 'button';
         tableInitOptions.data = d;
         let table = new DataTable("#filesTable", tableInitOptions);
         let toRemove = document.getElementById("welcomeDiv");
         if (d.length > 0 && toRemove !== null) {
             toRemove.remove();
         }
     }
 
+    function showExistingHubs(d) {
+        // Add the hubs to the files table
+        let table = $("#filesTable").DataTable();
+        d.forEach((hub) => {
+            let hubName = hub.hubName;
+            let db = hub.genome;
+            let data = {
+                fileName: hubName,
+                fileSize: null,
+                fileType: "hub",
+                genome: db,
+                hub: hubName,
+                createTime: null,
+            };
+            table.row.add(data).draw();
+        });
+    }
+
     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) {
             alert("Warning: " + jsonData.warning);
             return true;
         } else {
             if (debugCartJson) {
                 console.log('from server:\n', jsonData);
             }
@@ -608,36 +680,37 @@
         } else {
             // no cartJson object means we are coming to the page for the first time:
             //cart.send({ getUiState: {} }, handleRefreshState);
             //cart.flush();
             // TODO: initialize buttons, check if there are already files
             // TODO: write functions for
             //     after picking files
             //     choosing file types
             //     creating default trackDbs
             //     editing trackDbs
             let welcomeDiv = document.createElement("div");
             welcomeDiv.id = "welcomeDiv";
             welcomeDiv.textContent = "Once files are uploaded they will display here. Click \"Choose files\" above or \"Create Hub\" below to get started";
             let fileDiv = document.getElementById('filesDiv');
             fileDiv.insertBefore(welcomeDiv, fileDiv.firstChild);
-            if (typeof userFiles !== 'undefined' && typeof userFiles.fileList !== 'undefined' &&
-                    userFiles.fileList.length > 0) { 
+            if (typeof userFiles !== 'undefined' && (userFiles.fileList.length > 0 || userFiles.hubList.length > 0)) { 
                 uiState.fileList = userFiles.fileList;
+                uiState.hubList = userFiles.hubList;
                 uiState.userUrl = userFiles.userUrl;
             }
             showExistingFiles(uiState.fileList);
+            showExistingHubs(uiState.hubList);
             inputBtn.addEventListener("click", (e) => uiState.input.click());
             //uiState.input.addEventListener("change", listPickedFiles);
             // TODO: add event handler for when file is succesful upload
             // TODO: add event handlers for editing defaults, grouping into hub
             // TODO: display quota somewhere
             // TODO: customize the li to remove the picked file
         }
         $("#newTrackHubDialog").dialog({
             modal: true,
             autoOpen: false,
             title: "Create new track hub",
             closeOnEscape: true,
             minWidth: 400,
             minHeight: 120
         });