Track client operations and show inline progress
This commit is contained in:
65
src/web/state/operations.js
Normal file
65
src/web/state/operations.js
Normal file
@@ -0,0 +1,65 @@
|
||||
export const OPERATION_CONFLICTS = Object.freeze({
|
||||
connection: ['serverApply', 'subscriptionImport', 'subscriptionRefresh', 'subscriptionDelete', 'gatewayAuto'],
|
||||
serverApply: ['connection', 'subscriptionImport', 'subscriptionRefresh', 'subscriptionDelete', 'gatewayAuto'],
|
||||
subscriptionImport: ['connection', 'serverApply', 'subscriptionRefresh', 'subscriptionDelete', 'gatewayAuto'],
|
||||
subscriptionRefresh: ['connection', 'serverApply', 'subscriptionImport', 'subscriptionDelete', 'gatewayAuto'],
|
||||
subscriptionDelete: ['connection', 'serverApply', 'subscriptionImport', 'subscriptionRefresh', 'gatewayAuto'],
|
||||
gatewayAuto: ['connection', 'serverApply', 'subscriptionImport', 'subscriptionRefresh', 'subscriptionDelete'],
|
||||
});
|
||||
|
||||
export function operationBlocked(operations, key) {
|
||||
if (operations[key]?.status === 'running') return true;
|
||||
return (OPERATION_CONFLICTS[key] || []).some(
|
||||
(conflict) => operations[conflict]?.status === 'running',
|
||||
);
|
||||
}
|
||||
|
||||
export function createOperationRegistry(onChange = () => {}, now = () => new Date().toISOString()) {
|
||||
let operations = {};
|
||||
const inFlight = new Map();
|
||||
|
||||
function run(key, action) {
|
||||
if (inFlight.has(key)) return inFlight.get(key);
|
||||
if (operationBlocked(operations, key)) return Promise.resolve(false);
|
||||
|
||||
operations = { ...operations, [key]: { status: 'running', startedAt: now() } };
|
||||
onChange(operations);
|
||||
|
||||
const promise = Promise.resolve()
|
||||
.then(action)
|
||||
.finally(() => {
|
||||
const { [key]: completed, ...remaining } = operations;
|
||||
operations = remaining;
|
||||
inFlight.delete(key);
|
||||
onChange(operations);
|
||||
});
|
||||
inFlight.set(key, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
return { run, getSnapshot: () => operations };
|
||||
}
|
||||
|
||||
export function createLatestRequest() {
|
||||
let controller = null;
|
||||
|
||||
return {
|
||||
run(action) {
|
||||
controller?.abort();
|
||||
controller = new AbortController();
|
||||
const current = controller;
|
||||
return Promise.resolve()
|
||||
.then(() => action(current.signal))
|
||||
.then(
|
||||
(value) => current.signal.aborted ? undefined : value,
|
||||
(error) => {
|
||||
if (current.signal.aborted) return undefined;
|
||||
throw error;
|
||||
},
|
||||
);
|
||||
},
|
||||
cancel() {
|
||||
controller?.abort();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user