169 lines
6.2 KiB
JavaScript
169 lines
6.2 KiB
JavaScript
function buildMyWork(data) {
|
|
const login = data.user?.login || '';
|
|
const issues = (data.issues || []).map((item) => ({ ...item, kind: 'issue' }));
|
|
const pulls = (data.pull_requests || []).map((item) => ({ ...item, kind: 'pull' }));
|
|
const priorityLabels = ['p0', 'priority-high', 'critical'];
|
|
|
|
const work = issues.concat(pulls).map((item) => {
|
|
const labels = item.labels || [];
|
|
const priorityLabel = labels.find((label) =>
|
|
priorityLabels.includes(String(label).toLowerCase())
|
|
);
|
|
const assigned = (item.assignees || []).includes(login);
|
|
const isReview = (item.work_reasons || []).includes('review_requested');
|
|
return {
|
|
...item,
|
|
key: (item.repository || 'unknown') + '#' + item.number,
|
|
is_review: isReview,
|
|
is_assigned: assigned,
|
|
has_update: false,
|
|
reason: priorityLabel ? priorityLabel + ' priority' :
|
|
(isReview ? 'Needs your review' : (assigned ? 'Assigned to you' : 'Open work')),
|
|
_priority: priorityLabel ? 0 : (isReview ? 2 : (assigned ? 3 : 4)),
|
|
};
|
|
});
|
|
|
|
const byKey = new Map(work.map((item) => [item.kind + ':' + item.key, item]));
|
|
(data.notifications || []).filter((item) => item && item.unread).forEach((update) => {
|
|
const key = (update.repository || 'unknown') + '#' + update.number;
|
|
const subjectKind = String(update.subject_type || '').toLowerCase().includes('pull') ? 'pull' : 'issue';
|
|
const existing = byKey.get(subjectKind + ':' + key);
|
|
if (existing) {
|
|
existing.has_update = true;
|
|
existing.notification_id = update.id;
|
|
existing.url = update.url || existing.url;
|
|
existing.updated_at = update.updated_at || existing.updated_at;
|
|
existing._priority = Math.min(existing._priority, 1);
|
|
return;
|
|
}
|
|
if (!update.url) return;
|
|
const item = {
|
|
...update,
|
|
key,
|
|
kind: 'update',
|
|
is_review: false,
|
|
is_assigned: false,
|
|
has_update: true,
|
|
notification_id: update.id,
|
|
reason: 'Unread update',
|
|
_priority: 1,
|
|
};
|
|
work.push(item);
|
|
byKey.set(subjectKind + ':' + key, item);
|
|
});
|
|
|
|
return work.sort((left, right) =>
|
|
left._priority - right._priority ||
|
|
String(right.updated_at || '').localeCompare(String(left.updated_at || '')) ||
|
|
left.key.localeCompare(right.key)
|
|
).map(({ _priority, ...item }) => item);
|
|
}
|
|
|
|
function acknowledgeNotification(items, notificationId) {
|
|
return items.flatMap((item) => {
|
|
if (item.notification_id !== notificationId) return [item];
|
|
if (item.kind === 'update') return [];
|
|
const { notification_id, ...acknowledged } = item;
|
|
return [{ ...acknowledged, has_update: false }];
|
|
});
|
|
}
|
|
|
|
function createNotificationAcknowledger({ markRead, onItems, onStatus }) {
|
|
const pending = new Set();
|
|
return {
|
|
async acknowledge(items, notificationId) {
|
|
if (pending.has(notificationId)) return false;
|
|
pending.add(notificationId);
|
|
onItems(acknowledgeNotification(items, notificationId));
|
|
onStatus('Marking update read…');
|
|
try {
|
|
await markRead(notificationId);
|
|
onStatus('Update marked read.');
|
|
return true;
|
|
} catch (_error) {
|
|
onItems(items);
|
|
onStatus('Could not mark update read. Retry.');
|
|
return false;
|
|
} finally {
|
|
pending.delete(notificationId);
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
function notificationIds(items) {
|
|
return Array.from(new Set((items || [])
|
|
.filter((item) => item && item.has_update && Number.isInteger(item.notification_id))
|
|
.map((item) => item.notification_id)));
|
|
}
|
|
|
|
function createBulkNotificationAcknowledger({ markRead, onItems, onStatus }) {
|
|
let pending = false;
|
|
return {
|
|
async acknowledge(items, requestedIds) {
|
|
if (pending) return false;
|
|
const ids = Array.from(new Set((requestedIds || []).filter(Number.isInteger)));
|
|
if (!ids.length) return false;
|
|
pending = true;
|
|
onStatus('Marking ' + ids.length + ' updates read…');
|
|
try {
|
|
const result = await markRead(ids);
|
|
const marked = (result.marked || []).filter(Number.isInteger);
|
|
const failed = (result.failed || []).filter(Number.isInteger);
|
|
const updated = marked.reduce(
|
|
(current, notificationId) => acknowledgeNotification(current, notificationId),
|
|
items
|
|
);
|
|
onItems(updated);
|
|
onStatus(failed.length ?
|
|
marked.length + ' marked read · ' + failed.length + ' could not be updated — retry.' :
|
|
marked.length + ' updates marked read.');
|
|
return { marked, failed };
|
|
} catch (_error) {
|
|
onStatus('Could not mark updates read. Retry.');
|
|
return false;
|
|
} finally {
|
|
pending = false;
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
function filterMyWork(items, selectedFilter) {
|
|
if (selectedFilter === 'all') return items;
|
|
if (selectedFilter === 'review') return items.filter((item) => item.is_review);
|
|
if (selectedFilter === 'update') return items.filter((item) => item.has_update);
|
|
return items.filter((item) => item.kind === selectedFilter);
|
|
}
|
|
|
|
function summarizeMyWork(items) {
|
|
const updates = items.filter((item) => item.has_update).length;
|
|
const reviews = items.filter((item) => item.is_review).length;
|
|
const assigned = items.filter((item) => item.is_assigned).length;
|
|
const updateLabel = updates + ' unread update' + (updates === 1 ? '' : 's');
|
|
const reviewLabel = reviews + ' review' + (reviews === 1 ? '' : 's');
|
|
const assignedLabel = assigned + ' assigned';
|
|
return (updates ? updateLabel + ' · ' : '') + reviewLabel + ' · ' + assignedLabel;
|
|
}
|
|
|
|
function countMyWork(items) {
|
|
return {
|
|
all: items.length,
|
|
issue: items.filter((item) => item.kind === 'issue').length,
|
|
pull: items.filter((item) => item.kind === 'pull' && !item.is_review).length,
|
|
review: items.filter((item) => item.is_review).length,
|
|
update: items.filter((item) => item.has_update).length,
|
|
};
|
|
}
|
|
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
buildMyWork.filterMyWork = filterMyWork;
|
|
buildMyWork.summarizeMyWork = summarizeMyWork;
|
|
buildMyWork.countMyWork = countMyWork;
|
|
buildMyWork.acknowledgeNotification = acknowledgeNotification;
|
|
buildMyWork.createNotificationAcknowledger = createNotificationAcknowledger;
|
|
buildMyWork.notificationIds = notificationIds;
|
|
buildMyWork.createBulkNotificationAcknowledger = createBulkNotificationAcknowledger;
|
|
module.exports = buildMyWork;
|
|
}
|