diff --git a/bitbake/lib/toaster/toastergui/static/css/default.css b/bitbake/lib/toaster/toastergui/static/css/default.css index 3a0fbb82c8..0961c97747 100644 --- a/bitbake/lib/toaster/toastergui/static/css/default.css +++ b/bitbake/lib/toaster/toastergui/static/css/default.css @@ -250,6 +250,18 @@ code { color: #333; background-color: transparent; } /* Style the special no results message in the custom image details page */ [id^="no-results-special-"] > .alert-warning > ol { margin-top: 10px; } +/* style the loading spinner in the new custom image dialog */ +#create-new-custom-image-btn [data-role="loading-state"] { + padding-left: 16px; +} + +/* icon has to be absolutely positioned, otherwise the spin animation doesn't work */ +#create-new-custom-image-btn [data-role="loading-state"] .icon-spinner { + position: absolute; + left: 26px; + bottom: 26px; +} + /* Style the content of modal dialogs */ .modal-footer { text-align: left; } .date-filter-controls { margin-top: 10px; } diff --git a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js index a61b10e972..f56affd8ea 100644 --- a/bitbake/lib/toaster/toastergui/static/js/libtoaster.js +++ b/bitbake/lib/toaster/toastergui/static/js/libtoaster.js @@ -436,8 +436,23 @@ var libtoaster = (function () { }); } + // if true, the loading spinner for Ajax requests will be displayed + // if requests take more than 1200ms + var ajaxLoadingTimerEnabled = true; + + // turn on the page-level loading spinner for Ajax requests + function _enableAjaxLoadingTimer() { + ajaxLoadingTimerEnabled = true; + } + + // turn off the page-level loading spinner for Ajax requests + function _disableAjaxLoadingTimer() { + ajaxLoadingTimerEnabled = false; + } return { + enableAjaxLoadingTimer: _enableAjaxLoadingTimer, + disableAjaxLoadingTimer: _disableAjaxLoadingTimer, reload_params : reload_params, startABuild : _startABuild, cancelABuild : _cancelABuild, @@ -485,7 +500,6 @@ function reload_params(params) { window.location.href = url+"?"+callparams.join('&'); } - /* Things that happen for all pages */ $(document).ready(function() { @@ -644,7 +658,9 @@ $(document).ready(function() { window.clearTimeout(ajaxLoadingTimer); ajaxLoadingTimer = window.setTimeout(function() { - $("#loading-notification").fadeIn(); + if (libtoaster.ajaxLoadingTimerEnabled) { + $("#loading-notification").fadeIn(); + } }, 1200); }); diff --git a/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js b/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js index 8356c02b5a..dace8e3258 100644 --- a/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js +++ b/bitbake/lib/toaster/toastergui/static/js/newcustomimage_modal.js @@ -25,7 +25,11 @@ function newCustomImageModalInit(){ var duplicateNameMsg = "An image with this name already exists. Image names must be unique."; var duplicateImageInProjectMsg = "An image with this name already exists in this project." var invalidBaseRecipeIdMsg = "Please select an image to customise."; - + + // set button to "submit" state and enable text entry so user can + // enter the custom recipe name + showSubmitState(); + /* capture clicks on radio buttons inside the modal; when one is selected, * set the recipe on the modal */ @@ -40,6 +44,9 @@ function newCustomImageModalInit(){ }); newCustomImgBtn.click(function(e){ + // disable the button and text entry + showLoadingState(); + e.preventDefault(); var baseRecipeId = imgCustomModal.data('recipe'); @@ -69,12 +76,33 @@ function newCustomImageModalInit(){ } } else { imgCustomModal.modal('hide'); + imgCustomModal.one('hidden.bs.modal', showSubmitState); window.location.replace(ret.url + '?notify=new'); } }); } }); + // enable text entry, show "Create image" button text + function showSubmitState() { + libtoaster.enableAjaxLoadingTimer(); + newCustomImgBtn.find('[data-role="loading-state"]').hide(); + newCustomImgBtn.find('[data-role="submit-state"]').show(); + newCustomImgBtn.removeAttr('disabled'); + nameInput.removeAttr('disabled'); + } + + // disable text entry, show "Creating image..." button text; + // we also disabled the page-level ajax loading spinner while this spinner + // is active + function showLoadingState() { + libtoaster.disableAjaxLoadingTimer(); + newCustomImgBtn.find('[data-role="submit-state"]').hide(); + newCustomImgBtn.find('[data-role="loading-state"]').show(); + newCustomImgBtn.attr('disabled', 'disabled'); + nameInput.attr('disabled', 'disabled'); + } + function showNameError(text){ invalidNameHelp.text(text); invalidNameHelp.show(); @@ -167,6 +195,5 @@ function newCustomImageModalSetRecipes(baseRecipes) { // show the radio button container imageSelector.show(); - - } + } } diff --git a/bitbake/lib/toaster/toastergui/templates/newcustomimage_modal.html b/bitbake/lib/toaster/toastergui/templates/newcustomimage_modal.html index 5caa68392c..d448d3afc1 100644 --- a/bitbake/lib/toaster/toastergui/templates/newcustomimage_modal.html +++ b/bitbake/lib/toaster/toastergui/templates/newcustomimage_modal.html @@ -48,7 +48,12 @@