From 6437c7eb4278eb56cee930bef2e0d3b348a596f8 Mon Sep 17 00:00:00 2001 From: Martchus Date: Sat, 12 Mar 2022 18:53:46 +0100 Subject: [PATCH] Avoid concurrent AJAX requests and show loading indication --- srv/static/css/layout.css | 36 ++++++++++++++++++++++++++++- srv/static/js/ajaxhelper.js | 30 ++++++++++++++++++++++-- srv/static/js/buildactionspage.js | 4 ++-- srv/static/js/globalstatuspage.js | 2 +- srv/static/js/packagedetailspage.js | 4 ++-- 5 files changed, 68 insertions(+), 8 deletions(-) diff --git a/srv/static/css/layout.css b/srv/static/css/layout.css index 253c19e..681d7af 100644 --- a/srv/static/css/layout.css +++ b/srv/static/css/layout.css @@ -48,6 +48,7 @@ header nav li { } header nav ul li a, header nav ul li a:link, header nav ul li a:visited { display: inline-block; + position: relative; box-sizing: border-box; text-decoration: none; padding-left: 20px; @@ -62,6 +63,39 @@ header nav ul li a:hover, header nav ul li.active a { color: #fff; text-decoration: none; } +header nav ul li.progress a:after { + content: ''; + box-sizing: border-box; + position: absolute; + left: 0px; + bottom: 0px; + height: 5px; + width: 100%; + background: #08c; + animation: loading 2s infinite; +} +@keyframes loading { + 0% { + margin-left: 0%; + width: 30%; + } + 15% { + margin-left: 35%; + width: 50%; + } + 50% { + margin-left: 70%; + width: 30%; + } + 65% { + margin-left: 15%; + width: 50%; + } + 100% { + margin-left: 0%; + width: 30%; + } +} header nav li.active { background-color: #000; } @@ -99,4 +133,4 @@ main { section { background-color: white; padding: 10px; -} \ No newline at end of file +} diff --git a/srv/static/js/ajaxhelper.js b/srv/static/js/ajaxhelper.js index cdb3798..27040a7 100644 --- a/srv/static/js/ajaxhelper.js +++ b/srv/static/js/ajaxhelper.js @@ -1,12 +1,34 @@ export const apiPrefix = 'api/v0'; let authError = false; +let ongoingRequests = {}; /// \brief Makes an AJAX query with basic error handling. -export function queryRoute(method, path, callback) +export function queryRoute(method, path, callback, type) { + if (type) { + const ongoingRequest = ongoingRequests[type]; + if (ongoingRequest) { + ongoingRequest.abort(); + } + const navElement = document.getElementById(type + '-nav-link'); + if (navElement) { + navElement.classList.add('progress'); + } + } + const ajaxRequest = new XMLHttpRequest(); ajaxRequest.onreadystatechange = function() { if (this.readyState === 4) { + if (type) { + if (ongoingRequests[type] !== ajaxRequest) { + return; + } + delete ongoingRequests[type]; + const navElement = document.getElementById(type + '-nav-link'); + if (navElement) { + navElement.classList.remove('progress'); + } + } const status = this.status; authError = status === 403; switch (status) { @@ -30,6 +52,9 @@ export function queryRoute(method, path, callback) } ajaxRequest.open(...args); ajaxRequest.send(); + if (type) { + ongoingRequests[type] = ajaxRequest; + } return ajaxRequest; } @@ -44,8 +69,9 @@ export function startFormQueryEx(formId, handler) { const form = document.getElementById(formId); const params = makeFormQueryParameter(form); + const queryType = formId.endsWith('-form') ? formId.substr(0, formId.length - 5) : formId; return { - ajaxRequest: queryRoute(form.method, form.getAttribute('action') + params, handler), + ajaxRequest: queryRoute(form.method, form.getAttribute('action') + params, handler, queryType), form: form, params, params, }; diff --git a/srv/static/js/buildactionspage.js b/srv/static/js/buildactionspage.js index 7523d92..235b8f3 100644 --- a/srv/static/js/buildactionspage.js +++ b/srv/static/js/buildactionspage.js @@ -34,13 +34,13 @@ export function initBuildActionsForm() function queryBuildActions() { - AjaxHelper.queryRoute('GET', '/build-action', showBuildActions); + AjaxHelper.queryRoute('GET', '/build-action', showBuildActions, 'build-action'); return true; } function queryBuildActionDetails(ids) { - AjaxHelper.queryRoute('GET', '/build-action/details?' + AjaxHelper.makeIdParams(ids), showBuildActionDetails); + AjaxHelper.queryRoute('GET', '/build-action/details?' + AjaxHelper.makeIdParams(ids), showBuildActionDetails, 'build-action-details'); return true; } diff --git a/srv/static/js/globalstatuspage.js b/srv/static/js/globalstatuspage.js index 3f2f632..ec07cae 100644 --- a/srv/static/js/globalstatuspage.js +++ b/srv/static/js/globalstatuspage.js @@ -8,7 +8,7 @@ const status = {repoNames: undefined}; export function queryGlobalStatus() { - AjaxHelper.queryRoute('GET', '/status', handleGlobalStatusUpdate); + AjaxHelper.queryRoute('GET', '/status', handleGlobalStatusUpdate, 'global'); return true; } diff --git a/srv/static/js/packagedetailspage.js b/srv/static/js/packagedetailspage.js index cbe069d..187bc22 100644 --- a/srv/static/js/packagedetailspage.js +++ b/srv/static/js/packagedetailspage.js @@ -25,7 +25,7 @@ export function initPackageDetails(sectionElement, sectionData, newPackages) }; AjaxHelper.queryRoute('GET', '/packages?details=1&name=' + encodeURIComponent(packageStr), function(ajaxRequest) { showPackageDetails(ajaxRequest, packageObj); - }); + }, 'package-details'); return true; } @@ -38,7 +38,7 @@ export function queryPackageDetails(value, row) { AjaxHelper.queryRoute('GET', '/packages?details=1&name=' + encodeURIComponent(makePackageID(row)), function(ajaxRequest) { showPackageDetails(ajaxRequest, row); - }); + }, 'package-details'); } function switchToPackageDetails(packageID)