feat: read full unread update conversations (#219)
This commit is contained in:
1 parent
3824ff180a
commit
ea909ef222
8 files changed
+379
-20
No files matched your search
+39
-10
@@ -548,9 +548,10 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
<span class="pill" id="update-subject-type">Update</span>
|
||||
<span class="pill" id="update-subject-state"></span>
|
||||
</div>
|
||||
<h2>Latest comment</h2>
|
||||
<div class="small" id="update-comment-meta"></div>
|
||||
<p class="update-sheet-content" id="update-comment-body"></p>
|
||||
<h2>Full conversation</h2>
|
||||
<div id="update-comments"></div>
|
||||
<button class="conversation-more" id="load-older-update-comments" type="button" hidden>Load older messages</button>
|
||||
<div id="update-conversation-status" class="small" aria-live="assertive"></div>
|
||||
<details>
|
||||
<summary><h2>Subject context</h2></summary>
|
||||
<p class="update-sheet-content muted" id="update-subject-body"></p>
|
||||
@@ -845,6 +846,16 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function fetchNotificationConversation(notificationId, page) {
|
||||
const response = await fetch('api/v1/notifications/' + encodeURIComponent(notificationId) +
|
||||
'/conversation?page=' + encodeURIComponent(page) + '&limit=20', {
|
||||
headers: { Accept: 'application/json' },
|
||||
});
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok) throw new Error(payload.error || 'Loading older messages failed.');
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function fetchWorkPage(stream, page) {
|
||||
const response = await fetch('api/v1/work/' + encodeURIComponent(stream) + '?page=' + encodeURIComponent(page), {
|
||||
headers: { Accept: 'application/json' },
|
||||
@@ -924,14 +935,16 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
});
|
||||
const notificationReader = createNotificationReader({
|
||||
load: fetchNotificationDetail,
|
||||
loadConversation: fetchNotificationConversation,
|
||||
markRead: markNotificationRead,
|
||||
onOpen: item => {
|
||||
selectedUpdate = item;
|
||||
qs('#update-sheet').classList.add('open');
|
||||
qs('#update-sheet-key').textContent = item.key || '';
|
||||
qs('#update-sheet-title').textContent = item.title || 'Unread update';
|
||||
qs('#update-comment-meta').textContent = '';
|
||||
qs('#update-comment-body').textContent = '';
|
||||
qs('#update-comments').textContent = '';
|
||||
qs('#update-conversation-status').textContent = '';
|
||||
qs('#load-older-update-comments').hidden = true;
|
||||
qs('#update-subject-body').textContent = '';
|
||||
qs('#update-subject-type').textContent = item.subject_type || 'Update';
|
||||
qs('#update-subject-state').textContent = item.state || '';
|
||||
@@ -943,18 +956,14 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
qs('#keep-update-unread').focus();
|
||||
},
|
||||
onDetail: detail => {
|
||||
const comment = detail.latest_comment || {};
|
||||
qs('#update-sheet-title').textContent = detail.title || 'Unread update';
|
||||
qs('#update-subject-type').textContent = detail.subject_type || 'Update';
|
||||
qs('#update-subject-state').textContent = detail.state || '';
|
||||
qs('#update-comment-meta').textContent = comment.author ?
|
||||
comment.author + (comment.created_at ? ' · ' + fmt(comment.created_at) : '') :
|
||||
(comment.created_at ? fmt(comment.created_at) : 'No comment author reported');
|
||||
qs('#update-comment-body').textContent = comment.body || 'No comment body was provided.';
|
||||
qs('#update-subject-body').textContent = detail.subject_body || 'No subject context was provided.';
|
||||
qs('#open-update-gitea').href = detail.url || selectedUpdate?.url || '#';
|
||||
qs('#retry-update-load').hidden = true;
|
||||
},
|
||||
onConversation: renderUpdateConversation,
|
||||
onItems: items => {
|
||||
const readId = selectedUpdate?.notification_id;
|
||||
lastMyWork = items;
|
||||
@@ -1365,6 +1374,15 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
comments.length + ' of ' + Math.max(state.total || 0, comments.length) + ' messages loaded.' : 'No comments yet.';
|
||||
}
|
||||
|
||||
function renderUpdateConversation(state) {
|
||||
const comments = state?.comments || [];
|
||||
qs('#update-comments').innerHTML = comments.length ?
|
||||
comments.map(renderIssueComment).join('') : '<div class="muted">No comments yet.</div>';
|
||||
qs('#load-older-update-comments').hidden = !Number.isInteger(state?.older_page);
|
||||
qs('#update-conversation-status').textContent = comments.length ?
|
||||
comments.length + ' of ' + Math.max(state.total || 0, comments.length) + ' messages loaded.' : 'No comments yet.';
|
||||
}
|
||||
|
||||
function renderPullConversation(state) {
|
||||
const comments = state?.comments || [];
|
||||
qs('#pull-comments').innerHTML = comments.length ? comments.map(comment =>
|
||||
@@ -2713,6 +2731,16 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
qs('#retry-update-load').addEventListener('click', () => {
|
||||
if (selectedUpdate) notificationReader.open(selectedUpdate, lastMyWork);
|
||||
});
|
||||
qs('#load-older-update-comments').addEventListener('click', async () => {
|
||||
const button = qs('#load-older-update-comments');
|
||||
const panel = qs('#update-sheet .update-sheet-panel');
|
||||
const previousHeight = panel.scrollHeight;
|
||||
const previousTop = panel.scrollTop;
|
||||
button.disabled = true;
|
||||
await notificationReader.loadOlder();
|
||||
panel.scrollTop = previousTop + (panel.scrollHeight - previousHeight);
|
||||
button.disabled = false;
|
||||
});
|
||||
qs('#update-reply').addEventListener('input', event => {
|
||||
if (selectedUpdate) notificationReplier.saveDraft(selectedUpdate, event.target.value);
|
||||
});
|
||||
@@ -2728,6 +2756,7 @@ textarea { resize: vertical; min-height: 120px; }
|
||||
const result = await notificationReplier.submit(selectedUpdate, body);
|
||||
qs('#send-update-reply').disabled = false;
|
||||
if (result) {
|
||||
notificationReader.appendReply(result);
|
||||
qs('#update-reply').value = '';
|
||||
qs('#mark-update-read-next').focus();
|
||||
} else {
|
||||
|
||||
+38
-1
@@ -249,10 +249,16 @@ function createWorkPager({ load, onItems, onPagination, onStatus }) {
|
||||
};
|
||||
}
|
||||
|
||||
function createNotificationReader({ load, markRead, onOpen, onDetail, onItems, onStatus, onClose }) {
|
||||
function createNotificationReader({
|
||||
load, markRead, onOpen, onDetail, onItems, onStatus, onClose,
|
||||
loadConversation = null,
|
||||
onConversation = () => {},
|
||||
createPager = typeof createConversationPager === 'function' ? createConversationPager : null,
|
||||
}) {
|
||||
let selected = null;
|
||||
let loadVersion = 0;
|
||||
let marking = false;
|
||||
let conversationPager = null;
|
||||
|
||||
async function open(item) {
|
||||
selected = item;
|
||||
@@ -263,6 +269,14 @@ function createNotificationReader({ load, markRead, onOpen, onDetail, onItems, o
|
||||
const detail = await load(item.notification_id);
|
||||
if (selected !== item || version !== loadVersion) return false;
|
||||
onDetail(detail);
|
||||
if (createPager && loadConversation && detail.conversation) {
|
||||
conversationPager = createPager({
|
||||
loadPage: page => loadConversation(item.notification_id, page),
|
||||
});
|
||||
onConversation(conversationPager.reset(detail.conversation));
|
||||
} else {
|
||||
conversationPager = null;
|
||||
}
|
||||
onStatus('Update ready.');
|
||||
return true;
|
||||
} catch (_error) {
|
||||
@@ -275,6 +289,29 @@ function createNotificationReader({ load, markRead, onOpen, onDetail, onItems, o
|
||||
|
||||
return {
|
||||
open,
|
||||
appendReply(comment) {
|
||||
if (!conversationPager) return false;
|
||||
onConversation(conversationPager.append(comment));
|
||||
return true;
|
||||
},
|
||||
async loadOlder() {
|
||||
if (!conversationPager || !selected) return false;
|
||||
const pager = conversationPager;
|
||||
const version = loadVersion;
|
||||
onStatus('Loading older messages…');
|
||||
try {
|
||||
const state = await pager.loadOlder();
|
||||
if (pager !== conversationPager || version !== loadVersion) return false;
|
||||
onConversation(state);
|
||||
onStatus(state.comments.length + ' of ' + state.total + ' messages loaded.');
|
||||
return true;
|
||||
} catch (_error) {
|
||||
if (pager === conversationPager && version === loadVersion) {
|
||||
onStatus('Could not load older messages. Retry.');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
async markReadAndNext(items) {
|
||||
if (!selected || marking) return false;
|
||||
const current = selected;
|
||||
|
||||
Reference in new issue
Block a user