feat: preview issue details before claiming (#179)
This commit is contained in:
1 parent
1947f878e9
commit
b4b13a7db1
3 files changed
+103
-6
No files matched your search
+28
-6
@@ -150,11 +150,14 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
.find-work-sheet.open { display:flex; }
|
||||
.find-work-panel { width:min(560px,100%); height:100dvh; overflow:auto; display:grid; align-content:start; gap:12px; padding:18px; padding-bottom:calc(18px + env(safe-area-inset-bottom)); background:#0b1526; border-left:1px solid #2a496e; }
|
||||
.find-work-header { display:flex; align-items:center; justify-content:space-between; gap:10px; }
|
||||
.find-work-header button, .find-work-card button, .find-work-more { min-height:44px; }
|
||||
.find-work-header button, .find-work-card button, .find-work-card a, .find-work-more { min-height:44px; }
|
||||
.find-work-list { display:grid; gap:10px; }
|
||||
.find-work-card { display:grid; gap:8px; padding:12px; border:1px solid #2a496e; border-radius:12px; background:#101f36; overflow-wrap:anywhere; }
|
||||
.find-work-card button { width:100%; font-weight:700; }
|
||||
@media(max-width:320px) { .find-work-panel { padding:12px; } .my-work-actions { width:100%; } .my-work-actions button { flex:1 1 100%; } }
|
||||
.find-work-detail { min-width:0; display:grid; gap:10px; padding:10px; border-radius:10px; background:#0b1526; }
|
||||
.find-work-description { margin:0; white-space:pre-wrap; overflow-wrap:anywhere; }
|
||||
.find-work-detail a { display:flex; align-items:center; justify-content:center; border:1px solid #60a5fa; border-radius:10px; font-weight:700; }
|
||||
@media(max-width:320px) { .find-work-panel { padding:12px; overflow-x:hidden; } .find-work-card { min-width:0; } .my-work-actions { width:100%; } .my-work-actions button { flex:1 1 100%; } }
|
||||
.create-issue-sheet { position:fixed; inset:0; z-index:57; display:none; justify-content:flex-end; background:rgba(5,12,21,.72); backdrop-filter:blur(4px); }
|
||||
.create-issue-sheet.open { display:flex; }
|
||||
.create-issue-panel { width:min(560px,100%); height:100dvh; overflow:auto; display:grid; align-content:start; gap:12px; padding:18px; padding-bottom:calc(18px + env(safe-area-inset-bottom)); background:#0b1526; border-left:1px solid #2a496e; }
|
||||
@@ -1139,12 +1142,31 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
|
||||
function renderAvailableIssues(items) {
|
||||
const list = qs('#find-work-list');
|
||||
list.innerHTML = items.length ? items.map((item, index) =>
|
||||
'<article class="find-work-card"><div class="small">' + escapeHtml(item.repository) + '#' +
|
||||
list.innerHTML = items.length ? items.map((item, index) => {
|
||||
const expanded = findWorkController.isPreviewed(item);
|
||||
const detailId = 'find-work-detail-' + index;
|
||||
const detail = '<div id="' + detailId + '" class="find-work-detail"' + (expanded ? '' : ' hidden') +
|
||||
'><p class="find-work-description">' + escapeHtml(item.body || 'No description provided.') + '</p>' +
|
||||
(item.url ? '<a href="' + escapeHtml(item.url) + '" target="_blank" rel="noopener noreferrer">Open in Gitea</a>' : '') +
|
||||
'</div>';
|
||||
return '<article class="find-work-card"><div class="small">' + escapeHtml(item.repository) + '#' +
|
||||
Number(item.number) + '</div><strong>' + escapeHtml(item.title || 'Untitled issue') + '</strong>' +
|
||||
'<div>' + (item.labels || []).map(label => '<span class="pill">' + escapeHtml(label) + '</span>').join(' ') +
|
||||
'</div><button type="button" data-claim-index="' + index + '">Assign to me</button></article>'
|
||||
).join('') : '<div class="muted">No unassigned issues are available on this page.</div>';
|
||||
'</div><button type="button" data-preview-index="' + index + '" aria-expanded="' + expanded +
|
||||
'" aria-controls="' + detailId + '">' + (expanded ? 'Hide details' : 'View details') + '</button>' +
|
||||
detail + '<button type="button" data-claim-index="' + index + '">Assign to me</button></article>';
|
||||
}).join('') : '<div class="muted">No unassigned issues are available on this page.</div>';
|
||||
list.querySelectorAll('[data-preview-index]').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const item = findWorkController.items()[Number(button.dataset.previewIndex)];
|
||||
if (!item) return;
|
||||
const expanded = findWorkController.togglePreview(item);
|
||||
const detail = document.getElementById(button.getAttribute('aria-controls'));
|
||||
button.setAttribute('aria-expanded', String(expanded));
|
||||
button.textContent = expanded ? 'Hide details' : 'View details';
|
||||
if (detail) detail.hidden = !expanded;
|
||||
});
|
||||
});
|
||||
list.querySelectorAll('[data-claim-index]').forEach(button => {
|
||||
button.addEventListener('click', async () => {
|
||||
const item = findWorkController.items()[Number(button.dataset.claimIndex)];
|
||||
|
||||
@@ -3,6 +3,11 @@ function createFindWork({ fetchJson, onItems, onPagination, onStatus }) {
|
||||
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 : [];
|
||||
@@ -15,6 +20,10 @@ function createFindWork({ fetchJson, onItems, onPagination, onStatus }) {
|
||||
} 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,
|
||||
@@ -49,6 +58,15 @@ function createFindWork({ fetchJson, onItems, onPagination, onStatus }) {
|
||||
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;
|
||||
@@ -65,6 +83,7 @@ function createFindWork({ fetchJson, onItems, onPagination, onStatus }) {
|
||||
available = available.filter(candidate =>
|
||||
candidate.repository !== item.repository || candidate.number !== item.number
|
||||
);
|
||||
previewed.delete(itemKey(item));
|
||||
onItems(available.slice());
|
||||
onStatus('Assigned ' + key + ' to you.');
|
||||
return confirmed;
|
||||
|
||||
Reference in new issue
Block a user