From ce33f16dd0d7e0ded40c370fde67cfb662ba1d75 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Thu, 20 Aug 2026 16:50:37 +0800 Subject: [PATCH] Fix reply pagination after preloading --- packages/comment-widget/package.json | 3 +- packages/comment-widget/src/comment-list.ts | 6 +- .../comment-widget/src/comment-replies.ts | 66 ++++++++++++------- .../src/utils/reply-pagination.ts | 40 +++++++++++ .../tests/reply-pagination.test.ts | 50 ++++++++++++++ src/main/resources/extensions/settings.yaml | 12 +++- 6 files changed, 148 insertions(+), 29 deletions(-) create mode 100644 packages/comment-widget/src/utils/reply-pagination.ts create mode 100644 packages/comment-widget/tests/reply-pagination.test.ts diff --git a/packages/comment-widget/package.json b/packages/comment-widget/package.json index 506cbb9..a2f5468 100644 --- a/packages/comment-widget/package.json +++ b/packages/comment-widget/package.json @@ -36,7 +36,8 @@ "dev": "vite build --watch", "locale:build": "lit-localize build", "locale:extract": "lit-localize extract", - "prepublishOnly": "pnpm run build" + "prepublishOnly": "pnpm run build", + "test": "node --test tests/*.test.ts" }, "dependencies": { "@emoji-mart/data": "^1.2.1", diff --git a/packages/comment-widget/src/comment-list.ts b/packages/comment-widget/src/comment-list.ts index c43ed52..be62c38 100644 --- a/packages/comment-widget/src/comment-list.ts +++ b/packages/comment-widget/src/comment-list.ts @@ -22,6 +22,7 @@ import './loading-block'; import { when } from 'lit/directives/when.js'; import { ofetch } from 'ofetch'; import baseStyles from './styles/base'; +import { getInitialReplySize } from './utils/reply-pagination'; export class CommentList extends LitElement { @consume({ context: baseUrlContext }) @@ -106,6 +107,9 @@ export class CommentList extends LitElement { this.comments.page = page; } + const replySize = this.configMapData?.basic.replySize ?? 10; + const withReplySize = this.configMapData?.basic.withReplySize ?? 5; + const data = await ofetch( `${this.baseUrl}/apis/api.halo.run/v1alpha1/comments`, { @@ -117,7 +121,7 @@ export class CommentList extends LitElement { size: this.configMapData?.basic.size || 20, version: this.version, withReplies: this.configMapData?.basic.withReplies || false, - replySize: this.configMapData?.basic.replySize || 10, + replySize: getInitialReplySize(withReplySize, replySize), }, } ); diff --git a/packages/comment-widget/src/comment-replies.ts b/packages/comment-widget/src/comment-replies.ts index 564d585..1db400a 100644 --- a/packages/comment-widget/src/comment-replies.ts +++ b/packages/comment-widget/src/comment-replies.ts @@ -13,6 +13,7 @@ import { ofetch } from 'ofetch'; import type { ToastManager } from './lit-toast'; import baseStyles from './styles/base'; import type { ConfigMapData } from './types'; +import { getNextReplyRequest } from './utils/reply-pagination'; export class CommentReplies extends LitElement { @consume({ context: baseUrlContext }) @@ -35,6 +36,10 @@ export class CommentReplies extends LitElement { @state() page = 1; + private currentPageSize = 0; + + private preloaded = false; + @state() hasNext = false; @@ -82,14 +87,16 @@ export class CommentReplies extends LitElement { this.activeQuoteReply = event.detail.quoteReply; } - async fetchReplies(options?: { append: boolean }) { + async fetchReplies(options?: { + page?: number; + size?: number; + append?: boolean; + }) { try { this.loading = true; - // Reload replies list - if (!options?.append) { - this.page = 1; - } + const page = options?.page ?? 1; + const size = options?.size ?? this.configMapData?.basic.replySize ?? 10; const data = await ofetch( `${this.baseUrl}/apis/api.halo.run/v1alpha1/comments/${ @@ -97,8 +104,8 @@ export class CommentReplies extends LitElement { }/reply`, { query: { - page: this.page || 1, - size: this.configMapData?.basic.replySize || 10, + page, + size, }, } ); @@ -111,6 +118,8 @@ export class CommentReplies extends LitElement { this.hasNext = data.hasNext; this.page = data.page; + this.currentPageSize = data.size; + this.preloaded = false; } catch (error) { console.error(error); this.toastManager?.error( @@ -122,31 +131,38 @@ export class CommentReplies extends LitElement { } async fetchNext() { - if (this.configMapData?.basic.withReplies) { - // if withReplies is true, we need to reload the replies list - await this.fetchReplies({ append: !(this.page === 1) }); - this.page++; - } else { - this.page++; - await this.fetchReplies({ append: true }); + if (this.loading || !this.hasNext) { + return; } + + const request = getNextReplyRequest({ + page: this.page, + currentPageSize: this.currentPageSize, + replySize: this.configMapData?.basic.replySize ?? 10, + preloaded: this.preloaded, + }); + + await this.fetchReplies(request); } override connectedCallback(): void { super.connectedCallback(); if (this.configMapData?.basic.withReplies) { - // TODO: Fix ts error - // Needs @halo-dev/api-client@2.14.0 - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - this.replies = this.comment?.replies.items; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - this.page = this.comment?.replies.page; - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - this.hasNext = this.comment?.replies.hasNext; + const preloadedReplies = ( + this.comment as (CommentVo & { replies?: ReplyVoList }) | undefined + )?.replies; + + if (!preloadedReplies) { + this.fetchReplies(); + return; + } + + this.replies = preloadedReplies.items; + this.page = preloadedReplies.page; + this.currentPageSize = preloadedReplies.size; + this.hasNext = preloadedReplies.hasNext; + this.preloaded = true; } else { this.fetchReplies(); } diff --git a/packages/comment-widget/src/utils/reply-pagination.ts b/packages/comment-widget/src/utils/reply-pagination.ts new file mode 100644 index 0000000..3b27a61 --- /dev/null +++ b/packages/comment-widget/src/utils/reply-pagination.ts @@ -0,0 +1,40 @@ +export interface NextReplyRequestOptions { + page: number; + currentPageSize: number; + replySize: number; + preloaded: boolean; +} + +export interface NextReplyRequest { + page: number; + size: number; + append: boolean; +} + +export function getInitialReplySize( + withReplySize: number, + replySize: number +): number { + return Math.min(Math.max(1, withReplySize), Math.max(1, replySize)); +} + +export function getNextReplyRequest({ + page, + currentPageSize, + replySize, + preloaded, +}: NextReplyRequestOptions): NextReplyRequest { + if (preloaded && currentPageSize < replySize) { + return { + page, + size: replySize, + append: false, + }; + } + + return { + page: page + 1, + size: replySize, + append: true, + }; +} diff --git a/packages/comment-widget/tests/reply-pagination.test.ts b/packages/comment-widget/tests/reply-pagination.test.ts new file mode 100644 index 0000000..4797127 --- /dev/null +++ b/packages/comment-widget/tests/reply-pagination.test.ts @@ -0,0 +1,50 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { + getInitialReplySize, + getNextReplyRequest, +} from '../src/utils/reply-pagination.ts'; + +test('uses withReplySize for the initial replies request', () => { + assert.equal(getInitialReplySize(2, 4), 2); +}); + +test('fills the first page when fewer replies were preloaded', () => { + assert.deepEqual( + getNextReplyRequest({ + page: 1, + currentPageSize: 2, + replySize: 4, + preloaded: true, + }), + { page: 1, size: 4, append: false } + ); +}); + +test('loads page two when a full first page was preloaded', () => { + assert.deepEqual( + getNextReplyRequest({ + page: 1, + currentPageSize: 4, + replySize: 4, + preloaded: true, + }), + { page: 2, size: 4, append: true } + ); +}); + +test('continues with the next page after the preload is reconciled', () => { + assert.deepEqual( + getNextReplyRequest({ + page: 2, + currentPageSize: 4, + replySize: 4, + preloaded: false, + }), + { page: 3, size: 4, append: true } + ); +}); + +test('clamps legacy preload sizes to the reply page size', () => { + assert.equal(getInitialReplySize(5, 3), 3); +}); diff --git a/src/main/resources/extensions/settings.yaml b/src/main/resources/extensions/settings.yaml index 9fbfe42..cdf1c41 100644 --- a/src/main/resources/extensions/settings.yaml +++ b/src/main/resources/extensions/settings.yaml @@ -15,7 +15,11 @@ spec: - $formkit: number label: 回复分页条数 name: replySize - validation: required + id: replySize + key: replySize + min: 1 + step: 1 + validation: required|min:1 value: 10 - $formkit: checkbox label: 同时加载评论的回复 @@ -29,7 +33,11 @@ spec: name: withReplySize id: withReplySize key: withReplySize - validation: required + min: 1 + max: "$get(replySize).value" + step: 1 + validation: required|min:1 + help: 不能大于回复分页条数 value: 5 - $formkit: checkbox label: 显示评论者设备信息