105 lines
3.6 KiB
JavaScript
105 lines
3.6 KiB
JavaScript
function createFindWork({ fetchJson, onItems, onPagination, onStatus }) {
|
|
let available = [];
|
|
let pagination = { page: 1, total: 0, has_more: false };
|
|
let loadRequest = null;
|
|
let claimRequest = null;
|
|
const previewed = new Set();
|
|
|
|
function itemKey(item) {
|
|
return String(item?.repository || '') + '#' + String(item?.number || '');
|
|
}
|
|
|
|
function apply(result, append) {
|
|
const incoming = Array.isArray(result?.items) ? result.items : [];
|
|
if (append) {
|
|
const byId = new Map(available.map(item => [item.id, item]));
|
|
incoming.forEach(item => {
|
|
if (item && !byId.has(item.id)) byId.set(item.id, { ...item });
|
|
});
|
|
available = Array.from(byId.values());
|
|
} else {
|
|
available = incoming.map(item => ({ ...item }));
|
|
}
|
|
const availableKeys = new Set(available.map(itemKey));
|
|
previewed.forEach(key => {
|
|
if (!availableKeys.has(key)) previewed.delete(key);
|
|
});
|
|
pagination = {
|
|
page: Number(result?.page) || 1,
|
|
total: Number(result?.total) || available.length,
|
|
has_more: result?.has_more === true,
|
|
};
|
|
onItems(available.slice());
|
|
onPagination({ ...pagination });
|
|
}
|
|
|
|
function loadPage(page, append) {
|
|
if (loadRequest) return loadRequest;
|
|
loadRequest = fetchJson('api/v1/available-issues?page=' + page, {
|
|
headers: { Accept: 'application/json' },
|
|
}).then(result => {
|
|
apply(result, append);
|
|
if (result?.refresh_failed === true) {
|
|
onStatus('Showing saved available work. Catalog refresh failed; retrying shortly.');
|
|
} else if (result?.revalidating === true) {
|
|
onStatus('Showing saved available work while the catalog refreshes…');
|
|
}
|
|
return result;
|
|
}).finally(() => { loadRequest = null; });
|
|
return loadRequest;
|
|
}
|
|
|
|
return {
|
|
reset(result) {
|
|
apply(result, false);
|
|
},
|
|
load() {
|
|
return loadPage(1, false);
|
|
},
|
|
loadMore() {
|
|
if (!pagination.has_more) return Promise.resolve(false);
|
|
return loadPage(pagination.page + 1, true);
|
|
},
|
|
items() {
|
|
return available.slice();
|
|
},
|
|
togglePreview(item) {
|
|
const key = itemKey(item);
|
|
if (previewed.has(key)) previewed.delete(key);
|
|
else previewed.add(key);
|
|
return previewed.has(key);
|
|
},
|
|
isPreviewed(item) {
|
|
return previewed.has(itemKey(item));
|
|
},
|
|
claim(item) {
|
|
if (claimRequest) return claimRequest;
|
|
const key = item.repository + '#' + item.number;
|
|
onStatus('Assigning ' + key + '…');
|
|
const path = 'api/v1/repos/' + item.repository.split('/').map(encodeURIComponent).join('/') +
|
|
'/issues/' + encodeURIComponent(item.number) + '/claim';
|
|
claimRequest = fetchJson(path, {
|
|
method: 'PATCH',
|
|
headers: { Accept: 'application/json' },
|
|
}).then(confirmed => {
|
|
if (!Array.isArray(confirmed?.assignees) || !confirmed.assignees.length) {
|
|
throw new Error('Issue assignment was not confirmed.');
|
|
}
|
|
available = available.filter(candidate =>
|
|
candidate.repository !== item.repository || candidate.number !== item.number
|
|
);
|
|
pagination.total = Math.max(available.length, pagination.total - 1);
|
|
pagination.has_more = available.length < pagination.total;
|
|
previewed.delete(itemKey(item));
|
|
onItems(available.slice());
|
|
onPagination({ ...pagination });
|
|
onStatus('Assigned ' + key + ' to you.');
|
|
return confirmed;
|
|
}).finally(() => { claimRequest = null; });
|
|
return claimRequest;
|
|
},
|
|
};
|
|
}
|
|
|
|
if (typeof module !== 'undefined' && module.exports) module.exports = createFindWork;
|