feat(messages.html): user badges, contact search, auto-scroll to latest#946
Conversation
- Desktop/mobile login tab priority: bookmarklet on desktop (pointer: fine), session on mobile - Replace sender name links with Bootstrap popovers showing user info popup - Image zoom: cursor: zoom-in/zoom-out, title tooltip on images - Global Ctrl+V paste handler for images anywhere in thread view - Auto-resize compose textarea as user types Co-authored-by: PythonSmall-Q <106425289+PythonSmall-Q@users.noreply.github.com>
Deploying xmoj-script-dev-channel with
|
| Latest commit: |
d4f7bb1
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://63600953.xmoj-script-dev-channel.pages.dev |
| Branch Preview URL: | https://copilot-sub-pr-944-again.xmoj-script-dev-channel.pages.dev |
Co-authored-by: PythonSmall-Q <106425289+PythonSmall-Q@users.noreply.github.com>
Co-authored-by: PythonSmall-Q <106425289+PythonSmall-Q@users.noreply.github.com>
…nfo modal Co-authored-by: PythonSmall-Q <106425289+PythonSmall-Q@users.noreply.github.com>
|
@copilot 访问xmoj.tech的时候origin设置为xmoj.tech |
…in userinfo fetch Co-authored-by: PythonSmall-Q <106425289+PythonSmall-Q@users.noreply.github.com>
Done in 7678bd5. The
Note: the browser-controlled |
Co-authored-by: PythonSmall-Q <106425289+PythonSmall-Q@users.noreply.github.com>
…ck + auto-scroll fix Co-authored-by: PythonSmall-Q <106425289+PythonSmall-Q@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR improves the messages.html WebUI user experience by adding user badges and a user-info modal, enabling local (no-API) contact search, and making “scroll to latest” more reliable during thread rendering.
Changes:
- Add clickable usernames that open a user-info modal and display cached user badges (via
GetBadge) next to usernames. - Add a local contact search input backed by a cached
GetMailListresponse to filter without additional API calls. - Improve auto-scroll behavior using
requestAnimationFrameand re-scrolling on image load; adjust table header styling for dark mode.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function formatTime(ts) { | ||
| if (!ts) return ''; | ||
| var d = new Date(ts * 1000); | ||
| var d = new Date(ts); |
| async function showUserInfo(username) { | ||
| var title = document.getElementById('user-info-modal-title'); | ||
| var body = document.getElementById('user-info-modal-body'); | ||
| var profileLink = document.getElementById('user-info-profile-link'); | ||
| var profileUrl = XMOJ_BASE + '/userinfo.php?user=' + encodeURIComponent(username); | ||
|
|
||
| title.textContent = username; | ||
| profileLink.href = profileUrl; | ||
| body.innerHTML = '<div class="text-center py-3"><span class="spinner-border spinner-border-sm me-2"></span>加载中…</div>'; | ||
| userInfoModalBS.show(); | ||
|
|
||
| var results = await Promise.allSettled([ | ||
| fetch(profileUrl, { referrer: XMOJ_BASE + '/', credentials: 'include' }) | ||
| .then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); }), | ||
| getUserBadge(username) | ||
| ]); | ||
|
|
||
| // ── Badge (render into title) ─────────────────────────────────────────── | ||
| var badge = results[1].status === 'fulfilled' ? results[1].value : null; | ||
| if (badge && badge.Content) { | ||
| title.innerHTML = escapeHtml(username) + | ||
| ' <span class="badge ms-2" style="background-color:' + escapeHtml(badge.BackgroundColor) + | ||
| ';color:' + escapeHtml(badge.Color) + '">' + escapeHtml(badge.Content) + '</span>'; | ||
| } |
| var submitEl = doc.querySelector('#statics > tbody > tr:nth-child(3) > td:nth-child(2)'); | ||
| var acceptEl = doc.querySelector('#statics > tbody > tr:nth-child(4) > td:nth-child(2)'); | ||
| var submitCount = submitEl ? (parseInt(submitEl.textContent.trim()) || 0) : 0; | ||
| var acceptCount = acceptEl ? (parseInt(acceptEl.textContent.trim()) || 0) : 0; | ||
| var rating = submitCount > 0 ? ((acceptCount / submitCount) * 1000).toFixed(1) : '—'; | ||
|
|
||
| var tbodyRows = doc.querySelectorAll('#statics > tbody > tr'); | ||
| var email = ''; | ||
| if (tbodyRows.length > 0) { | ||
| var lastCells = tbodyRows[tbodyRows.length - 1].querySelectorAll('td'); | ||
| if (lastCells.length >= 2) email = lastCells[1].textContent.trim(); |
| // Re-scroll after each image loads so the bottom stays in view | ||
| if (atBottom) { | ||
| img.addEventListener('load', function() { | ||
| scrollEl.scrollTop = scrollEl.scrollHeight; | ||
| }); |
There was a problem hiding this comment.
2 issues found across 1 file
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="messages.html">
<violation number="1" location="messages.html:513">
P2: Out-of-order `showUserInfo()` responses can overwrite the profile that is currently open.</violation>
<violation number="2" location="messages.html:660">
P2: Scope or clear `mailListCache` on session changes; otherwise search can briefly expose the previous user's inbox after re-login.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| var results = await Promise.allSettled([ | ||
| fetch(profileUrl, { referrer: XMOJ_BASE + '/', credentials: 'include' }) | ||
| .then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); }), | ||
| getUserBadge(username) | ||
| ]); |
There was a problem hiding this comment.
P2: Out-of-order showUserInfo() responses can overwrite the profile that is currently open.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At messages.html, line 513:
<comment>Out-of-order `showUserInfo()` responses can overwrite the profile that is currently open.</comment>
<file context>
@@ -397,18 +434,153 @@ <h5 class="mb-0 flex-grow-1" id="thread-title">与 … 的对话</h5>
+ body.innerHTML = '<div class="text-center py-3"><span class="spinner-border spinner-border-sm me-2"></span>加载中…</div>';
+ userInfoModalBS.show();
+
+ var results = await Promise.allSettled([
+ fetch(profileUrl, { referrer: XMOJ_BASE + '/', credentials: 'include' })
+ .then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); }),
</file context>
| var results = await Promise.allSettled([ | |
| fetch(profileUrl, { referrer: XMOJ_BASE + '/', credentials: 'include' }) | |
| .then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); }), | |
| getUserBadge(username) | |
| ]); | |
| var requestId = String(Date.now()) + Math.random(); | |
| title.dataset.requestId = requestId; | |
| var results = await Promise.allSettled([ | |
| fetch(profileUrl, { referrer: XMOJ_BASE + '/', credentials: 'include' }) | |
| .then(function(r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); }), | |
| getUserBadge(username) | |
| ]); | |
| if (title.dataset.requestId !== requestId) return; |
| } | ||
|
|
||
| // ── Mail List ────────────────────────────────────────────────────────────── | ||
| function renderMailList(query) { |
There was a problem hiding this comment.
P2: Scope or clear mailListCache on session changes; otherwise search can briefly expose the previous user's inbox after re-login.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At messages.html, line 660:
<comment>Scope or clear `mailListCache` on session changes; otherwise search can briefly expose the previous user's inbox after re-login.</comment>
<file context>
@@ -485,35 +657,53 @@ <h5 class="mb-0 flex-grow-1" id="thread-title">与 … 的对话</h5>
}
// ── Mail List ──────────────────────────────────────────────────────────────
+function renderMailList(query) {
+ var tbody = document.getElementById('list-tbody');
+ var q = query ? query.trim().toLowerCase() : '';
</file context>
Adds three UX improvements to the messages WebUI: user badge display (mirroring
XMOJ.user.js), a local contact search box, and reliable auto-scroll to the latest message.User badges
getUserBadge(username)— callsapiCall('GetBadge', { UserID }), caches result 24 h in localStorage underUserScript-User-{u}-Badge-{BackgroundColor|Color|Content|LastUpdateTime}(same keys asXMOJ.user.js)loadBadgesForContainer(container)— single DOM pass using aMapkeyed by username; for each unique user fires onegetUserBadge()call, then inserts a Bootstrap badge<span>after every matching.user-info-btnviainsertAdjacentElementinitUserButtonsin bothrenderMailListandloadThread; also rendered in the user-info modal titleuser-badgeclass check onnextElementSiblingContact search
mailListCachestores the lastGetMailListresponserenderMailList(query)filtersOtherUsercase-insensitively; no API call<input type="search" id="list-search">above the table drives filtering oninputeventrenderMailList('')before refreshing, so returning to the list always shows all contactsAuto-scroll
scrollTop = scrollHeightwithrequestAnimationFrame(…)so layout is computed before readingscrollHeightimg.addEventListener('load', …)inside thread render to re-scroll as images load in✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.
Summary by cubic
Adds a new
messages.htmlWebUI with local contact search and user badges next to usernames. Fixes dark‑mode table colors and timestamp formatting, and improves scroll‑to‑latest behavior.New Features
GetBadge.Bug Fixes
.table-primaryand removetable-lightheaders so unread rows render correctly.requestAnimationFrameand per‑image load handlers; clear search on Back to restore the full contact list.Written for commit d4f7bb1. Summary will update on new commits.