From c06013234d16ab3889bd7fce09f6606b45fd2b9f Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 01 Jan 2026 23:38:14 +0000
Subject: [PATCH] Merge branch 'main' of https://github.com/jakevdwerf/jvb
---
assets/js/concise/Queue.js | 153 ++++++++++++++++++++++++--------------------------
1 files changed, 73 insertions(+), 80 deletions(-)
diff --git a/assets/js/concise/Queue.js b/assets/js/concise/Queue.js
index 4590141..7e3033c 100644
--- a/assets/js/concise/Queue.js
+++ b/assets/js/concise/Queue.js
@@ -87,36 +87,25 @@
}
async initQueue() {
- const incomplete = this.getOperationsByStatus(['completed', 'failed_permanent'], false)
-
- if (incomplete.length > 0) {
- this.startPolling();
- } else {
+ let polling = this.maybeStartPolling();
+ if (!polling) {
this.updateStatusPanel('synced');
}
+
this.store.subscribe((event, data) => {
switch (event) {
case 'data-loaded':
case 'items-saved':
- // Initial load from IndexedDB
- const incomplete = this.getOperationsByStatus(['completed', 'failed_permanent'], false);
- if (incomplete.length > 0) {
- this.startPolling();
- }
+ this.maybeStartPolling();
this.updateUI();
break;
case 'item-saved':
- // Check for status changes
- if (data.item) {
- const oldItem = this.store.data.get(data.item.id);
- if (oldItem && oldItem.status !== data.item.status) {
- this.handleOperationStatusChange(data.item, oldItem.status);
- }
+ console.log(data,'Item saved data');
+ if (data.previousItem && data.previousItem.status !== data.item.status) {
+ this.handleOperationStatusChange(data.item, data.previousItem.status);
}
- if (this.hasQueuedOperations()) {
- this.startPolling();
- }
+ this.maybeStartPolling();
break;
default:
this.updateUI();
@@ -124,18 +113,27 @@
}
});
- this.notify('queue-initialized', {operations: incomplete});
+ }
+
+ maybeStartPolling()
+ {
+ const incomplete = this.getOperationsByStatus(['completed', 'failed_permanent'], false);
+ if (incomplete.length > 0) {
+ this.startPolling();
+ return true;
+ }
+ return false;
}
/**
* Handle operation status changes and notify subscribers
*/
- handleOperationStatusChange(operation, oldStatus) {
- if (!operation || oldStatus === operation.status) return;
+ handleOperationStatusChange(operation) {
// Notify based on new status
switch(operation.status) {
case 'completed':
+ console.log(operation);
this.notify('operation-completed', operation);
break;
case 'failed':
@@ -165,6 +163,7 @@
method: 'POST',
headers: {},
data: {},
+ sendNow: false, // true = process immediately
canMerge: true,
popup: 'Saving changes...',
title: 'Operation',
@@ -185,6 +184,14 @@
return null;
}
+ if (item.sendNow) {
+ this.processOperation(item).then(()=> {});
+ this.store.clearCache();
+ window.debouncer.schedule('fastQueue', this.startPolling.bind(this), 200);
+ this.showQueue();
+ return item.id;
+ }
+
const existingOps = Array.from(this.store.data.values()).filter(op=>
op.status === 'queued' &&
op.endpoint === item.endpoint &&
@@ -203,7 +210,6 @@
return existing.id;
}
- console.log('Added to Queue: ', item);
this.store.clearCache();
//Add new operation to DataStore
@@ -239,7 +245,6 @@
}
clearQueue(itemID) {
- const item = this.store.get(itemID);
this.store.delete(itemID);
}
@@ -256,8 +261,6 @@
}
resetActivityTimer() {
- this.lastActivity = Date.now();
-
if (this.activityTimer) {
clearTimeout(this.activityTimer);
}
@@ -315,21 +318,17 @@
this.setProcessing(false);
this.stopActivityTracking();
- const pending = this.getOperationsByStatus(['queued', 'completed', 'failed_permanent'], false);
- if (pending.length > 0) {
- this.startPolling();
- this.showQueue();
- } else {
- this.hideQueue();
- }
+ this.maybeStartPolling() ? this.showQueue() : this.hideQueue();
}
- async processOperation(operation) {
+ async processOperation(operation, skip = false) {
try {
- this.updateOperationStatus(operation.id, 'uploading');
+ if (!skip) {
+ this.updateOperationStatus(operation.id, 'uploading');
- if (operation.data?._isFormData) {
- operation.data = await this.store.objectToFormData(operation.data);
+ if (operation.data?._isFormData) {
+ operation.data = await this.store.objectToFormData(operation.data);
+ }
}
const url = `${this.config.apiBase}${operation.endpoint}`;
@@ -347,7 +346,6 @@
});
operation.headers['Content-Type'] = 'application/json';
}
-
const response = await fetch(url, {
method: operation.method,
headers: operation.headers,
@@ -355,43 +353,17 @@
});
const result = await response.json();
-
+ if (skip) {
+ operation.data = {};
+ }
if (response.ok && result.success !== false) {
// Handle server-side merge
if (result.id && operation.id !== result.id) {
-
- // Check if the returned ID exists locally
- const existingOp = this.getQueue(result.id);
-
- if (existingOp) {
- // Merge data from both operations
- existingOp.data = window.deepMerge(existingOp.data, operation.data);
- existingOp.status = result.status || 'pending';
- existingOp.serverData = result;
- this.updateOperationStatus(existingOp.id, existingOp.status);
- // Update the existing operation
- this.setQueue(existingOp);
-
- this.removeOperationFromUI(operation.id);
-
- // Switch reference to the merged operation
- operation = existingOp;
- } else {
- // Server merged with an operation we don't have locally
- // Update the ID and continue
- this.clearQueue(operation.id);
- operation.id = result.id;
- operation.status = result.status || 'pending';
- operation.serverData = result;
- this.updateOperationStatus(operation.id, operation.status);
- this.setQueue(operation);
- }
+ operation = await this.handleServerMerge(operation, result);
} else {
- // Normal processing - no merge
operation.status = result.status || 'pending';
operation.serverData = result;
this.updateOperationStatus(operation.id, operation.status);
- this.setQueue(operation);
}
this.a11y.announce(`${operation.title} sent to server for processing.`);
@@ -417,6 +389,29 @@
}
}
+ async handleServerMerge(operation, result) {
+ const existingOp = this.getQueue(result.id);
+
+ if (existingOp) {
+ // Merge with existing local operation
+ existingOp.data = window.deepMerge(existingOp.data, operation.data);
+ existingOp.status = result.status || 'pending';
+ existingOp.serverData = result;
+ this.updateOperationStatus(existingOp.id, existingOp.status);
+ this.removeOperationFromUI(operation.id);
+ this.clearQueue(operation.id);
+ return existingOp;
+ } else {
+ // Server merged with unknown operation
+ this.clearQueue(operation.id);
+ operation.id = result.id;
+ operation.status = result.status || 'pending';
+ operation.serverData = result;
+ this.updateOperationStatus(operation.id, operation.status);
+ return operation;
+ }
+ }
+
startPolling() {
if (this.isPolling) return;
@@ -428,8 +423,7 @@
this.store.clearCache();
await this.store.fetch(); // Fetches from server, updates store.data
- const incomplete = this.getOperationsByStatus(['completed', 'failed_permanent'], false);
- if (incomplete.length === 0) {
+ if (!this.maybeStartPolling()) {
this.stopPolling();
this.updateStatusPanel('synced');
}
@@ -451,7 +445,9 @@
this.countdownTimer = null;
}
}
-
+ getOperationIds(operations) {
+ return operations.map(op => op.id);
+ }
/***********************************************************
USER ACTIONS
***********************************************************/
@@ -560,10 +556,9 @@
};
this.handleOffline = () => this.updateStatusPanel('offline');
this.handleBeforeUnload = (e) => {
- const hasPending = this.getOperationsByStatus(['queued', 'uploading']);
- if (hasPending.length > 0) {
+ if (this.isPolling || this.isProcessing) {
e.preventDefault();
- return 'You have unsaved changes in the queue.';
+ return 'You have unsaved changes in the queue. Proceed?';
}
};
@@ -580,16 +575,14 @@
this.store.clearHttpHeaders(); // Clear cached headers first
this.store.fetch();
} else if (e.target.closest(this.selectors.clearButton)) {
- const completedOps = this.getOperationsByStatus('completed');
+ const completedOps = this.getOperationIds(this.getOperationsByStatus('completed'));
if (completedOps.length > 0) {
- const ids = completedOps.map(op => op.id);
- this.updateServerOperations(ids, 'dismiss');
+ this.updateServerOperations(completedOps, 'dismiss');
}
} else if (e.target.closest(this.selectors.retryButton)) {
- const failedOps = this.getOperationsByStatus('failed');
+ const failedOps = this.getOperationIds(this.getOperationsByStatus('failed'));
if (failedOps.length > 0) {
- const ids = failedOps.map(op => op.id);
- this.updateServerOperations(ids, 'retry');
+ this.updateServerOperations(failedOps, 'retry');
}
} else if (e.target.closest('[data-action]')) {
const button = e.target.closest('[data-action]');
--
Gitblit v1.10.0