Skip to content

fix(comment): keep pagination state after replying to a comment#996

Merged
imaegoo merged 2 commits into
twikoojs:mainfrom
CuteLeaf:fix/keep-pagination-after-reply
Jun 22, 2026
Merged

fix(comment): keep pagination state after replying to a comment#996
imaegoo merged 2 commits into
twikoojs:mainfrom
CuteLeaf:fix/keep-pagination-after-reply

Conversation

@CuteLeaf

@CuteLeaf CuteLeaf commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

问题

在回复其他页的评论后,评论列表会重载回第一页,丢失已展开的分页状态。

原因

TkCommentonLoad() 在回复成功后 emit 'load' 事件,TkComments 收到后调用 initComments(),该方法总是加载第一页(无 before 参数),导致已加载的后续页被丢弃。

修改内容

TkComments.vue

  • 添加 loadedPages 跟踪已展开页数
  • 新增 refreshPreservingState() 方法,刷新时保持分页状态
  • TkComment 的 @load 改为调用 refreshPreservingState(顶层提交仍用 initComments

TkComment.vue

  • onLoad() 移除无效的 scrollIntoView(刷新前 DOM 未更新)
  • 新增 onRefreshed(),在刷新完成后滚动到最新回复"

Summary by Sourcery

在回复评论并刷新线程时,保留评论列表的分页和滚动行为。

Bug 修复:

  • 在发布回复后保持当前的评论页和已展开的分页状态,而不是重新加载并回到第一页。

功能增强:

  • 追踪已加载的评论页,并添加一个在保留分页状态的前提下重新加载评论的刷新方法。
  • 将回复后的滚动行为移动到评论刷新完成之后执行,使视图能够平滑滚动到最新回复。
Original summary in English

Summary by Sourcery

Preserve comment list pagination and scroll behavior when replying to comments and refreshing the thread.

Bug Fixes:

  • Keep the current comments page and expanded pagination state after posting a reply instead of reloading back to the first page.

Enhancements:

  • Track loaded comment pages and add a refresh method that reloads comments while preserving pagination state.
  • Move reply-scroll behavior to run after comments refresh completes so the view scrolls smoothly to the latest reply.

@sourcery-ai

sourcery-ai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

在回复评论后,通过跟踪已加载的页数并重新获取这些页面,以及将“滚动到最新回复”的行为延迟到刷新后的 DOM 渲染完成之后,来在刷新评论列表时保留当前已经展开的评论分页页面。

在回复评论后保留分页和滚动行为的时序图

sequenceDiagram
  actor User
  participant TkComment
  participant TkComments

  User->>TkComment: onReplyReply
  TkComment->>TkComment: onLoad
  TkComment->>TkComments: emit load
  TkComments->>TkComments: refreshPreservingState
  TkComments->>TkComments: getComments(url, sort)
  loop for each extra loaded page
    TkComments->>TkComments: getComments(url, before, sort)
  end
  TkComments->>TkComment: emit refreshed
  TkComment->>TkComment: onRefreshed
  TkComment->>TkComment: $nextTick
  TkComment->>TkComment: scrollIntoView(lastElementChild)
Loading

File-Level Changes

Change Details Files
当回复触发重新加载时,在主评论列表中保留分页状态。
  • 通过新增的 loadedPages data 字段在组件状态中跟踪已加载的评论页数。
  • 每当完整评论列表被刷新或排序方式改变时,将 loadedPages 重置为 1。
  • 每次用户展开加载更多评论时,递增 loadedPages
  • 引入 refreshPreservingState(),先重新加载第一页,然后使用从非顶部评论中得到的 before 游标,按顺序重新获取直至 loadedPages 指定的额外页面。
  • @load 处理函数从 initComments 切换为 refreshPreservingState,使得回复时在保留分页状态的前提下刷新,而其他顶层刷新流程仍然调用 initComments()
  • 在状态保留式刷新完成后发出 refreshed 事件,并在操作过程中管理 loading 标志。
src/client/view/components/TkComments.vue
调整单条评论的行为,将数据重新加载与滚动行为解耦,并响应新的 refreshed 事件。
  • 在模板中,将子回复组件绑定到一个新的 @refreshed 处理函数,同时保留 @load 指向现有的 onLoad
  • 简化 onLoad(),只清理 reply pid、发出 replyload 事件并展开评论,移除过早的 scrollIntoView 逻辑。
  • 新增 onRefreshed(),向上发出 refreshed 事件,并在 nextTick 中,如果存在回复且 ref 可用,则将回复容器的最后一个元素滚动到可视区域,确保滚动发生在 DOM 更新之后。
src/client/view/components/TkComment.vue

Tips and commands

Interacting with Sourcery

  • 触发新评审: 在 pull request 上评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub issue: 通过回复某条评审评论让 Sourcery 从该评论创建一个 issue。你也可以回复该评审评论 @sourcery-ai issue 来从中创建 issue。
  • 生成 pull request 标题: 在 pull request 标题的任意位置写上 @sourcery-ai,即可在任何时候生成标题。你也可以在 pull request 上评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 总结: 在 pull request 描述正文的任意位置写上 @sourcery-ai summary,即可在你想要的位置生成 PR 总结。你也可以在 pull request 上评论 @sourcery-ai summary 来在任何时候(重新)生成总结。
  • 生成评审者指南: 在 pull request 上评论 @sourcery-ai guide,即可在任何时候(重新)生成评审者指南。
  • 一次性解决所有 Sourcery 评论: 在 pull request 上评论 @sourcery-ai resolve,即可标记所有 Sourcery 评论为已解决。如果你已经处理完所有评论且不想再看到它们,这个非常有用。
  • 一次性驳回所有 Sourcery 评审: 在 pull request 上评论 @sourcery-ai dismiss 来驳回所有现有的 Sourcery 评审。特别适合你想重新开始一次全新的评审——别忘了再评论 @sourcery-ai review 来触发新的评审!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或禁用 Sourcery 生成的 pull request 总结、评审者指南等评审功能。
  • 更改评审语言。
  • 添加、移除或编辑自定义评审指令。
  • 调整其他评审设置。

Getting Help

Original review guide in English

Reviewer's Guide

Preserve the currently expanded comment pages when refreshing the comment list after replying, by tracking how many pages are loaded and re-fetching them, and by deferring scroll-to-latest-reply behavior until after the refreshed DOM is rendered.

Sequence diagram for preserving pagination and scrolling after replying to a comment

sequenceDiagram
  actor User
  participant TkComment
  participant TkComments

  User->>TkComment: onReplyReply
  TkComment->>TkComment: onLoad
  TkComment->>TkComments: emit load
  TkComments->>TkComments: refreshPreservingState
  TkComments->>TkComments: getComments(url, sort)
  loop for each extra loaded page
    TkComments->>TkComments: getComments(url, before, sort)
  end
  TkComments->>TkComment: emit refreshed
  TkComment->>TkComment: onRefreshed
  TkComment->>TkComment: $nextTick
  TkComment->>TkComment: scrollIntoView(lastElementChild)
Loading

File-Level Changes

Change Details Files
Preserve pagination state in the main comments list when a reply triggers a reload.
  • Track the number of loaded comment pages in component state via a new loadedPages data field.
  • Reset loadedPages to 1 whenever the full comment list is refreshed or the sort order changes.
  • Increment loadedPages each time the user expands to load more comments.
  • Introduce refreshPreservingState() to reload the first page and then sequentially re-fetch additional pages up to loadedPages using the before cursor derived from non-top comments.
  • Switch the @load handler from initComments to refreshPreservingState so replies refresh while keeping pagination state, while other top-level refresh flows still call initComments().
  • Emit a refreshed event after the state-preserving refresh completes and manage loading flags during the operation.
src/client/view/components/TkComments.vue
Adjust individual comment behavior to decouple data reload from scroll behavior and react to the new refreshed event.
  • In the template, wire the child replies component to a new @Refreshed handler while keeping @load pointing to the existing onLoad.
  • Simplify onLoad() to only clear the reply pid, emit reply and load events, and expand the comment, removing premature scrollIntoView logic.
  • Add onRefreshed(), which emits a refreshed event upward and, on nextTick, scrolls the replies container's last element into view if replies exist and the ref is available, ensuring scrolling occurs after DOM update.
src/client/view/components/TkComment.vue

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些整体反馈:

  • refreshPreservingState 中,分页逻辑(推导 before 并调用 getComments)与 onExpand 中的逻辑重复;可以考虑抽取一个用于加载下一页的共享辅助函数,这样两条代码路径可以保持一致,也更易于维护。
  • refreshPreservingState 中计算 before 时,会反复对所有非置顶评论进行排序(.sort((a, b) => a - b)[0]);可以通过对 created 值使用 Math.min,或者在加载分页时跟踪最早的 created 值,使其更高效且更易读。
  • refreshPreservingState 循环中,如果没有非置顶评论,before 将会是 undefined;在这种情况下,最好提前短路或跳过请求,而不是依赖后续逻辑来处理一个 undefinedbefore
给 AI 代理的提示
Please address the comments from this code review:

## Overall Comments
-`refreshPreservingState` 中,分页逻辑(推导 `before` 并调用 `getComments`)与 `onExpand` 中的逻辑重复;可以考虑抽取一个用于加载下一页的共享辅助函数,这样两条代码路径可以保持一致,也更易于维护。
-`refreshPreservingState` 中计算 `before` 时,会反复对所有非置顶评论进行排序(`.sort((a, b) => a - b)[0]`);可以通过对 `created` 值使用 `Math.min`,或者在加载分页时跟踪最早的 `created` 值,使其更高效且更易读。
-`refreshPreservingState` 循环中,如果没有非置顶评论,`before` 将会是 `undefined`;在这种情况下,最好提前短路或跳过请求,而不是依赖后续逻辑来处理一个 `undefined``before`## Individual Comments

### Comment 1
<location path="src/client/view/components/TkComments.vue" line_range="104-113" />
<code_context>
+      this.loadedPages = 1
       this.initComments()
     },
