Skip to content

Commit

Permalink
feat: fetch comments with related replies (#106)
Browse files Browse the repository at this point in the history
支持加载评论列表时同时加载对应回复。

<img width="895" alt="image" src="https://github.com/halo-dev/plugin-comment-widget/assets/21301288/a70d8490-fe9f-4361-a246-9f8638f5b646">

TODO:

- [x] 在插件设置中提供设置,是否启用自动加载回复,以及默认加载条数

```release-note
支持加载评论列表时同时加载对应回复。
```
  • Loading branch information
ruibaby committed Mar 19, 2024
1 parent d29197e commit 4c3da7e
Show file tree
Hide file tree
Showing 13 changed files with 274 additions and 63 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Expand Up @@ -42,4 +42,4 @@ build {

halo {
version = "2.13.0"
}
}
3 changes: 2 additions & 1 deletion packages/comment-widget/src/base-comment-item-action.ts
Expand Up @@ -21,7 +21,7 @@ export class BaseCommentItemAction extends LitElement {
display: inline-flex;
align-items: center;
cursor: pointer;
margin-right: 0.5em;
gap: 0.1em;
}
.item-action__icon {
Expand All @@ -44,6 +44,7 @@ export class BaseCommentItemAction extends LitElement {
.item-action__text {
color: var(--base-info-color);
user-select: none;
font-size: 0.75em;
}
.item-action:hover .item-action__icon {
Expand Down
1 change: 1 addition & 0 deletions packages/comment-widget/src/base-comment-item.ts
Expand Up @@ -108,6 +108,7 @@ export class BaseCommentItem extends LitElement {
margin-top: 0.5em;
display: flex;
align-items: center;
gap: 0.7em;
}
.item--animate-breath {
Expand Down
116 changes: 92 additions & 24 deletions packages/comment-widget/src/comment-item.ts
Expand Up @@ -7,9 +7,11 @@ import './user-avatar';
import './base-comment-item';
import './base-comment-item-action';
import { consume } from '@lit/context';
import { baseUrlContext } from './context';
import { baseUrlContext, withRepliesContext } from './context';
import { LS_UPVOTED_COMMENTS_KEY } from './constant';
import varStyles from './styles/var';
import { Ref, createRef, ref } from 'lit/directives/ref.js';
import { CommentReplies } from './comment-replies';

export class CommentItem extends LitElement {
@consume({ context: baseUrlContext })
Expand All @@ -19,19 +21,33 @@ export class CommentItem extends LitElement {
@property({ type: Object })
comment: CommentVo | undefined;

@consume({ context: withRepliesContext, subscribe: true })
@state()
withReplies = false;

@state()
showReplies = false;

@state()
showReplyForm = false;

@state()
upvoted = false;

@state()
upvoteCount = 0;

commentRepliesRef: Ref<CommentReplies> = createRef<CommentReplies>();

override connectedCallback(): void {
super.connectedCallback();
this.checkUpvotedStatus();

if (this.withReplies) {
this.showReplies = true;
this.showReplyForm = false;
}

this.upvoteCount = this.comment?.stats.upvote || 0;
}

Expand Down Expand Up @@ -72,6 +88,19 @@ export class CommentItem extends LitElement {
this.checkUpvotedStatus();
}

handleShowReplies() {
this.showReplies = !this.showReplies;

if (!this.withReplies) {
this.showReplyForm = !this.showReplyForm;
}
}

onReplyCreated() {
this.commentRepliesRef.value?.fetchReplies();
this.showReplies = true;
}

override render() {
return html`<base-comment-item
.userAvatar="${this.comment?.owner.avatar}"
Expand Down Expand Up @@ -121,32 +150,67 @@ export class CommentItem extends LitElement {
</svg>`}
</base-comment-item-action>
<base-comment-item-action
slot="action"
.text="${(this.comment?.status?.visibleReplyCount || 0) + ''}"
@click="${() => (this.showReplies = !this.showReplies)}"
>
<svg
slot="icon"
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m3 20l1.3-3.9C1.976 12.663 2.874 8.228 6.4 5.726c3.526-2.501 8.59-2.296 11.845.48c3.255 2.777 3.695 7.266 1.029 10.501C16.608 19.942 11.659 20.922 7.7 19L3 20"
/>
</svg>
</base-comment-item-action>
${this.withReplies && this.comment?.status?.visibleReplyCount === 0
? ''
: html`<base-comment-item-action
slot="action"
.text="${(this.comment?.status?.visibleReplyCount || 0) + ''}"
@click="${this.handleShowReplies}"
>
<svg
slot="icon"
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m3 20l1.3-3.9C1.976 12.663 2.874 8.228 6.4 5.726c3.526-2.501 8.59-2.296 11.845.48c3.255 2.777 3.695 7.266 1.029 10.501C16.608 19.942 11.659 20.922 7.7 19L3 20"
/>
</svg>
</base-comment-item-action>`}
${this.withReplies
? html` <base-comment-item-action
slot="action"
.text=${this.showReplyForm ? '取消回复' : '加入回复'}
@click="${() => (this.showReplyForm = !this.showReplyForm)}"
>
<svg
slot="icon"
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 24 24"
>
<path
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12.007 19.98A9.869 9.869 0 0 1 7.7 19L3 20l1.3-3.9C1.976 12.663 2.874 8.228 6.4 5.726c3.526-2.501 8.59-2.296 11.845.48c1.992 1.7 2.93 4.04 2.747 6.34M16 19h6m-3-3v6"
/>
</svg>
</base-comment-item-action>`
: ''}
<div slot="footer">
${this.showReplyForm
? html`<div class="item__reply-form">
<reply-form @reload=${this.onReplyCreated} .comment=${this.comment}></reply-form>
</div>`
: ''}
${this.showReplies
? html`<comment-replies .comment="${this.comment}"></comment-replies>`
? html`<comment-replies
${ref(this.commentRepliesRef)}
.comment="${this.comment}"
.showReplyForm=${this.showReplyForm}
></comment-replies>`
: ``}
</div>
</base-comment-item>`;
Expand All @@ -159,6 +223,10 @@ export class CommentItem extends LitElement {
.item__action--upvote {
margin-left: -0.5em;
}
.item__reply-form {
margin-top: 0.5em;
}
`,
];
}
Expand Down
115 changes: 95 additions & 20 deletions packages/comment-widget/src/comment-replies.ts
@@ -1,9 +1,9 @@
import { CommentVo, ReplyVo } from '@halo-dev/api-client';
import { CommentVo, ReplyVo, ReplyVoList } from '@halo-dev/api-client';
import { LitElement, css, html } from 'lit';
import { property, state } from 'lit/decorators.js';
import { repeat } from 'lit/directives/repeat.js';
import { consume } from '@lit/context';
import { baseUrlContext, toastContext } from './context';
import { baseUrlContext, replySizeContext, toastContext, withRepliesContext } from './context';
import './reply-item';
import './loading-block';
import './reply-form';
Expand All @@ -16,12 +16,29 @@ export class CommentReplies extends LitElement {
@property({ attribute: false })
baseUrl = '';

@consume({ context: withRepliesContext, subscribe: true })
@state()
withReplies = false;

@consume({ context: replySizeContext, subscribe: true })
@state()
replySize = 10;

@property({ type: Object })
comment: CommentVo | undefined;

@property({ type: Boolean })
showReplyForm = false;

@state()
replies: ReplyVo[] = [];

@state()
page = 1;

@state()
hasNext = false;

@state()
loading = false;

Expand All @@ -34,10 +51,8 @@ export class CommentReplies extends LitElement {

override render() {
return html`<div class="replies__wrapper">
<reply-form @reload=${this.fetchReplies} .comment=${this.comment}></reply-form>
${this.loading
? html`<loading-block></loading-block>`
: html`
${this.replies.length
? html`
<div class="replies__list">
${repeat(
this.replies,
Expand All @@ -53,55 +68,115 @@ export class CommentReplies extends LitElement {
></reply-item>`
)}
</div>
`}
`
: ''}
${this.loading ? html`<loading-block></loading-block>` : ''}
${this.hasNext && !this.loading
? html`<div class="replies__next-wrapper">
<button @click=${this.fetchNext}>加载更多</button>
</div>`
: ''}
</div>`;
}

onSetActiveQuoteReply(event: CustomEvent) {
this.activeQuoteReply = event.detail.quoteReply;
}

async fetchReplies() {
if (this.replies.length === 0) {
async fetchReplies(options?: { append: boolean }) {
try {
this.loading = true;
}

try {
// Reload replies list
if (!options?.append) {
this.page = 1;
}

const queryParams = [`page=${this.page || 0}`, `size=${this.replySize}`];

const response = await fetch(
`${this.baseUrl}/apis/api.halo.run/v1alpha1/comments/${this.comment?.metadata.name}/reply`
`${this.baseUrl}/apis/api.halo.run/v1alpha1/comments/${this.comment?.metadata.name}/reply?${queryParams.join('&')}`
);

if (!response.ok) {
throw new Error('加载回复列表失败,请稍后重试');
}

const data = await response.json();
this.replies = data.items;
const data = (await response.json()) as ReplyVoList;

if (options?.append) {
this.replies = this.replies.concat(data.items);
} else {
this.replies = data.items;
}

this.hasNext = data.hasNext;
this.page = data.page;
} catch (error) {
if (error instanceof Error) {
this.toastManager?.error(error.message);
}
} finally {
this.loading = false;
}
}

this.loading = false;
async fetchNext() {
this.page++;
this.fetchReplies({ append: true });
}

override connectedCallback(): void {
super.connectedCallback();
this.fetchReplies();

if (this.withReplies) {
// TODO: Fix ts error
// Needs @halo-dev/[email protected]
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.replies = this.comment?.replies.items;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.page = this.comment?.replies.page;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.hasNext = this.comment?.replies.hasNext;
} else {
this.fetchReplies();
}
}

static override styles = [
varStyles,
baseStyles,
css`
.replies__wrapper {
margin-top: 0.5em;
}
.replies__list {
margin-top: 0.875em;
}
.replies__next-wrapper {
display: flex;
justify-content: center;
margin: 0.5em 0;
}
.replies__next-wrapper button {
border-radius: var(--base-border-radius);
color: var(--base-color);
font-size: 0.875em;
display: inline-flex;
align-items: center;
font-weight: 600;
padding: 0.4em 0.875em;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 0.15s;
border: 1px solid transparent;
}
.replies__next-wrapper button:hover {
background-color: var(--component-pagination-button-bg-color-hover);
}
`,
];
}
Expand Down

0 comments on commit 4c3da7e

Please sign in to comment.