From 7662cca9e6bef65c7fdff9d99b5c7a39b8f77482 Mon Sep 17 00:00:00 2001 From: timmy Date: Tue, 25 Aug 2026 09:11:14 +0000 Subject: [PATCH] feat: act on Search and Following comments (Closes #1388) --- frontend/conversation.js | 33 +++++++++ frontend/dashboard.js | 57 +++++---------- frontend/index.html | 1 + frontend/search-preview.js | 38 +++++++++- src/frontend_bundle.py | 4 +- .../test_mobile_comment_reactions_release.py | 38 ++++++++++ tests/test_conversation_action_hydrator.py | 72 +++++++++++++++++++ tests/test_markdown_renderer.py | 2 +- tests/test_update_read_position.py | 3 +- 9 files changed, 202 insertions(+), 46 deletions(-) diff --git a/frontend/conversation.js b/frontend/conversation.js index 56ad7e5..beff6c9 100644 --- a/frontend/conversation.js +++ b/frontend/conversation.js @@ -81,4 +81,37 @@ function createConversationPager({ loadPage }) { }; } +function renderConversationComment(comment, controller, escapeHtml, formatTime, renderMarkdown) { + const actions = controller?.actionHtml?.(comment) || ''; + return '
' + + escapeHtml(comment.author || 'Unknown author') + + (comment.created_at ? ' · ' + escapeHtml(formatTime(comment.created_at)) : '') + + '
' + actions + '
' + + renderMarkdown(comment.body || 'No comment body provided.') + '
'; +} + +function createConversationRenderers({qs,renderComment,updateReadPosition,getSelectedUpdate}) { + function status(kind, comments, total) { + qs('#' + kind + '-conversation-status').textContent = comments.length ? + comments.length + ' of ' + Math.max(total || 0, comments.length) + ' messages loaded.' : 'No comments yet.'; + } + function paint(kind, state, controller) { + const comments = state?.comments || []; + qs('#' + kind + '-comments').innerHTML = comments.length ? comments.map(comment => + kind === 'pull' ? '
' + renderComment(comment,controller) + '
' : + renderComment(comment,controller)).join('') : '
No comments yet.
'; + qs('#load-older-' + kind + '-comments').hidden = !Number.isInteger(state?.older_page); + status(kind,comments,state?.total); + if (kind === 'update') { + const newest = qs('#update-comments .issue-comment:last-child'); + updateReadPosition.ready(String(getSelectedUpdate()?.notification_id || ''),newest); + } + } + return { + paintIssueConversation:(state,controller)=>paint('issue',state,controller), + paintPullConversation:(state,controller)=>paint('pull',state,controller), + paintUpdateConversation:(state,controller)=>paint('update',state,controller), + }; +} + if (typeof module !== 'undefined' && module.exports) module.exports = createConversationPager; diff --git a/frontend/dashboard.js b/frontend/dashboard.js index bf6ab29..5721d4b 100644 --- a/frontend/dashboard.js +++ b/frontend/dashboard.js @@ -4097,46 +4097,17 @@ toggle?.focus(); } - function renderIssueComment(comment, controller = commentActions) { - const actions = controller?.actionHtml?.(comment) || ''; - return '
' + - escapeHtml(comment.author || 'Unknown author') + - (comment.created_at ? ' · ' + escapeHtml(fmt(comment.created_at)) : '') + - '
' + actions + '
' + - renderMarkdown(comment.body || 'No comment body provided.') + '
'; - } - - function paintIssueConversation(state, controller) { - const comments = state?.comments || []; - qs('#issue-comments').innerHTML = comments.length ? - comments.map(comment => renderIssueComment(comment, controller)).join('') : '
No comments yet.
'; - qs('#load-older-issue-comments').hidden = !Number.isInteger(state?.older_page); - qs('#issue-conversation-status').textContent = comments.length ? - comments.length + ' of ' + Math.max(state.total || 0, comments.length) + ' messages loaded.' : 'No comments yet.'; - } - - function paintUpdateConversation(state, controller) { - const comments = state?.comments || []; - qs('#update-comments').innerHTML = comments.length ? - comments.map(comment => renderIssueComment(comment, controller)).join('') : '
No comments yet.
'; - 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.'; - const newest = qs('#update-comments .issue-comment:last-child'); - updateReadPosition.ready(String(selectedUpdate?.notification_id || ''), newest); - } - - function paintPullConversation(state, controller) { - const comments = state?.comments || []; - qs('#pull-comments').innerHTML = comments.length ? comments.map(comment => - '
' + renderIssueComment(comment, controller) + '
' - ).join('') : '
No comments yet.
'; - qs('#load-older-pull-comments').hidden = !Number.isInteger(state?.older_page); - qs('#pull-conversation-status').textContent = comments.length ? - comments.length + ' of ' + Math.max(state.total || 0, comments.length) + ' messages loaded.' : 'No comments yet.'; - } + const {paintIssueConversation,paintPullConversation,paintUpdateConversation}=createConversationRenderers({ + qs,renderComment:(comment,controller)=>renderConversationComment( + comment,controller,escapeHtml,fmt,renderMarkdown), + updateReadPosition,getSelectedUpdate:()=>selectedUpdate, + }); function commentSurface(selector) { + if (selector === '#search-preview-comments') return { + context:{kind:searchPreviewDetail.kind,item:searchPreviewDetail}, pager:searchPreview.commentPager(), + render:state=>showSearchConversationWithActions(state), status:qs('#search-preview-conversation-status'), + }; if (selector === '#issue-comments') return { context:{kind:'issue',item:selectedIssue}, pager:issueConversation, render:renderIssueConversation, status:qs('#issue-sheet-status'), @@ -5664,8 +5635,14 @@ commandSearch.setScope(scope); } let searchPreviewDetail = null; - const renderSearchConversation = conversation => - renderSearchPreviewConversation(conversation, document, escapeHtml, fmt, renderMarkdown); + const showSearchConversationWithActions = createSearchPreviewConversationActions({ + hydrator:actionHydrator, rootNode:qs('#search-preview-comments'), + retry:qs('#retry-search-preview-comment-actions'), + paint:(conversation,controller)=>renderSearchPreviewConversation( + conversation,document,escapeHtml,fmt,renderMarkdown,controller), + wire:controller=>wireCommentActions('#search-preview-comments',controller), + }); + const renderSearchConversation = conversation => { void showSearchConversationWithActions(conversation); }; function renderSearchPreview(state) { followingQueue.preview(state); const sheet = qs('#search-preview'); diff --git a/frontend/index.html b/frontend/index.html index c903f29..efc1207 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -901,6 +901,7 @@
+