From 233b6d0898340779208796e613e746ed388efd47 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 14 Nov 2023 09:23:59 +0100 Subject: [PATCH 1/3] Add progress overlay when saving settings and add general progress bar for (1) general page load, (2) login, and (3) saving settings Signed-off-by: DL6ER --- scripts/pi-hole/js/footer.js | 7 +- scripts/pi-hole/js/login.js | 5 +- scripts/pi-hole/js/settings.js | 4 +- scripts/pi-hole/js/utils.js | 46 +++++++++++- scripts/pi-hole/lua/footer.lp | 6 ++ scripts/pi-hole/lua/header.lp | 4 + scripts/vendor/nprogress.min.js | 128 ++++++++++++++++++++++++++++++++ scripts/vendor/waitMe.min.js | 13 ++++ style/vendor/nprogress.min.css | 1 + style/vendor/waitMe.min.css | 6 ++ 10 files changed, 215 insertions(+), 5 deletions(-) create mode 100644 scripts/vendor/nprogress.min.js create mode 100644 scripts/vendor/waitMe.min.js create mode 100644 style/vendor/nprogress.min.css create mode 100644 style/vendor/waitMe.min.css diff --git a/scripts/pi-hole/js/footer.js b/scripts/pi-hole/js/footer.js index 380bc485..1fd91282 100644 --- a/scripts/pi-hole/js/footer.js +++ b/scripts/pi-hole/js/footer.js @@ -5,7 +5,7 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global utils:false, moment:false */ +/* global utils:false, moment:false, NProgress: false */ //The following functions allow us to display time until pi-hole is enabled after disabling. //Works between all pages @@ -712,3 +712,8 @@ if ($.fn.dataTable) { console.log("DataTables warning: " + message); }; } + +// Page has finished loading +$(window).on("load", function () { + NProgress.done(); +}); diff --git a/scripts/pi-hole/js/login.js b/scripts/pi-hole/js/login.js index a1e4b5ca..6b3a37c3 100644 --- a/scripts/pi-hole/js/login.js +++ b/scripts/pi-hole/js/login.js @@ -5,7 +5,7 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global utils:false */ +/* global utils:false, NProgress:false */ function getParams() { var GETDict = {}; @@ -104,6 +104,7 @@ function wrongPassword(isError = false, isSuccess = false, data = null) { function doLogin(password) { wrongPassword(false, false, null); + NProgress.start(); utils.disableAll(); $.ajax({ url: "/api/auth", @@ -114,10 +115,12 @@ function doLogin(password) { }) .done(function (data) { wrongPassword(false, true, data); + NProgress.done(); redirect(); }) .fail(function (data) { wrongPassword(true, false, data); + NProgress.done(); utils.enableAll(); }); } diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 534b93a3..0b16d685 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -175,8 +175,8 @@ function saveSettings() { "Successfully saved and applied settings", "" ); - // Reload page - location.reload(); + // Show loading overlay + utils.loadingOverlay(1000, true); }) .fail(function (data) { apiFailure(data); diff --git a/scripts/pi-hole/js/utils.js b/scripts/pi-hole/js/utils.js index 63fc3ec8..7fe17860 100644 --- a/scripts/pi-hole/js/utils.js +++ b/scripts/pi-hole/js/utils.js @@ -5,7 +5,7 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global moment:false, apiFailure: false, updateFtlInfo: false */ +/* global moment:false, apiFailure: false, updateFtlInfo: false, NProgress:false */ $(function () { // CSRF protection for AJAX requests, this has to be configured globally @@ -615,6 +615,49 @@ function listAlert(type, items, data) { ); } +var loadingTime = 0; +// Callback function for the loading overlay timeout +function loadingOverlayTimeoutCallback(loadingTimeout, reloadAfterTimeout) { + if (loadingTime <= loadingTimeout) { + // Update progress bar + NProgress.set(loadingTime / loadingTimeout); + // Increase loading time + loadingTime += 100; + // Schedule next update + setTimeout(loadingOverlayTimeoutCallback, 100, loadingTimeout, reloadAfterTimeout); + return; + } + + // Hide loading overlay + NProgress.done(); + + // Reload page if requested + if (reloadAfterTimeout) { + location.reload(); + } +} + +function loadingOverlay(timeout = 6000, reloadAfterTimeout = false) { + // Add three extra seconds to the timeout as safety margin + // timeout is specified in milliseconds + timeout += 3000; + + NProgress.configure({ minimum: 0, trickle: false }); + NProgress.start(); + $(".wrapper").waitMe({ + effect: "bounce", + text: "Pi-hole is currently applying your changes...", + bg: "rgba(0,0,0,0.7)", + color: "#fff", + maxSize: "", + waitTime: timeout, + textPos: "vertical", + }); + setTimeout(loadingOverlayTimeoutCallback, 100, timeout, reloadAfterTimeout); + + return true; +} + window.utils = (function () { return { escapeHtml: escapeHtml, @@ -648,5 +691,6 @@ window.utils = (function () { hexEncode: hexEncode, hexDecode: hexDecode, listsAlert: listAlert, + loadingOverlay: loadingOverlay, }; })(); diff --git a/scripts/pi-hole/lua/footer.lp b/scripts/pi-hole/lua/footer.lp index 3be0b1d9..8e9a4fbc 100644 --- a/scripts/pi-hole/lua/footer.lp +++ b/scripts/pi-hole/lua/footer.lp @@ -61,4 +61,10 @@ + diff --git a/scripts/pi-hole/lua/header.lp b/scripts/pi-hole/lua/header.lp index 8e41a0e8..c4c81585 100644 --- a/scripts/pi-hole/lua/header.lp +++ b/scripts/pi-hole/lua/header.lp @@ -78,6 +78,8 @@ is_authenticated = mg.request_info.is_authenticated html { background-color: #000; } + + @@ -118,3 +120,5 @@ if startsWith(scriptname, 'groups') then + + diff --git a/scripts/vendor/nprogress.min.js b/scripts/vendor/nprogress.min.js new file mode 100644 index 00000000..34220a37 --- /dev/null +++ b/scripts/vendor/nprogress.min.js @@ -0,0 +1,128 @@ +/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT */ +(function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():e.NProgress=n()})(this,function(){ +/** + * Helpers + */ +function e(e){return"object"==typeof HTMLElement?e instanceof HTMLElement:e&&"object"==typeof e&&1===e.nodeType&&"string"==typeof e.nodeName}function n(e,n,t){return et?t:e} +/** + * (Internal) converts a percentage (`0..1`) to a bar translateX + * percentage (`-100%..0%`). + */function t(e){return 100*(-1+e)} +/** + * (Internal) returns the correct CSS for changing the bar's + * position given an n percentage, and speed and ease from Settings + */function r(e,n,r){var i;return i="translate3d"===d.positionUsing?{transform:"translate3d("+t(e)+"%,0,0)"}:"translate"===d.positionUsing?{transform:"translate("+t(e)+"%,0)"}:{"margin-left":t(e)+"%"},i.transition="all "+n+"ms "+r,i} +/** + * (Internal) Queues a function to be executed. + */ +/** + * (Internal) Determines if an element or space separated list of class names contains a class name. + */ +function i(e,n){var t="string"==typeof e?e:a(e);return t.indexOf(" "+n+" ")>=0} +/** + * (Internal) Adds a class to an element. + */function o(e,n){var t=a(e),r=t+n;i(t,n)||( +// Trim the opening space. +e.className=r.substring(1))} +/** + * (Internal) Removes a class from an element. + */function s(e,n){var t,r=a(e);i(e,n)&&( +// Replace the class name. +t=r.replace(" "+n+" "," "), +// Trim the opening and closing spaces. +e.className=t.substring(1,t.length-1))} +/** + * (Internal) Gets a space separated list of the class names on the element. + * The list is wrapped with a single space on each end to facilitate finding + * matches within the list. + */function a(e){return(" "+(e&&e.className||"")+" ").replace(/\s+/gi," ")} +/** + * (Internal) Removes an element from the DOM. + */function u(e){e&&e.parentNode&&e.parentNode.removeChild(e)}var c,l,f={version:"0.2.0"},d=f.settings={minimum:.08,easing:"linear",positionUsing:"",speed:200,trickle:!0,trickleSpeed:200,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'}; +/** + * Updates configuration. + * + * NProgress.configure({ + * minimum: 0.1 + * }); + */ +f.configure=function(e){var n,t;for(n in e)t=e[n],void 0!==t&&e.hasOwnProperty(n)&&(d[n]=t);return this}, +/** + * Last number. + */ +f.status=null, +/** + * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`. + * + * NProgress.set(0.4); + * NProgress.set(1.0); + */ +f.set=function(e){var t=f.isStarted();e=n(e,d.minimum,1),f.status=1===e?null:e;var i=f.render(!t),o=i.querySelector(d.barSelector),s=d.speed,a=d.easing;return i.offsetWidth,/* Repaint */ +p(function(n){ +// Set positionUsing if it hasn't already been set +""===d.positionUsing&&(d.positionUsing=f.getPositioningCSS()), +// Add transition +m(o,r(e,s,a)),1===e?( +// Fade out +m(i,{transition:"none",opacity:1}),i.offsetWidth,/* Repaint */ +setTimeout(function(){m(i,{transition:"all "+s+"ms linear",opacity:0}),setTimeout(function(){f.remove(),n()},s)},s)):setTimeout(n,s)}),this},f.isStarted=function(){return"number"==typeof f.status}, +/** + * Shows the progress bar. + * This is the same as setting the status to 0%, except that it doesn't go backwards. + * + * NProgress.start(); + * + */ +f.start=function(){f.status||f.set(0);var e=function(){setTimeout(function(){f.status&&(f.trickle(),e())},d.trickleSpeed)};return d.trickle&&e(),this}, +/** + * Hides the progress bar. + * This is the *sort of* the same as setting the status to 100%, with the + * difference being `done()` makes some placebo effect of some realistic motion. + * + * NProgress.done(); + * + * If `true` is passed, it will show the progress bar even if its hidden. + * + * NProgress.done(true); + */ +f.done=function(e){return e||f.status?f.inc(.3+.5*Math.random()).set(1):this}, +/** + * Increments by a random amount. + */ +f.inc=function(e){var t=f.status;return t?t>1?void 0:("number"!=typeof e&&(e=t>=0&&t<.2?.1:t>=.2&&t<.5?.04:t>=.5&&t<.8?.02:t>=.8&&t<.99?.005:0),t=n(t+e,0,.994),f.set(t)):f.start()},f.trickle=function(){return f.inc()}, +/** + * Waits for all supplied jQuery promises and + * increases the progress as the promises resolve. + * + * @param $promise jQUery Promise + */ +c=0,l=0,f.promise=function(e){return e&&"resolved"!==e.state()?(0===l&&f.start(),c++,l++,e.always(function(){l--,0===l?(c=0,f.done()):f.set((c-l)/c)}),this):this}, +/** + * (Internal) renders the progress bar markup based on the `template` + * setting. + */ +f.render=function(n){if(f.isRendered())return document.getElementById("nprogress");o(document.documentElement,"nprogress-busy");var r=document.createElement("div");r.id="nprogress",r.innerHTML=d.template;var i,s=r.querySelector(d.barSelector),a=n?"-100":t(f.status||0),c=e(d.parent)?d.parent:document.querySelector(d.parent);return m(s,{transition:"all 0 linear",transform:"translate3d("+a+"%,0,0)"}),d.showSpinner||(i=r.querySelector(d.spinnerSelector),i&&u(i)),c!=document.body&&o(c,"nprogress-custom-parent"),c.appendChild(r),r}, +/** + * Removes the element. Opposite of render(). + */ +f.remove=function(){s(document.documentElement,"nprogress-busy");var n=e(d.parent)?d.parent:document.querySelector(d.parent);s(n,"nprogress-custom-parent");var t=document.getElementById("nprogress");t&&u(t)}, +/** + * Checks if the progress bar is rendered. + */ +f.isRendered=function(){return!!document.getElementById("nprogress")}, +/** + * Determine which positioning CSS rule to use. + */ +f.getPositioningCSS=function(){ +// Sniff on document.body.style +var e=document.body.style,n="WebkitTransform"in e?"Webkit":"MozTransform"in e?"Moz":"msTransform"in e?"ms":"OTransform"in e?"O":""; +// Sniff prefixes +return n+"Perspective"in e?"translate3d":n+"Transform"in e?"translate":"margin"};var p=function(){function e(){var t=n.shift();t&&t(e)}var n=[];return function(t){n.push(t),1==n.length&&e()}}(),m=function(){function e(e){return e.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(e,n){return n.toUpperCase()})}function n(e){var n=document.body.style;if(e in n)return e;for(var t,r=i.length,o=e.charAt(0).toUpperCase()+e.slice(1);r--;)if(t=i[r]+o,t in n)return t;return e}function t(t){return t=e(t),o[t]||(o[t]=n(t))}function r(e,n,r){n=t(n),e.style[n]=r}var i=["Webkit","O","Moz","ms"],o={};return function(e,n){var t,i,o=arguments;if(2==o.length)for(t in n)i=n[t],void 0!==i&&n.hasOwnProperty(t)&&r(e,t,i);else r(e,o[1],o[2])}}(); +/** + * (Internal) Applies css properties to an element, similar to the jQuery + * css method. + * + * While this helper does assist with vendor prefixed property names, it + * does not perform any manipulation of values prior to setting styles. + */return f}); \ No newline at end of file diff --git a/scripts/vendor/waitMe.min.js b/scripts/vendor/waitMe.min.js new file mode 100644 index 00000000..fdb67289 --- /dev/null +++ b/scripts/vendor/waitMe.min.js @@ -0,0 +1,13 @@ +/* +waitMe - 1.19 [31.10.17] +Author: vadimsva +Github: https://github.com/vadimsva/waitMe +*/ +(function(b){b.fn.waitMe=function(p){return this.each(function(){function y(){var a=f.attr("data-waitme_id");f.removeClass("waitMe_container").removeAttr("data-waitme_id");f.find('.waitMe[data-waitme_id="'+a+'"]').remove()}var f=b(this),z,g,e,r=!1,t="background-color",u="",q="",v,a,w,n={init:function(){function n(a){l.css({top:"auto",transform:"translateY("+a+"px) translateZ(0)"})}a=b.extend({effect:"bounce",text:"",bg:"rgba(255,255,255,0.7)",color:"#000",maxSize:"",waitTime:-1,textPos:"vertical", +fontSize:"",source:"",onClose:function(){}},p);w=(new Date).getMilliseconds();v=b('
');switch(a.effect){case "none":e=0;break;case "bounce":e=3;break;case "rotateplane":e=1;break;case "stretch":e=5;break;case "orbit":e=2;r=!0;break;case "roundBounce":e=12;break;case "win8":e=5;r=!0;break;case "win8_linear":e=5;r=!0;break;case "ios":e=12;break;case "facebook":e=3;break;case "rotation":e=1;t="border-color";break;case "timer":e=2;var c=b.isArray(a.color)? +a.color[0]:a.color;u="border-color:"+c;break;case "pulse":e=1;t="border-color";break;case "progressBar":e=1;break;case "bouncePulse":e=3;break;case "img":e=1}""!==u&&(u+=";");if(0';else for(var d=1;d<=e;++d)b.isArray(a.color)?(c=a.color[d],void 0==c&&(c="#000")):c=a.color,q=r?q+('
'):q+('
');g=b('
'+q+"
")}a.text&&(c=b.isArray(a.color)?a.color[0]:a.color,z=b('
'+a.text+"
"));var k=f.find("> .waitMe");k&&k.remove();c=b('
');c.append(g,z);v.append(c);"HTML"==f[0].tagName&&(f=b("body"));f.addClass("waitMe_container").attr("data-waitme_id",w).append(v);k=f.find("> .waitMe");var l=f.find(".waitMe_content");k.css({background:a.bg}); +""!==a.maxSize&&"none"!=a.effect&&(c=g.outerHeight(),g.outerWidth(),"img"===a.effect?(g.css({height:a.maxSize+"px"}),g.find(">img").css({maxHeight:"100%"}),l.css({marginTop:-l.outerHeight()/2+"px"})):a.maxSize div").css({margin:"0 5%"})):(c=a.maxSize/c-.2,d="-50%","roundBounce"==a.effect?(d="-75%",a.text&&(d="75%")):"win8"==a.effect||"timer"==a.effect||"orbit"==a.effect?(d="-20%",a.text&&(d="20%")):"ios"==a.effect&& +(d="-15%",a.text&&(d="15%")),"rotation"==a.effect&&a.text&&(d="75%"),g.css({transform:"scale("+c+") translateX("+d+")",whiteSpace:"nowrap"}))));l.css({marginTop:-l.outerHeight()/2+"px"});if(f.outerHeight()>b(window).height()){c=b(window).scrollTop();var h=l.outerHeight(),m=f.offset().top,x=f.outerHeight();d=c-m+b(window).height()/2;0>d&&(d=Math.abs(d));0<=d-h&&d+h<=x?m-c>b(window).height()/2&&(d=h):d=c>m+x-h?c-m-h:c-m+h;n(d);b(document).scroll(function(){var a=b(window).scrollTop()-m+b(window).height()/ +2;0<=a-h&&a+h<=x&&n(a)})}0.waitMe_progress{display:inline-block;vertical-align:middle}.waitMe_container .waitMe .waitMe_content.horizontal>.waitMe_text{display:inline-block;margin:0 0 0 20px;vertical-align:middle}.waitMe_container .waitMe .waitMe_progress{position:relative;font-size:0;line-height:0}.waitMe_container .waitMe .waitMe_progress>div{animation-fill-mode:both;display:inline-block;transform:translateZ(0);backface-visibility:hidden}.waitMe_container .waitMe .waitMe_text{position:relative;margin:20px 0 0}body.waitMe_body{overflow:hidden;height:100%}body.waitMe_body.hideMe{transition:opacity .2s ease-in-out;opacity:0}body.waitMe_body .waitMe_container:not([data-waitme_id]){position:fixed;z-index:9989;top:0;bottom:0;left:0;right:0;background:#fff}body.waitMe_body .waitMe_container:not([data-waitme_id])>div{animation-fill-mode:both;position:absolute}body.waitMe_body .waitMe_container.progress>div{width:0;height:3px;top:0;left:0;background:#000;box-shadow:-5px 0 5px 2px rgba(0,0,0,.2);animation:progress_body 7s infinite ease-out}body.waitMe_body .waitMe_container.working>div{width:10%;height:3px;top:0;left:-10%;background:#000;box-shadow:-5px 0 5px 2px rgba(0,0,0,.2);animation:working_body 2s infinite linear}body.waitMe_body .waitMe_container.progress>div:after{content:'';position:absolute;top:0;bottom:60%;right:0;width:60px;border-radius:50%;opacity:.5;transform:rotate(3deg);box-shadow:#000 1px 0 6px 1px}body.waitMe_body .waitMe_container.img>div{width:100%;height:100%;text-align:center;background-position:center!important;background-repeat:no-repeat!important}body.waitMe_body .waitMe_container.text>div{width:100%;top:45%;text-align:center}@keyframes progress_body{0%{width:0}100%{width:100%}}@keyframes working_body{0%{left:-10%}100%{left:100%}}.waitMe_container .waitMe_progress.bounce>div{width:20px;height:20px;border-radius:50%;animation:bounce 1.4s infinite ease-in-out}.waitMe_container .waitMe_progress.bounce .waitMe_progress_elem1{animation-delay:-.32s}.waitMe_container .waitMe_progress.bounce .waitMe_progress_elem2{animation-delay:-.16s}@keyframes bounce{0%,100%,80%{transform:scale(0)}40%{transform:scale(1)}}.waitMe_container .waitMe_progress.rotateplane>div{width:30px;height:30px;animation:rotateplane 1.2s infinite ease-in-out;backface-visibility:visible}@keyframes rotateplane{0%{transform:perspective(120px)}50%{transform:perspective(120px) rotateY(180deg)}100%{transform:perspective(120px) rotateY(180deg) rotateX(180deg)}}.waitMe_container .waitMe_progress.stretch{height:60px}.waitMe_container .waitMe_progress.stretch>div{width:1px;height:100%;margin:0 4px;animation:stretch 1.2s infinite ease-in-out}.waitMe_container .waitMe_progress.stretch .waitMe_progress_elem2{animation-delay:-1.1s}.waitMe_container .waitMe_progress.stretch .waitMe_progress_elem3{animation-delay:-1s}.waitMe_container .waitMe_progress.stretch .waitMe_progress_elem4{animation-delay:-.9s}.waitMe_container .waitMe_progress.stretch .waitMe_progress_elem5{animation-delay:-.8s}@keyframes stretch{0%,100%,40%{transform:scaleY(.4)}20%{transform:scaleY(1)}}.waitMe_container .waitMe_progress.orbit{width:40px;height:40px;margin:auto;position:relative}.waitMe_container .waitMe_progress.orbit>div{width:100%;height:100%;margin:auto;top:0;left:0;position:absolute;animation:orbit_rotate 2s infinite linear}.waitMe_container .waitMe_progress.orbit>div>div{width:50%;height:50%;border-radius:50%;top:10%;left:10%;position:absolute;animation:orbit 2s infinite ease-in-out}.waitMe_container .waitMe_progress.orbit .waitMe_progress_elem2>div{top:auto;bottom:10%;left:auto;right:10%;animation-delay:-1s}@keyframes orbit_rotate{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes orbit{0%,100%{transform:scale(0)}50%{transform:scale(1)}}.waitMe_container .waitMe_progress.roundBounce{width:60px;height:60px;margin:auto}.waitMe_container .waitMe_progress.roundBounce>div{width:24%;height:24%;border-radius:50%;position:absolute;animation:roundBounce 1.2s infinite ease-in-out}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem1{top:0;left:0}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem2{top:0;right:0}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem3{bottom:0;right:0}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem4{bottom:0;left:0}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem5{top:-3%;left:50%;margin-top:-12%;margin-left:-12%;animation-delay:-1.1s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem6{top:50%;right:-3%;margin-top:-12%;margin-right:-12%}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem7{bottom:-3%;left:50%;margin-bottom:-12%;margin-left:-12%}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem8{top:50%;left:-3%;margin-top:-12%;margin-left:-12%}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem9{top:0;right:0;animation-delay:-1s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem2{animation-delay:-.9s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem6{animation-delay:-.8s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem10{bottom:0;right:0;animation-delay:-.7s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem3{animation-delay:-.6s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem7{animation-delay:-.5s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem11{bottom:0;left:0;animation-delay:-.4s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem4{animation-delay:-.3s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem8{animation-delay:-.2s}.waitMe_container .waitMe_progress.roundBounce .waitMe_progress_elem12{top:0;left:0;animation-delay:-.1s}@keyframes roundBounce{0%,100%,80%{transform:scale(0)}40%{transform:scale(1)}}.waitMe_container .waitMe_progress.win8{width:40px;height:40px;margin:auto}.waitMe_container .waitMe_progress.win8>div{width:100%;height:100%;opacity:0;position:absolute;margin:auto;left:0;right:0;transform:rotate(225deg);animation:win8 5.5s infinite}.waitMe_container .waitMe_progress.win8>div>div{width:15%;height:15%;border-radius:50%;position:absolute}.waitMe_container .waitMe_progress.win8 .waitMe_progress_elem2{animation-delay:.24s}.waitMe_container .waitMe_progress.win8 .waitMe_progress_elem3{animation-delay:.48s}.waitMe_container .waitMe_progress.win8 .waitMe_progress_elem4{animation-delay:.72s}.waitMe_container .waitMe_progress.win8 .waitMe_progress_elem5{animation-delay:.96s}@keyframes win8{0%{transform:rotate(225deg);animation-timing-function:ease-out}7%{opacity:1;transform:rotate(345deg);animation-timing-function:linear}30%{transform:rotate(455deg);animation-timing-function:ease-in-out}39%{transform:rotate(690deg);animation-timing-function:linear}70%{opacity:1;transform:rotate(815deg);animation-timing-function:ease-out}75%{transform:rotate(945deg);animation-timing-function:ease-out}100%,76%{opacity:0;transform:rotate(945deg)}}.waitMe_container .waitMe_progress.win8_linear{margin:auto;width:150px;height:6px}.waitMe_container .waitMe_progress.win8_linear>div{width:100%;height:100%;left:0;opacity:0;position:absolute;animation:win8_linear 3s infinite}.waitMe_container .waitMe_progress.win8_linear>div>div{width:4%;height:100%;border-radius:50%}.waitMe_container .waitMe_progress.win8_linear .waitMe_progress_elem2{animation-delay:.3s}.waitMe_container .waitMe_progress.win8_linear .waitMe_progress_elem3{animation-delay:.6s}.waitMe_container .waitMe_progress.win8_linear .waitMe_progress_elem4{animation-delay:.9s}.waitMe_container .waitMe_progress.win8_linear .waitMe_progress_elem5{animation-delay:1.2s}@keyframes win8_linear{0%,100%{transform:translateX(0);animation-timing-function:ease-out}10%{opacity:1;transform:translateX(33.333%);animation-timing-function:linear}50%{opacity:1;transform:translateX(53.333%);animation-timing-function:ease-in-out}60%{opacity:0;transform:translateX(86.666%)}}.waitMe_container .waitMe_progress.ios{margin:auto;width:40px;height:40px}.waitMe_container .waitMe_progress.ios>div{width:10%;height:26%;position:absolute;left:44.5%;top:37%;opacity:0;border-radius:50px;box-shadow:0 0 3px rgba(0,0,0,.2);animation:ios 1s infinite linear}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem1{transform:rotate(0) translate(0,-142%);animation-delay:0s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem2{transform:rotate(30deg) translate(0,-142%);animation-delay:-.9167s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem3{transform:rotate(60deg) translate(0,-142%);animation-delay:-.833s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem4{transform:rotate(90deg) translate(0,-142%);animation-delay:-.75s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem5{transform:rotate(120deg) translate(0,-142%);animation-delay:-.667s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem6{transform:rotate(150deg) translate(0,-142%);animation-delay:-.5833s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem7{transform:rotate(180deg) translate(0,-142%);animation-delay:-.5s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem8{transform:rotate(210deg) translate(0,-142%);animation-delay:-.41667s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem9{transform:rotate(240deg) translate(0,-142%);animation-delay:-.333s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem10{transform:rotate(270deg) translate(0,-142%);animation-delay:-.25s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem11{transform:rotate(300deg) translate(0,-142%);animation-delay:-.1667s}.waitMe_container .waitMe_progress.ios .waitMe_progress_elem12{transform:rotate(330deg) translate(0,-142%);animation-delay:-.0833s}@keyframes ios{0%{opacity:1}100%{opacity:.25}}.waitMe_container .waitMe_progress.facebook{margin:auto}.waitMe_container .waitMe_progress.facebook>div{width:6px;height:25px;margin-left:3px;border-radius:20px;transform:scaleY(.7);opacity:.1;animation:facebook 1.3s infinite ease-in-out}.waitMe_container .waitMe_progress.facebook>.waitMe_progress_elem1{animation-delay:.2s;transform:scaleY(.7)}.waitMe_container .waitMe_progress.facebook>.waitMe_progress_elem2{animation-delay:.4s;transform:scaleY(.85)}.waitMe_container .waitMe_progress.facebook>.waitMe_progress_elem3{animation-delay:.6s;transform:scaleY(1)}@keyframes facebook{0%,100%{transform:scaleY(.7);opacity:.1}50%{transform:scaleY(1);opacity:1}}.waitMe_container .waitMe_progress.rotation>div{width:60px;height:60px;margin:auto;border-radius:100%;border-width:6px;border-style:solid;border-left-color:transparent!important;border-right-color:transparent!important;border-bottom-color:transparent!important;animation:rotation 1s infinite linear}@keyframes rotation{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.waitMe_container .waitMe_progress.timer{width:40px;height:40px;margin:auto;border-width:2px;border-style:solid;border-radius:50%;box-sizing:border-box;position:relative;text-indent:-9999px}.waitMe_container .waitMe_progress.timer>.waitMe_progress_elem1,.waitMe_container .waitMe_progress.timer>.waitMe_progress_elem2{border-radius:3px;position:absolute;width:2px;height:48%;left:50%;top:50%;margin-left:-1px;margin-top:-1px;animation:timer 1.25s infinite linear;transform-origin:1px 1px}.waitMe_container .waitMe_progress.timer>.waitMe_progress_elem2{height:40%;animation:timer 15s infinite linear}@keyframes timer{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.waitMe_container .waitMe_progress.pulse{width:30px;height:30px;margin:auto;position:relative}.waitMe_container .waitMe_progress.pulse>div{margin:auto;top:0;left:0;right:0;bottom:0;border-width:3px;border-style:solid;border-radius:50%;position:absolute;opacity:0;animation:pulsate 1s infinite ease-out}@keyframes pulsate{0%{transform:scale(.1);opacity:0}50%{opacity:1}100%{transform:scale(1.2);opacity:0}}.waitMe_container .waitMe_progress.progressBar{width:200px;height:20px;margin:auto;background:rgba(0,0,0,.1);padding:5px;border-radius:20px;line-height:0;max-width:100%}.waitMe_container .waitMe_progress.progressBar>div{width:100%;height:100%;overflow:hidden;border-radius:20px;background-size:50px 50px;box-shadow:inset 0 2px 9px rgba(255,255,255,.3),inset 0 -2px 6px rgba(0,0,0,.4);background-image:linear-gradient(-45deg,rgba(240,240,240,.4) 26%,transparent 25%,transparent 51%,rgba(240,240,240,.4) 50%,rgba(240,240,240,.4) 76%,transparent 75%,transparent);animation:progressBar 2s linear infinite}@keyframes progressBar{0%{background-position:0 0}100%{background-position:50px 50px}}.waitMe_container .waitMe_progress.bouncePulse>div{width:20px;height:20px;margin-right:1px;display:inline-block;border-radius:50%;transform:scale(.5);animation:bouncePulse 1.4s infinite ease-in-out}.waitMe_container .waitMe_progress.bouncePulse>.waitMe_progress_elem1,.waitMe_container .waitMe_progress.bouncePulse>.waitMe_progress_elem3{animation-delay:.1s}.waitMe_container .waitMe_progress.bouncePulse>.waitMe_progress_elem2{animation-delay:.4s}@keyframes bouncePulse{0%,100%,80%{transform:scale(.5)}40%{transform:scale(1)}} From 0d35ad2cafe00235fe5e352a37a968bce52e09c2 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 14 Nov 2023 10:11:21 +0100 Subject: [PATCH 2/3] Remove progress bar on general page loading Signed-off-by: DL6ER --- scripts/pi-hole/js/footer.js | 7 +------ scripts/pi-hole/lua/footer.lp | 6 ------ 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/scripts/pi-hole/js/footer.js b/scripts/pi-hole/js/footer.js index 1fd91282..380bc485 100644 --- a/scripts/pi-hole/js/footer.js +++ b/scripts/pi-hole/js/footer.js @@ -5,7 +5,7 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ -/* global utils:false, moment:false, NProgress: false */ +/* global utils:false, moment:false */ //The following functions allow us to display time until pi-hole is enabled after disabling. //Works between all pages @@ -712,8 +712,3 @@ if ($.fn.dataTable) { console.log("DataTables warning: " + message); }; } - -// Page has finished loading -$(window).on("load", function () { - NProgress.done(); -}); diff --git a/scripts/pi-hole/lua/footer.lp b/scripts/pi-hole/lua/footer.lp index 8e9a4fbc..3be0b1d9 100644 --- a/scripts/pi-hole/lua/footer.lp +++ b/scripts/pi-hole/lua/footer.lp @@ -61,10 +61,4 @@ - From afacc21b1c9f71ec943a7837c1c7224bec4b90d7 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 14 Nov 2023 22:16:59 +0100 Subject: [PATCH 3/3] Dynamically determine when FTL finished restarting by pinging it every 0.5 seconds (after an initial delay of 2 seconds) Signed-off-by: DL6ER --- scripts/pi-hole/js/settings.js | 2 +- scripts/pi-hole/js/utils.js | 51 ++++++++++++++++------------------ 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index 0b16d685..8342fec6 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -176,7 +176,7 @@ function saveSettings() { "" ); // Show loading overlay - utils.loadingOverlay(1000, true); + utils.loadingOverlay(true); }) .fail(function (data) { apiFailure(data); diff --git a/scripts/pi-hole/js/utils.js b/scripts/pi-hole/js/utils.js index 7fe17860..d3562c04 100644 --- a/scripts/pi-hole/js/utils.js +++ b/scripts/pi-hole/js/utils.js @@ -615,34 +615,31 @@ function listAlert(type, items, data) { ); } -var loadingTime = 0; // Callback function for the loading overlay timeout -function loadingOverlayTimeoutCallback(loadingTimeout, reloadAfterTimeout) { - if (loadingTime <= loadingTimeout) { - // Update progress bar - NProgress.set(loadingTime / loadingTimeout); - // Increase loading time - loadingTime += 100; - // Schedule next update - setTimeout(loadingOverlayTimeoutCallback, 100, loadingTimeout, reloadAfterTimeout); - return; - } - - // Hide loading overlay - NProgress.done(); - - // Reload page if requested - if (reloadAfterTimeout) { - location.reload(); - } +function loadingOverlayTimeoutCallback(reloadAfterTimeout) { + // Try to ping FTL to see if it finished restarting + $.ajax({ + url: "/api/info/login", + method: "GET", + cache: false, + dataType: "json", + }) + .done(function () { + // FTL is running again, hide loading overlay + NProgress.done(); + if (reloadAfterTimeout) { + location.reload(); + } else { + $(".wrapper").waitMe("hide"); + } + }) + .fail(function () { + // FTL is not running yet, try again in 500ms + setTimeout(loadingOverlayTimeoutCallback, 500, reloadAfterTimeout); + }); } -function loadingOverlay(timeout = 6000, reloadAfterTimeout = false) { - // Add three extra seconds to the timeout as safety margin - // timeout is specified in milliseconds - timeout += 3000; - - NProgress.configure({ minimum: 0, trickle: false }); +function loadingOverlay(reloadAfterTimeout = false) { NProgress.start(); $(".wrapper").waitMe({ effect: "bounce", @@ -650,10 +647,10 @@ function loadingOverlay(timeout = 6000, reloadAfterTimeout = false) { bg: "rgba(0,0,0,0.7)", color: "#fff", maxSize: "", - waitTime: timeout, textPos: "vertical", }); - setTimeout(loadingOverlayTimeoutCallback, 100, timeout, reloadAfterTimeout); + // Start checking for FTL status after 2 seconds + setTimeout(loadingOverlayTimeoutCallback, 2000, reloadAfterTimeout); return true; }