118 lines
3.0 KiB
JavaScript
118 lines
3.0 KiB
JavaScript
(function () {
|
|||
|
|
const STORAGE_KEY = "lapp.pendingItemUpdates";
|
||
|
|
|
||
|
|
function readQueue() {
|
||
|
|
try {
|
||
|
|
return JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
|
||
|
|
} catch (error) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function writeQueue(queue) {
|
||
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(queue));
|
||
|
|
}
|
||
|
|
|
||
|
|
function queueRequest(request) {
|
||
|
|
const queue = readQueue();
|
||
|
|
const queuedRequest = {
|
||
|
|
url: request.url,
|
||
|
|
method: request.method || "GET",
|
||
|
|
coalesceKey: request.coalesceKey || null,
|
||
|
|
clientUpdatedAt: request.clientUpdatedAt || Date.now()
|
||
|
|
};
|
||
|
|
|
||
|
|
if (queuedRequest.coalesceKey) {
|
||
|
|
const existingIndex = queue.findIndex(function (item) {
|
||
|
|
return item.coalesceKey === queuedRequest.coalesceKey;
|
||
|
|
});
|
||
|
|
if (existingIndex >= 0) {
|
||
|
|
queue[existingIndex] = queuedRequest;
|
||
|
|
} else {
|
||
|
|
queue.push(queuedRequest);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
queue.push(queuedRequest);
|
||
|
|
}
|
||
|
|
|
||
|
|
writeQueue(queue);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function sendRequest(request) {
|
||
|
|
const url = new URL(request.url, window.location.origin);
|
||
|
|
if (request.clientUpdatedAt) {
|
||
|
|
url.searchParams.set("client_updated_at", request.clientUpdatedAt);
|
||
|
|
}
|
||
|
|
|
||
|
|
const response = await fetch(url.toString(), {
|
||
|
|
method: request.method || "GET",
|
||
|
|
cache: "no-store",
|
||
|
|
credentials: "same-origin"
|
||
|
|
});
|
||
|
|
|
||
|
|
if (response.redirected && new URL(response.url).pathname === "/login") {
|
||
|
|
const error = new Error("Login required");
|
||
|
|
error.retryable = true;
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!response.ok) {
|
||
|
|
const error = new Error("Request failed: " + response.status);
|
||
|
|
error.retryable = response.status >= 500 || response.status === 408 || response.status === 429;
|
||
|
|
throw error;
|
||
|
|
}
|
||
|
|
return response;
|
||
|
|
}
|
||
|
|
|
||
|
|
async function queueableItemRequest(url, options) {
|
||
|
|
const request = Object.assign({}, options || {}, {
|
||
|
|
url: url,
|
||
|
|
clientUpdatedAt: Date.now()
|
||
|
|
});
|
||
|
|
try {
|
||
|
|
await sendRequest(request);
|
||
|
|
flushItemUpdateQueue();
|
||
|
|
return true;
|
||
|
|
} catch (error) {
|
||
|
|
if (error.retryable !== false) {
|
||
|
|
queueRequest(request);
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function flushItemUpdateQueue() {
|
||
|
|
if (flushItemUpdateQueue.running) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
flushItemUpdateQueue.running = true;
|
||
|
|
|
||
|
|
const queue = readQueue();
|
||
|
|
const remaining = [];
|
||
|
|
|
||
|
|
for (const request of queue) {
|
||
|
|
try {
|
||
|
|
await sendRequest(request);
|
||
|
|
} catch (error) {
|
||
|
|
if (error.retryable !== false) {
|
||
|
|
remaining.push(request);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
writeQueue(remaining);
|
||
|
|
flushItemUpdateQueue.running = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
window.lappQueueItemRequest = queueableItemRequest;
|
||
|
|
window.lappFlushItemUpdateQueue = flushItemUpdateQueue;
|
||
|
|
|
||
|
|
window.addEventListener("online", flushItemUpdateQueue);
|
||
|
|
document.addEventListener("visibilitychange", function () {
|
||
|
|
if (!document.hidden) {
|
||
|
|
flushItemUpdateQueue();
|
||
|
|
}
|
||
|
|
});
|
||
|
|
document.addEventListener("DOMContentLoaded", flushItemUpdateQueue);
|
||
|
|
})();
|