feat: keep progressive My Work live (Closes #1411)
This commit is contained in:
1 parent
e7e8c24d05
commit
f9b29d8d14
2 files changed
+186
-17
No files matched your search
@@ -1,8 +1,11 @@
|
||||
function createProgressiveMyWork({ document, fetchSnapshot }) {
|
||||
function createProgressiveMyWork({
|
||||
document, fetchSnapshot, pollerOptions = {}, lifecycleTarget = globalThis,
|
||||
}) {
|
||||
const list = document.querySelector('#my-work-list');
|
||||
const status = document.querySelector('#my-work-status');
|
||||
const filters = Array.from(document.querySelectorAll('[data-work-filter]'));
|
||||
const listeners = [];
|
||||
const lifecycleListeners = [];
|
||||
let items = [];
|
||||
let active = 'all';
|
||||
let stopped = false;
|
||||
@@ -10,6 +13,7 @@ function createProgressiveMyWork({ document, fetchSnapshot }) {
|
||||
let liveSnapshot = null;
|
||||
let liveSnapshotPromise = null;
|
||||
let confirmedLogin = '';
|
||||
let poller = null;
|
||||
const deferredQueues = {
|
||||
today:'Today', agenda:'Agenda', later:'Later', draft:'Drafts',
|
||||
};
|
||||
@@ -60,6 +64,40 @@ function createProgressiveMyWork({ document, fetchSnapshot }) {
|
||||
listeners.push([button, listener]);
|
||||
});
|
||||
|
||||
const applySnapshot = snapshot => {
|
||||
if (stopped) return false;
|
||||
const transferable = snapshot && typeof snapshot === 'object' &&
|
||||
Object.prototype.hasOwnProperty.call(snapshot, 'context');
|
||||
if (transferable) liveSnapshot = snapshot;
|
||||
const context = snapshot?.context || snapshot || {};
|
||||
confirmedLogin = String(context.user?.login || '').trim();
|
||||
items = buildMyWork({ ...context, notifications:snapshot?.notifications || context.notifications || [] });
|
||||
updateCounts();
|
||||
render();
|
||||
const assigned = items.filter(item => item.is_assigned).length;
|
||||
if (status) status.textContent = assigned + ' assigned work item' + (assigned === 1 ? '' : 's') + ' ready.';
|
||||
return true;
|
||||
};
|
||||
|
||||
if (typeof createContextPoller === 'function') {
|
||||
poller = createContextPoller({
|
||||
...pollerOptions,
|
||||
fetchContext: fetchSnapshot,
|
||||
onSnapshot: applySnapshot,
|
||||
onError: () => {
|
||||
if (status && !stopped) status.textContent = 'Assigned work is reconnecting…';
|
||||
},
|
||||
isHidden: pollerOptions.isHidden || (() => Boolean(document.hidden)),
|
||||
});
|
||||
const recover = () => {
|
||||
if (!document.hidden) void poller.refresh();
|
||||
};
|
||||
['online', 'visibilitychange'].forEach(eventName => {
|
||||
lifecycleTarget.addEventListener?.(eventName, recover);
|
||||
lifecycleListeners.push([eventName, recover]);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
login() { return confirmedLogin; },
|
||||
handoff() {
|
||||
@@ -76,6 +114,14 @@ function createProgressiveMyWork({ document, fetchSnapshot }) {
|
||||
},
|
||||
async start() {
|
||||
if (status) status.textContent = 'Loading assigned work…';
|
||||
if (poller) {
|
||||
const request = poller.start();
|
||||
liveSnapshotPromise = request.then(snapshot => (
|
||||
snapshot && typeof snapshot === 'object' &&
|
||||
Object.prototype.hasOwnProperty.call(snapshot, 'context') ? snapshot : null
|
||||
));
|
||||
return Boolean(await request);
|
||||
}
|
||||
const request = Promise.resolve().then(() => fetchSnapshot());
|
||||
liveSnapshotPromise = request.then(snapshot => (
|
||||
snapshot && typeof snapshot === 'object' &&
|
||||
@@ -84,18 +130,9 @@ function createProgressiveMyWork({ document, fetchSnapshot }) {
|
||||
try {
|
||||
const snapshot = await request;
|
||||
if (stopped) return false;
|
||||
const transferable = snapshot && typeof snapshot === 'object' &&
|
||||
Object.prototype.hasOwnProperty.call(snapshot, 'context');
|
||||
if (transferable) liveSnapshot = snapshot;
|
||||
else liveSnapshotPromise = null;
|
||||
const context = snapshot?.context || snapshot || {};
|
||||
confirmedLogin = String(context.user?.login || '').trim();
|
||||
items = buildMyWork({ ...context, notifications:snapshot?.notifications || context.notifications || [] });
|
||||
updateCounts();
|
||||
render();
|
||||
const assigned = items.filter(item => item.is_assigned).length;
|
||||
if (status) status.textContent = assigned + ' assigned work item' + (assigned === 1 ? '' : 's') + ' ready.';
|
||||
return true;
|
||||
const applied = applySnapshot(snapshot);
|
||||
if (!liveSnapshot) liveSnapshotPromise = null;
|
||||
return applied;
|
||||
} catch (_error) {
|
||||
if (status && !stopped) status.textContent = 'Assigned work is reconnecting…';
|
||||
return false;
|
||||
@@ -103,7 +140,10 @@ function createProgressiveMyWork({ document, fetchSnapshot }) {
|
||||
},
|
||||
stop() {
|
||||
stopped = true;
|
||||
poller?.stop();
|
||||
listeners.forEach(([button, listener]) => button.removeEventListener?.('click', listener));
|
||||
lifecycleListeners.forEach(([eventName, listener]) =>
|
||||
lifecycleTarget.removeEventListener?.(eventName, listener));
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -111,9 +151,17 @@ function createProgressiveMyWork({ document, fetchSnapshot }) {
|
||||
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
||||
window.stackchainProgressiveMyWork = createProgressiveMyWork({
|
||||
document,
|
||||
fetchSnapshot: async () => {
|
||||
const response = await fetch('api/v1/live', { headers:{Accept:'application/json'} });
|
||||
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||
lifecycleTarget: window,
|
||||
fetchSnapshot: async (revisions = {}, { signal } = {}) => {
|
||||
const query = createContextPoller.buildRevisionQuery(revisions);
|
||||
const response = await fetch('api/v1/live' + (query ? '?' + query : ''), {
|
||||
headers:{Accept:'application/json'}, signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
const error = new Error('HTTP ' + response.status);
|
||||
error.retryAfterMs = createContextPoller.retryAfterMs(response.headers.get('Retry-After'));
|
||||
throw error;
|
||||
}
|
||||
return response.json();
|
||||
},
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user