Skip to content

Commit 869aa11

Browse files
committed
调整留言板 api 支持嵌套回复
1 parent 6a730cd commit 869aa11

3 files changed

Lines changed: 375 additions & 57 deletions

File tree

public/images/guest-book.png

3.02 MB
Loading

src/pages/guestbook/index.astro

Lines changed: 165 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
---
22
import comments from '@/data/comments.json';
33
import siteConfig from '@/site.config.ts';
4+
import BaseHead from '@/components/BaseHead.astro';
5+
import { Image } from 'astro:assets';
46
57
// Define types for comments and reaction groups
68
interface ReactionGroup {
@@ -21,11 +23,21 @@ interface Comment {
2123
};
2224
bodyHTML: string;
2325
reactionGroups?: ReactionGroup[]; // Optional as it might not always be present
26+
parentId: string | null;
27+
replyToId: string | null;
28+
replyToAuthor: string | null;
29+
level: number;
30+
type: string;
31+
discussionId: string;
32+
isDiscussion: boolean;
33+
title: string;
2434
}
2535
2636
const title = 'Guestbook';
2737
const description = 'A collection of all comments, styled like VS Code.';
2838
39+
const commentsById = new Map(comments.map((c: Comment) => [c.id, c]));
40+
2941
const discussionsUrl = `https://github.com/${siteConfig.integ.giscus?.repo}/discussions`;
3042
3143
// Helper function to format time ago
@@ -70,11 +82,7 @@ function getReactionEmoji(reaction: string) {
7082
<!DOCTYPE html>
7183
<html lang="zh-CN">
7284
<head>
73-
<meta charset="UTF-8" />
74-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
75-
<title>{title}</title>
76-
<meta name="description" content={description} />
77-
<meta property="og:title" content={title} />
85+
<BaseHead title={title} description={description} ogImage='/images/guest-book.png' />
7886
<style>
7987
:root {
8088
--accent-color: #0969da;
@@ -117,10 +125,36 @@ function getReactionEmoji(reaction: string) {
117125
border-right: 1px solid var(--border-color);
118126
display: flex;
119127
flex-direction: column;
128+
-ms-overflow-style: none; /* IE and Edge */
129+
scrollbar-width: none; /* Firefox */
130+
}
131+
132+
.comments-pane::-webkit-scrollbar {
133+
display: none; /* Chrome, Safari, Opera*/
120134
}
121135

122136
.giscus-pane {
123137
width: 40%;
138+
display: flex;
139+
flex-direction: column;
140+
overflow-y: auto;
141+
}
142+
143+
.giscus-image-container {
144+
display: flex;
145+
justify-content: center;
146+
align-items: center;
147+
margin-bottom: 1.5rem; /* Space between image and Giscus */
148+
}
149+
150+
.giscus-image-container img {
151+
width: 100%;
152+
height: 100%;
153+
object-fit: cover;
154+
}
155+
156+
.giscus-comments-container {
157+
flex-grow: 1; /* Giscus takes remaining space */
124158
padding: 1.5rem;
125159
}
126160

@@ -200,6 +234,10 @@ function getReactionEmoji(reaction: string) {
200234
opacity: 1;
201235
}
202236

237+
.comment-item.highlighted-comment {
238+
background: #fffacd; /* A more noticeable light yellow for highlighting */
239+
}
240+
203241
.line-number {
204242
width: 50px;
205243
color: var(--text-secondary-color);
@@ -307,6 +345,49 @@ function getReactionEmoji(reaction: string) {
307345
border-color: var(--accent-color);
308346
}
309347

348+
.quoted-reply {
349+
border-left: 3px solid var(--border-color);
350+
padding-left: 12px;
351+
margin-bottom: 8px;
352+
font-size: 13px;
353+
color: var(--text-secondary-color);
354+
}
355+
.quoted-reply p {
356+
margin-bottom: 4px;
357+
}
358+
.quoted-reply a {
359+
color: var(--accent-color);
360+
text-decoration: none;
361+
}
362+
.quoted-reply a:hover {
363+
text-decoration: underline;
364+
}
365+
.quoted-reply blockquote {
366+
margin: 0;
367+
padding: 0;
368+
border: none;
369+
max-height: 7em; /* Limit height of quoted text */
370+
overflow: hidden;
371+
text-overflow: ellipsis;
372+
opacity: 0.8;
373+
position: relative;
374+
}
375+
.quoted-reply blockquote::after {
376+
content: '';
377+
position: absolute;
378+
bottom: 0;
379+
left: 0;
380+
right: 0;
381+
height: 1.5em;
382+
background: linear-gradient(to bottom, transparent, var(--bg-color));
383+
}
384+
.quoted-reply blockquote > *:first-child {
385+
margin-top: 0;
386+
}
387+
.quoted-reply blockquote > *:last-child {
388+
margin-bottom: 0;
389+
}
390+
310391
/* Responsive */
311392
@media (max-width: 768px) {
312393
.guestbook-container {
@@ -366,8 +447,10 @@ function getReactionEmoji(reaction: string) {
366447
</div>
367448
<div class="comments-list">
368449
{
369-
comments.map((comment: Comment, index: number) => (
370-
<div class="comment-item">
450+
comments.map((comment: Comment, index: number) => {
451+
const repliedToComment = comment.replyToId ? commentsById.get(comment.replyToId) : null;
452+
return (
453+
<div class="comment-item" id={comment.id}>
371454
<div class="line-number">{comments.length - index}</div>
372455
<div class="comment-content-wrapper">
373456
<div class="comment-header">
@@ -387,6 +470,14 @@ function getReactionEmoji(reaction: string) {
387470
rel="noopener noreferrer">View on GitHub</a
388471
>
389472
</div>
473+
{ repliedToComment && (
474+
<div class="quoted-reply">
475+
<p>
476+
Replying to <a href={`#${repliedToComment.id}`}>@{comment.replyToAuthor}</a>
477+
</p>
478+
<blockquote set:html={repliedToComment.bodyHTML} />
479+
</div>
480+
)}
390481
<div class="comment-body" set:html={comment.bodyHTML} />
391482
<div class="reactions">
392483
{comment.reactionGroups &&
@@ -401,37 +492,84 @@ function getReactionEmoji(reaction: string) {
401492
</div>
402493
</div>
403494
</div>
404-
))
495+
)})
405496
}
406497
</div>
407498
<div class="see-more-container">
408499
<a href={discussionsUrl} class="see-more-btn" target="_blank" rel="noopener noreferrer">
409-
See more on GitHub
500+
See more on GitHub
410501
</a>
411502
</div>
412503
</div>
413504

414505
<!-- Right Pane: Giscus -->
415506
<div class="giscus-pane">
416-
<script
417-
is:inline
418-
src="https://giscus.app/client.js"
419-
data-repo="CatCodeMe/catcodeme.github.io"
420-
data-repo-id="R_kgDOLTuIuQ"
421-
data-category="General"
422-
data-category-id="DIC_kwDOLTuIuc4Csz7O"
423-
data-mapping="og:title"
424-
data-strict="0"
425-
data-reactions-enabled="1"
426-
data-emit-metadata="0"
427-
data-input-position="top"
428-
data-theme="light"
429-
data-lang="en"
430-
crossorigin="anonymous"
431-
async
432-
>
507+
<div class="giscus-image-container">
508+
<Image src="/images/guest-book.png" alt="Guest Book @8cat.life" width={1200} height={630} />
509+
</div>
510+
<div class="giscus-comments-container">
511+
<script
512+
is:inline
513+
src="https://giscus.app/client.js"
514+
data-repo="CatCodeMe/catcodeme.github.io"
515+
data-repo-id="R_kgDOLTuIuQ"
516+
data-category="General"
517+
data-category-id="DIC_kwDOLTuIuc4Csz7O"
518+
data-mapping="og:title"
519+
data-strict="0"
520+
data-reactions-enabled="1"
521+
data-emit-metadata="0"
522+
data-input-position="top"
523+
data-theme="light"
524+
data-lang="en"
525+
crossorigin="anonymous"
526+
async
527+
>
528+
</script>
529+
</div>
530+
<script is:inline>
531+
function applyHighlight() {
532+
const hash = window.location.hash.substring(1);
533+
document.querySelectorAll('.comment-item.highlighted-comment').forEach(el => {
534+
el.classList.remove('highlighted-comment');
535+
});
536+
if (hash) {
537+
const targetComment = document.getElementById(hash);
538+
if (targetComment) {
539+
targetComment.classList.add('highlighted-comment');
540+
// Optional: Scroll into view smoothly if not already in view
541+
targetComment.scrollIntoView({ behavior: 'smooth', block: 'center' });
542+
// Remove highlight after a short delay
543+
setTimeout(() => {
544+
targetComment.classList.remove('highlighted-comment');
545+
}, 2000); // 2 seconds
546+
}
547+
}
548+
}
549+
550+
// Apply highlight on initial load if hash is present
551+
document.addEventListener('DOMContentLoaded', applyHighlight);
552+
// Apply highlight when hash changes (e.g., clicking a reply link)
553+
window.addEventListener('hashchange', applyHighlight);
554+
555+
// Listen for clicks on internal anchor links within the comments list
556+
document.addEventListener('click', (event) => {
557+
const target = event.target;
558+
// Check if the clicked element is an anchor tag within a .quoted-reply and its href starts with '#'
559+
if (target.tagName === 'A' && target.closest('.quoted-reply') && target.getAttribute('href')?.startsWith('#')) {
560+
const hash = target.getAttribute('href');
561+
if (hash && window.location.hash !== hash) { // Only update if hash is different to avoid unnecessary history entries
562+
event.preventDefault(); // Prevent default browser jump
563+
window.location.hash = hash; // Manually update hash, which should trigger hashchange
564+
} else if (hash && window.location.hash === hash) {
565+
// If clicking the same hash, hashchange won't fire, so manually apply highlight
566+
event.preventDefault(); // Prevent default browser jump
567+
applyHighlight();
568+
}
569+
}
570+
});
433571
</script>
434572
</div>
435573
</div>
436574
</body>
437-
</html>
575+
</html>

0 commit comments

Comments
 (0)