diff options
author | Elliot Smith <elliot.smith@intel.com> | 2016-07-15 15:20:36 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2016-08-11 00:09:27 +0100 |
commit | 01c8496d47eb37a44442e79a7b071321599d7c6e (patch) | |
tree | d99258b6e3bba016815a1b0a0e1e70d5872babe3 | |
parent | 6172fb1923a34b9ba286debcb89653a347c0f5e0 (diff) | |
download | poky-01c8496d47eb37a44442e79a7b071321599d7c6e.tar.gz |
bitbake: toaster: show loading spinner after creating custom image
Creating a custom image through the "New custom image" dialog
can sometimes result in a long pause between pressing the button
to create the image, and being transferred to the page showing
details of its content. This can make it appear as though pressing
the button had no effect.
To prevent this from happening, disable the button and text box
in the new custom image dialog after the "Create image" button is
pressed. Also show a loading spinner and "loading..." text on
the button to make it clear that the application is still responding.
[YOCTO #9475]
(Bitbake rev: dd8bede91e08c0b64b949ca98c74e6144da88fd1)
Signed-off-by: Elliot Smith <elliot.smith@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
4 files changed, 66 insertions, 6 deletions
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; } | |||
250 | /* Style the special no results message in the custom image details page */ | 250 | /* Style the special no results message in the custom image details page */ |
251 | [id^="no-results-special-"] > .alert-warning > ol { margin-top: 10px; } | 251 | [id^="no-results-special-"] > .alert-warning > ol { margin-top: 10px; } |
252 | 252 | ||
253 | /* style the loading spinner in the new custom image dialog */ | ||
254 | #create-new-custom-image-btn [data-role="loading-state"] { | ||
255 | padding-left: 16px; | ||
256 | } | ||
257 | |||
258 | /* icon has to be absolutely positioned, otherwise the spin animation doesn't work */ | ||
259 | #create-new-custom-image-btn [data-role="loading-state"] .icon-spinner { | ||
260 | position: absolute; | ||
261 | left: 26px; | ||
262 | bottom: 26px; | ||
263 | } | ||
264 | |||
253 | /* Style the content of modal dialogs */ | 265 | /* Style the content of modal dialogs */ |
254 | .modal-footer { text-align: left; } | 266 | .modal-footer { text-align: left; } |
255 | .date-filter-controls { margin-top: 10px; } | 267 | .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 () { | |||
436 | }); | 436 | }); |
437 | } | 437 | } |
438 | 438 | ||
439 | // if true, the loading spinner for Ajax requests will be displayed | ||
440 | // if requests take more than 1200ms | ||
441 | var ajaxLoadingTimerEnabled = true; | ||
442 | |||
443 | // turn on the page-level loading spinner for Ajax requests | ||
444 | function _enableAjaxLoadingTimer() { | ||
445 | ajaxLoadingTimerEnabled = true; | ||
446 | } | ||
447 | |||
448 | // turn off the page-level loading spinner for Ajax requests | ||
449 | function _disableAjaxLoadingTimer() { | ||
450 | ajaxLoadingTimerEnabled = false; | ||
451 | } | ||
439 | 452 | ||
440 | return { | 453 | return { |
454 | enableAjaxLoadingTimer: _enableAjaxLoadingTimer, | ||
455 | disableAjaxLoadingTimer: _disableAjaxLoadingTimer, | ||
441 | reload_params : reload_params, | 456 | reload_params : reload_params, |
442 | startABuild : _startABuild, | 457 | startABuild : _startABuild, |
443 | cancelABuild : _cancelABuild, | 458 | cancelABuild : _cancelABuild, |
@@ -485,7 +500,6 @@ function reload_params(params) { | |||
485 | window.location.href = url+"?"+callparams.join('&'); | 500 | window.location.href = url+"?"+callparams.join('&'); |
486 | } | 501 | } |
487 | 502 | ||
488 | |||
489 | /* Things that happen for all pages */ | 503 | /* Things that happen for all pages */ |
490 | $(document).ready(function() { | 504 | $(document).ready(function() { |
491 | 505 | ||
@@ -644,7 +658,9 @@ $(document).ready(function() { | |||
644 | window.clearTimeout(ajaxLoadingTimer); | 658 | window.clearTimeout(ajaxLoadingTimer); |
645 | 659 | ||
646 | ajaxLoadingTimer = window.setTimeout(function() { | 660 | ajaxLoadingTimer = window.setTimeout(function() { |
647 | $("#loading-notification").fadeIn(); | 661 | if (libtoaster.ajaxLoadingTimerEnabled) { |
662 | $("#loading-notification").fadeIn(); | ||
663 | } | ||
648 | }, 1200); | 664 | }, 1200); |
649 | }); | 665 | }); |
650 | 666 | ||
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(){ | |||
25 | var duplicateNameMsg = "An image with this name already exists. Image names must be unique."; | 25 | var duplicateNameMsg = "An image with this name already exists. Image names must be unique."; |
26 | var duplicateImageInProjectMsg = "An image with this name already exists in this project." | 26 | var duplicateImageInProjectMsg = "An image with this name already exists in this project." |
27 | var invalidBaseRecipeIdMsg = "Please select an image to customise."; | 27 | var invalidBaseRecipeIdMsg = "Please select an image to customise."; |
28 | 28 | ||
29 | // set button to "submit" state and enable text entry so user can | ||
30 | // enter the custom recipe name | ||
31 | showSubmitState(); | ||
32 | |||
29 | /* capture clicks on radio buttons inside the modal; when one is selected, | 33 | /* capture clicks on radio buttons inside the modal; when one is selected, |
30 | * set the recipe on the modal | 34 | * set the recipe on the modal |
31 | */ | 35 | */ |
@@ -40,6 +44,9 @@ function newCustomImageModalInit(){ | |||
40 | }); | 44 | }); |
41 | 45 | ||
42 | newCustomImgBtn.click(function(e){ | 46 | newCustomImgBtn.click(function(e){ |
47 | // disable the button and text entry | ||
48 | showLoadingState(); | ||
49 | |||
43 | e.preventDefault(); | 50 | e.preventDefault(); |
44 | 51 | ||
45 | var baseRecipeId = imgCustomModal.data('recipe'); | 52 | var baseRecipeId = imgCustomModal.data('recipe'); |
@@ -69,12 +76,33 @@ function newCustomImageModalInit(){ | |||
69 | } | 76 | } |
70 | } else { | 77 | } else { |
71 | imgCustomModal.modal('hide'); | 78 | imgCustomModal.modal('hide'); |
79 | imgCustomModal.one('hidden.bs.modal', showSubmitState); | ||
72 | window.location.replace(ret.url + '?notify=new'); | 80 | window.location.replace(ret.url + '?notify=new'); |
73 | } | 81 | } |
74 | }); | 82 | }); |
75 | } | 83 | } |
76 | }); | 84 | }); |
77 | 85 | ||
86 | // enable text entry, show "Create image" button text | ||
87 | function showSubmitState() { | ||
88 | libtoaster.enableAjaxLoadingTimer(); | ||
89 | newCustomImgBtn.find('[data-role="loading-state"]').hide(); | ||
90 | newCustomImgBtn.find('[data-role="submit-state"]').show(); | ||
91 | newCustomImgBtn.removeAttr('disabled'); | ||
92 | nameInput.removeAttr('disabled'); | ||
93 | } | ||
94 | |||
95 | // disable text entry, show "Creating image..." button text; | ||
96 | // we also disabled the page-level ajax loading spinner while this spinner | ||
97 | // is active | ||
98 | function showLoadingState() { | ||
99 | libtoaster.disableAjaxLoadingTimer(); | ||
100 | newCustomImgBtn.find('[data-role="submit-state"]').hide(); | ||
101 | newCustomImgBtn.find('[data-role="loading-state"]').show(); | ||
102 | newCustomImgBtn.attr('disabled', 'disabled'); | ||
103 | nameInput.attr('disabled', 'disabled'); | ||
104 | } | ||
105 | |||
78 | function showNameError(text){ | 106 | function showNameError(text){ |
79 | invalidNameHelp.text(text); | 107 | invalidNameHelp.text(text); |
80 | invalidNameHelp.show(); | 108 | invalidNameHelp.show(); |
@@ -167,6 +195,5 @@ function newCustomImageModalSetRecipes(baseRecipes) { | |||
167 | 195 | ||
168 | // show the radio button container | 196 | // show the radio button container |
169 | imageSelector.show(); | 197 | imageSelector.show(); |
170 | 198 | } | |
171 | } | ||
172 | } | 199 | } |
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 @@ | |||
48 | </div> | 48 | </div> |
49 | 49 | ||
50 | <div class="modal-footer"> | 50 | <div class="modal-footer"> |
51 | <button id="create-new-custom-image-btn" class="btn btn-primary btn-lg" data-original-title="" title="" disabled>Create custom image</button> | 51 | <button id="create-new-custom-image-btn" class="btn btn-primary btn-large" disabled> |
52 | <span data-role="submit-state">Create custom image</span> | ||
53 | <span data-role="loading-state" style="display:none"> | ||
54 | <i class="fa-pulse icon-spinner"></i> Creating custom image... | ||
55 | </span> | ||
56 | </button> | ||
52 | </div> | 57 | </div> |
53 | </div> | 58 | </div> |
54 | </div> | 59 | </div> |