+    async refreshPreservingState () {
+      this.loading = true
+      const url = getUrl(this.$twikoo.path)
+      await this.getComments({ url, sort: this.currentSort })
+      for (let i = 1; i < this.loadedPages; i++) {
+        const before = this.comments
+          .filter((item) => !item.top)
+          .map((item) => item.created)
+          .sort((a, b) => a - b)[0]
+        await this.getComments({ url, before, sort: this.currentSort })
+      }
+      this.loading = false
+      this.$emit('refreshed')
+    },
     setSort (sort) {
</code_context>
<issue_to_address>
**issue (bug_risk):** 考虑使用 try/finally 来保护 `loading`,以确保在出现错误时也能重置。

如果 `getComments` 抛出异常或被拒绝,`this.loading` 将不会被重置为 `false`,从而导致 UI 卡在加载状态。可以将方法主体包裹在 `try { ... } finally { this.loading = false }` 中(可选地添加 `catch` 进行错误处理),以确保该标志始终会被重置。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的代码审查有帮助,请考虑分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据这些反馈改进以后的代码审查。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In refreshPreservingState, the pagination logic (deriving before and calling getComments) duplicates what onExpand is doing; consider extracting a shared helper for loading the next page so the two code paths stay consistent and easier to maintain.
  • The computation of before in refreshPreservingState repeatedly sorts all non-top comments (.sort((a, b) => a - b)[0]); this could be made more efficient and readable by using Math.min over the created values or by tracking the oldest created value as you load pages.
  • In the refreshPreservingState loop, if there are no non-top comments, before will be undefined; it would be safer to short-circuit or skip the request in that case to avoid relying on downstream handling of an undefined before.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `refreshPreservingState`, the pagination logic (deriving `before` and calling `getComments`) duplicates what `onExpand` is doing; consider extracting a shared helper for loading the next page so the two code paths stay consistent and easier to maintain.
- The computation of `before` in `refreshPreservingState` repeatedly sorts all non-top comments (`.sort((a, b) => a - b)[0]`); this could be made more efficient and readable by using `Math.min` over the `created` values or by tracking the oldest `created` value as you load pages.
- In the `refreshPreservingState` loop, if there are no non-top comments, `before` will be `undefined`; it would be safer to short-circuit or skip the request in that case to avoid relying on downstream handling of an undefined `before`.

## Individual Comments

### Comment 1
<location path="src/client/view/components/TkComments.vue" line_range="104-113" />
<code_context>
+      this.loadedPages = 1
       this.initComments()
     },
+    async refreshPreservingState () {
+      this.loading = true
+      const url = getUrl(this.$twikoo.path)
+      await this.getComments({ url, sort: this.currentSort })
+      for (let i = 1; i < this.loadedPages; i++) {
+        const before = this.comments
+          .filter((item) => !item.top)
+          .map((item) => item.created)
+          .sort((a, b) => a - b)[0]
+        await this.getComments({ url, before, sort: this.currentSort })
+      }
+      this.loading = false
+      this.$emit('refreshed')
+    },
     setSort (sort) {
</code_context>
<issue_to_address>
**issue (bug_risk):** Consider guarding `loading` with try/finally so it is always reset on errors.

If `getComments` throws or rejects, `this.loading` never returns to `false`, leaving the UI stuck in a loading state. Wrapping the method body in `try { ... } finally { this.loading = false }` (with an optional `catch` for error handling) ensures the flag is always reset.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/client/view/components/TkComments.vue Outdated
@imaegoo imaegoo merged commit 7ef83fe into twikoojs:main Jun 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants