feat: Introduce queue based live stat refresh (#1059)

This commit is contained in:
Attila Kerekes 2022-12-05 15:29:06 +00:00 committed by GitHub
parent 6b93f8ed5c
commit 11257a272e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 84 deletions

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
{
"/css/app.css": "/css/app.css?id=910562b0b11f9731ff1cdded5e57f7b5",
"/js/app.js": "/js/app.js?id=455ff98b9cf4ee09f73e8521270cea96"
"/js/app.js": "/js/app.js?id=86f896ef6c2066036a4e2b946e07750d"
}

View file

@ -19,88 +19,6 @@ $.when($.ready).then(() => {
}, 3500);
}
// from https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API
// Set the name of the hidden property and the change event for visibility
let hidden;
let visibilityChange;
if (typeof document.hidden !== "undefined") {
// Opera 12.10 and Firefox 18 and later support
hidden = "hidden";
visibilityChange = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
const livestatsRefreshTimeouts = [];
const livestatsFuncs = [];
const livestatsContainers = $(".livestats-container");
function stopLivestatsRefresh() {
livestatsRefreshTimeouts.forEach((timeoutId) => {
window.clearTimeout(timeoutId);
});
}
function startLivestatsRefresh() {
livestatsFuncs.forEach((fun) => {
fun();
});
}
if (livestatsContainers.length > 0) {
if (
typeof document.addEventListener === "undefined" ||
hidden === undefined
) {
console.log("This browser does not support visibilityChange");
} else {
document.addEventListener(
visibilityChange,
() => {
if (document[hidden]) {
stopLivestatsRefresh();
} else {
startLivestatsRefresh();
}
},
false
);
}
livestatsContainers.each(function (index) {
const id = $(this).data("id");
const dataonly = $(this).data("dataonly");
const increaseby = dataonly === 1 ? 20000 : 1000;
const container = $(this);
const maxTimer = 30000;
let timer = 5000;
const fun = function worker() {
$.ajax({
url: `${base}get_stats/${id}`,
dataType: "json",
success(data) {
container.html(data.html);
if (data.status === "active") timer = increaseby;
else if (timer < maxTimer) timer += 2000;
},
complete(jqXHR) {
if (jqXHR.status > 299) {
// Stop polling when we get errors
return;
}
// Schedule the next request when the current one's complete
livestatsRefreshTimeouts[index] = window.setTimeout(worker, timer);
},
});
};
livestatsFuncs[index] = fun;
fun();
});
}
function readURL(input) {
if (input.files && input.files[0]) {
const reader = new FileReader();

100
resources/assets/js/liveStatRefresh.js vendored Normal file
View file

@ -0,0 +1,100 @@
const REFRESH_INTERVAL_SMALL = 5000;
const REFRESH_INTERVAL_BIG = 30000;
const QUEUE_PROCESSING_INTERVAL = 1000;
const CONTAINER_SELECTOR = ".livestats-container";
/**
* @returns {*[]}
*/
function createQueue() {
const queue = [];
let suspended = false;
function processQueue() {
if (queue.length === 0 || suspended === true) {
return;
}
const next = queue.shift();
next();
}
document.addEventListener("visibilitychange", () => {
suspended = document.hidden;
});
setInterval(processQueue, QUEUE_PROCESSING_INTERVAL);
return queue;
}
/**
* @returns {NodeListOf<Element>}
*/
function getContainers() {
return document.querySelectorAll(CONTAINER_SELECTOR);
}
/**
*
* @param {boolean} dataOnly
* @param {boolean} active
* @returns {number}
*/
function getQueueInterval(dataOnly, active) {
if (dataOnly) {
return REFRESH_INTERVAL_BIG;
}
if (active) {
return REFRESH_INTERVAL_SMALL;
}
return REFRESH_INTERVAL_BIG;
}
/**
* @param {HTMLElement} container
* @param {array} queue
* @returns {function(): Promise<Response>}
*/
function createUpdateJob(container, queue) {
const id = container.getAttribute("data-id");
// Data only attribute seems to indicate that the item should not be updated that often
const isDataOnly = container.getAttribute("data-dataonly") === "1";
return () =>
fetch(`get_stats/${id}`)
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error(`Network response was not ok: ${response.status}`);
})
.then((data) => {
// eslint-disable-next-line no-param-reassign
container.innerHTML = data.html;
const isActive = data.status === "active";
if (queue) {
setTimeout(() => {
queue.push(createUpdateJob(container, queue));
}, getQueueInterval(isDataOnly, isActive));
}
})
.catch((error) => {
console.error(error);
});
}
const livestatContainers = getContainers();
if (livestatContainers.length > 0) {
const myQueue = createQueue();
livestatContainers.forEach((container) => {
createUpdateJob(container, myQueue)();
});
}

1
webpack.mix.js vendored
View file

@ -20,6 +20,7 @@ mix
"resources/assets/js/keyBindings.js",
"resources/assets/js/itemExport.js",
"resources/assets/js/itemImport.js",
"resources/assets/js/liveStatRefresh.js",
],
"public/js/app.js"
)