82 lines
3.2 KiB
JavaScript
82 lines
3.2 KiB
JavaScript
function createIssueSheet({ fetchJson, storage }) {
|
|
let commentRequest = null;
|
|
let closeRequest = null;
|
|
let labelRequest = null;
|
|
const issuePath = item => 'api/v1/repos/' + item.repository.split('/').map(encodeURIComponent).join('/') +
|
|
'/issues/' + encodeURIComponent(item.number);
|
|
const draftKey = item => 'stackchain.issue-comment.v1:' + item.repository + '#' + item.number;
|
|
const labelDraftKey = item => 'stackchain.issue-labels.v1:' + item.repository + '#' + item.number;
|
|
|
|
return {
|
|
load(item) {
|
|
return fetchJson(issuePath(item) + '/detail', {
|
|
headers: { Accept: 'application/json' },
|
|
});
|
|
},
|
|
loadLabels(item) {
|
|
return fetchJson(issuePath(item) + '/labels', {
|
|
headers: { Accept: 'application/json' },
|
|
});
|
|
},
|
|
loadDraft(item) {
|
|
try { return storage?.getItem(draftKey(item)) || ''; }
|
|
catch (_error) { return ''; }
|
|
},
|
|
saveDraft(item, body) {
|
|
try { storage?.setItem(draftKey(item), body); }
|
|
catch (_error) { /* The textarea remains the fallback. */ }
|
|
},
|
|
loadLabelDraft(item) {
|
|
try {
|
|
const value = JSON.parse(storage?.getItem(labelDraftKey(item)) || '[]');
|
|
return Array.isArray(value) ? value.filter(Number.isInteger) : [];
|
|
} catch (_error) { return []; }
|
|
},
|
|
updateLabels(item, labelIds) {
|
|
if (labelRequest) return labelRequest;
|
|
try { storage?.setItem(labelDraftKey(item), JSON.stringify(labelIds)); }
|
|
catch (_error) { /* The checked controls remain the fallback. */ }
|
|
labelRequest = fetchJson(issuePath(item) + '/labels', {
|
|
method: 'PATCH',
|
|
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ label_ids: labelIds }),
|
|
}).then(result => {
|
|
if (result?.number !== item.number || !Array.isArray(result.labels)) {
|
|
throw new Error('Issue labels were not confirmed.');
|
|
}
|
|
try { storage?.removeItem(labelDraftKey(item)); }
|
|
catch (_error) { /* The upstream label set is authoritative. */ }
|
|
return result;
|
|
}).finally(() => { labelRequest = null; });
|
|
return labelRequest;
|
|
},
|
|
close(item) {
|
|
if (closeRequest) return closeRequest;
|
|
closeRequest = fetchJson(issuePath(item) + '/close', {
|
|
method: 'PATCH',
|
|
headers: { Accept: 'application/json' },
|
|
}).then(result => {
|
|
if (result?.state !== 'closed') throw new Error('Issue closure was not confirmed.');
|
|
return result;
|
|
}).finally(() => { closeRequest = null; });
|
|
return closeRequest;
|
|
},
|
|
comment(item, body) {
|
|
if (commentRequest) return commentRequest;
|
|
this.saveDraft(item, body);
|
|
commentRequest = fetchJson(issuePath(item) + '/comments', {
|
|
method: 'POST',
|
|
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ body }),
|
|
}).then(result => {
|
|
try { storage?.removeItem(draftKey(item)); }
|
|
catch (_error) { /* The upstream comment is authoritative. */ }
|
|
return result;
|
|
}).finally(() => { commentRequest = null; });
|
|
return commentRequest;
|
|
},
|
|
};
|
|
}
|
|
|
|
if (typeof module !== 'undefined' && module.exports) module.exports = createIssueSheet;
|