fix: authorize queued issue closures explicitly (Closes #483)
This commit is contained in:
1 parent
cc63fc6499
commit
62a0841eb5
16 files changed
+165
-67
No files matched your search
@@ -241,7 +241,7 @@ function createAuthoredOutbox({ storage, fetchJson, coordinator, backgroundSync,
|
||||
let blocked = 0;
|
||||
currentLogin = String(currentLogin || '').trim();
|
||||
for (const item of read()) {
|
||||
if (item.status === 'attention') continue;
|
||||
if (item.status === 'attention' || item.kind === 'issue-close') continue;
|
||||
if (!currentLogin || item.ownerLogin !== currentLogin) { blocked += 1; continue; }
|
||||
const outcome = await sendItem(item, currentLogin);
|
||||
if (outcome.result) confirmed.push(outcome.result);
|
||||
|
||||
@@ -97,6 +97,7 @@ function createIssueSyncStore({
|
||||
const timestamp = Number(now());
|
||||
const items = await records.getAll();
|
||||
const item = items.find(candidate => candidate.ownerLogin === ownerLogin &&
|
||||
candidate.kind !== 'issue-close' &&
|
||||
(candidate.status === 'queued' || candidate.status === 'sending') &&
|
||||
(candidate.status !== 'sending' || Number(candidate.claimUntil) <= timestamp));
|
||||
if (!item) return null;
|
||||
@@ -114,6 +115,7 @@ function createIssueSyncStore({
|
||||
const eligible = (await records.getAll()).filter(candidate =>
|
||||
candidate.recordType !== 'receipt-preference' &&
|
||||
candidate.ownerLogin === ownerLogin &&
|
||||
candidate.kind !== 'issue-close' &&
|
||||
(candidate.status === 'queued' || candidate.status === 'sending') &&
|
||||
(candidate.status !== 'sending' || Number(candidate.claimUntil) <= timestamp));
|
||||
const lanes = {
|
||||
|
||||
+21
-4
@@ -1662,9 +1662,12 @@
|
||||
'<button class="draft-resume" data-draft-index="' + index + '" type="button">Open current review</button>' +
|
||||
'<button class="draft-copy" data-draft-index="' + index + '" type="button">Copy feedback</button>' +
|
||||
'<button class="draft-discard" data-draft-index="' + index + '" type="button">Discard queued review</button>' :
|
||||
item.kind === 'authored-outbox' && closureOutbox ?
|
||||
'<button class="draft-resume" data-draft-index="' + index + '" type="button">Open issue</button>' +
|
||||
'<button class="draft-authorize" data-draft-index="' + index + '" type="button">Authorize & close</button>' +
|
||||
'<button class="draft-discard" data-draft-index="' + index + '" type="button">Discard</button>' :
|
||||
item.kind === 'authored-outbox' && !reviewOutbox ?
|
||||
'<button class="draft-resume" data-draft-index="' + index + '" type="button">' +
|
||||
(closureOutbox ? 'Open issue' : 'Open message') + '</button>' +
|
||||
'<button class="draft-resume" data-draft-index="' + index + '" type="button">Open message</button>' +
|
||||
'<button class="draft-send" data-draft-index="' + index + '" type="button">' + sendLabel + '</button>' +
|
||||
'<button class="draft-discard" data-draft-index="' + index + '" type="button">Discard</button>' :
|
||||
reviewOutbox ?
|
||||
@@ -1676,7 +1679,8 @@
|
||||
const state = (isOutbox || isUnfiled) ?
|
||||
'<span class="pill">' + (item.quarantined ? 'Identity protected' :
|
||||
(isUnfiled ? 'Needs filing' : item.status === 'completion' ? 'Created · ready to start' :
|
||||
item.status === 'attention' ? 'Needs attention' : item.status === 'sending' ? 'Sending' : 'Queued for sync')) + '</span>' +
|
||||
item.status === 'attention' ? 'Needs attention' : item.status === 'sending' ? 'Sending' :
|
||||
item.status === 'authorization' ? 'Awaiting authorization' : 'Queued for sync')) + '</span>' +
|
||||
(item.ownership ? '<div class="small">' + escapeHtml(item.ownership) + '</div>' : '') : '';
|
||||
const attempt = item.last_attempt_error ? '<span class="delivery-attempt small">Last attempt ' +
|
||||
escapeHtml(fmt(item.last_attempt_at)) + ' · ' + escapeHtml(item.last_attempt_error) + '</span>' : '';
|
||||
@@ -1691,7 +1695,8 @@
|
||||
'<div><h3 id="delivery-center-title">Delivery center</h3>' +
|
||||
'<p class="small">Waiting <strong data-delivery-count="waiting">' + deliveryCenter.counts.waiting + '</strong> · ' +
|
||||
'Sending <strong data-delivery-count="sending">' + deliveryCenter.counts.sending + '</strong> · ' +
|
||||
'Needs attention <strong data-delivery-count="attention">' + deliveryCenter.counts.attention + '</strong></p></div>' +
|
||||
'Needs attention <strong data-delivery-count="attention">' + deliveryCenter.counts.attention + '</strong> · ' +
|
||||
'Authorize <strong data-delivery-count="authorization">' + deliveryCenter.counts.authorization + '</strong></p></div>' +
|
||||
'<button id="retry-waiting-deliveries" type="button"' +
|
||||
(deliveryCenter.retryable.length && activeFlushLogin ? '' : ' disabled') + '>Retry waiting</button></section>';
|
||||
const deliveryCards = deliveryCenter.deliveries.length ? deliveryCenter.deliveries.map(renderDraftCard).join('') :
|
||||
@@ -1769,6 +1774,18 @@
|
||||
else applyOutboxResult(await issueOutbox.retry(item.outbox_id, activeFlushLogin));
|
||||
});
|
||||
});
|
||||
list.querySelectorAll('.draft-authorize').forEach(button => {
|
||||
button.addEventListener('click', async () => {
|
||||
const item = lastDrafts[Number(button.dataset.draftIndex)];
|
||||
if (!item?.outbox_id || !activeFlushLogin) return;
|
||||
button.disabled = true;
|
||||
qs('#my-work-action-status').textContent = 'Fresh authorization required for this exact issue.';
|
||||
const result = await authoredOutbox.retry(item.outbox_id, activeFlushLogin);
|
||||
applyAuthoredOutboxResult(result);
|
||||
qs('#my-work-action-status').textContent = result.confirmed?.length ?
|
||||
'Issue closed and queued intent cleared.' : 'Issue closure was not confirmed. The queued intent is still safe.';
|
||||
});
|
||||
});
|
||||
list.querySelectorAll('.draft-copy').forEach(button => {
|
||||
button.addEventListener('click', async () => {
|
||||
const item = lastDrafts[Number(button.dataset.draftIndex)];
|
||||
|
||||
+8
-5
@@ -160,10 +160,12 @@ function createDraftInbox({ storage, getCurrentLogin = () => '', now = () => Dat
|
||||
outbox_id: item.id,
|
||||
outbox_kind: item.kind,
|
||||
kind: 'authored-outbox',
|
||||
status: item.status === 'attention' ? 'attention' : (item.status === 'sending' ? 'sending' : 'queued'),
|
||||
status: item.status === 'attention' ? 'attention' : (item.status === 'sending' ? 'sending' :
|
||||
(isClosure ? 'authorization' : 'queued')),
|
||||
label: item.deliveryState === 'uncertain' ? 'Verify delivery' :
|
||||
(item.status === 'attention' ? (isClosure ? 'Issue closure needs attention' : 'Needs attention') :
|
||||
(isReview ? 'Queued review' : (isClosure ? 'Queued issue closure' : 'Queued message'))),
|
||||
(isReview ? 'Queued review' : (isClosure ? 'Awaiting authorization' : 'Queued message'))),
|
||||
authorization_required: isClosure,
|
||||
delivery_state: item.deliveryState,
|
||||
repository: isUpdate ? '' : item.repository,
|
||||
title: target,
|
||||
@@ -237,12 +239,13 @@ function createDraftInbox({ storage, getCurrentLogin = () => '', now = () => Dat
|
||||
const deliveries = items.filter(item => item.kind === 'issue-outbox' || item.kind === 'authored-outbox');
|
||||
const drafts = items.filter(item => item.kind !== 'issue-outbox' && item.kind !== 'authored-outbox');
|
||||
const counts = deliveries.reduce((summary, item) => {
|
||||
const state = item.status === 'sending' ? 'sending' : (item.status === 'attention' ? 'attention' : 'waiting');
|
||||
const state = item.status === 'sending' ? 'sending' : (item.status === 'attention' ? 'attention' :
|
||||
(item.status === 'authorization' ? 'authorization' : 'waiting'));
|
||||
summary[state] += 1;
|
||||
return summary;
|
||||
}, { waiting:0, sending:0, attention:0 });
|
||||
}, { waiting:0, sending:0, attention:0, authorization:0 });
|
||||
const retryable = deliveries.filter(item =>
|
||||
item.status === 'queued' && !item.quarantined && item.delivery_state !== 'uncertain'
|
||||
item.status === 'queued' && !item.authorization_required && !item.quarantined && item.delivery_state !== 'uncertain'
|
||||
);
|
||||
return { drafts, deliveries, counts, retryable };
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const BASE = new URL('./', self.location.href).pathname;
|
||||
importScripts(BASE + 'static/background-issue-sync.js');
|
||||
const CACHE = 'stackchain-dashboard-shell-v81';
|
||||
const CACHE = 'stackchain-dashboard-shell-v82';
|
||||
const OFFLINE_LEASE_URL = new URL(BASE + '__offline-session-lease', self.location.origin).href;
|
||||
const OUTAGE_STATUSES = new Set([500, 502, 503, 504]);
|
||||
const NAVIGATION_TIMEOUT_MS = self.__STACKCHAIN_NAVIGATION_TIMEOUT_MS || 4000;
|
||||
|
||||
Reference in new issue
Block a user