当一个频道或私信中有未读消息,且其数量超过第一页加载的内容时,如果在您滚动浏览该历史记录时有人发布了新消息,那么已加载页面末尾与新实时消息之间的所有消息都会被跳过。列表会直接从较早的消息跳到新的实时消息(通常相隔数小时),并且没有任何迹象表明有内容缺失。重新加载频道会一次一页地填补这些空缺,因此较大的空缺需要多次重新加载才能补全。
此问题与浏览器或设备类型无关(已在 Firefox 桌面版和安卓版以及 Chrome 安卓版 PWA 上测试)。
复现步骤:
用户 A 和用户 B 共用一个私信或频道。用户 A 阅读到最新消息后离开。
用户 B 发布了 60 条以上的消息。
用户 A 打开私信。页面会在其最后阅读的位置附近加载(下方有约 25 条较新的消息)。
在滚动到该已加载页面的底部之前,用户 B 又发布了一条消息。
用户 A 向下滚动。
预期:在下方加载接下来的 25 条消息页面,以此类推,直到实时消息。
实际:实时消息直接出现在已加载页面之后。滚动到底部时会请求实时消息之后的消息,响应为空,can_load_more_future 变为 false,而那 35+ 条中间的消息从未显示。由于实时消息现在可见,last_read_message_id 会推进到空缺之后,因此未读指示器也会清除。
以下是我对为何发生此问题的最佳分析:
自 2025 年 12 月左右的一次提交(“FEATURE: respect user presence when chat is on screen”, #36652 )以来,ChatChannel#onNewMessage 始终通过 ChatPaneState#handleIncomingMessage 路由消息,后者在自动滚动时调用 addMessage,否则通过 maintainScrollPosition 处理。
ChatMessagesLoader#computeNextTargetMessage 使用列表中最后一条非暂存(non-staged)消息作为 FUTURE 页面的光标。一旦实时消息位于部分加载列表的末尾,该光标就会跳过所有未加载的历史记录。
此外,Chat::MessagesQuery.query_paginated_messages 仅为 FUTURE 页面设置 can_load_more_future,仅为 PAST 页面设置 can_load_more_past,另一项则为 nil。ChatMessagesLoader#loadMore 无条件地从 result.meta 分配这两项,因此单个 PAST 页面(在打开位于最后阅读位置后稍向上滚动时)会在下方历史记录仍未加载的情况下将 canLoadMoreFuture 设置为 null。由于 loadMore 检查的是 === false,进一步的分页仍然有效。
除此之外,还有一个独立的、更旧的 bug:当 canLoadMoreFuture 为 true 时,#sendNewMessage 会将消息暂存在列表末尾,而不加载最新页面。一旦消息被确认,它就成为 FUTURE 光标,因此如果您在正在阅读的历史记录中回复较旧的消息,也会发生同样的跳过情况。
我们的社区主要并非以聊天为核心,但我们的用户经常回溯阅读,因此在您未在场的持续对话中遗漏消息是一个问题。我认为这对于以聊天为主的社区来说会是更大的问题,尤其是考虑到现在可以将聊天设置为主页这一新功能。
3 个赞
感谢反馈!我能通过你提供的步骤复现该问题,这肯定不是理想状态。
我已在此处添加了修复方案:
main ← fix/chat-load-more-flag-clobbering
Reported in: https://meta.discourse.org/t/chat-skips-a-block-of-messages-when-so… meone-posts-while-youre-catching-up-on-unread-history/416446
When you're scrolled up in the chat history and newer messages haven't been loaded yet, a new message that comes in is added to the end of the partially loaded list. The next page is then requested from that message.
Because of this, everything between the most recent message you had loaded and the new message is skipped and marked as read, and doesn't show up until you reload the channel. Loading older messages also reset `canLoadMoreFuture` to `null`, which broke the "new messages below" arrow.
This change makes `ChatMessagesLoader` request the next page from the oldest and newest messages the server has sent, rather than from the ends of what's on screen, so live messages can no longer change where pagination starts. It also only updates the flag for the direction it fetched, so the arrow keeps working.
While newer history is still unloaded, live messages are now counted on the arrow instead of being added to the list, so they appear in order as you scroll instead of pages loading in above them. If one arrives just after the server built the last page, the loader fetches one more page so it isn't missed.
Before... note the new message and our count ending at 55 of 96
https://github.com/user-attachments/assets/b6424498-c158-4410-81d2-717c440d5bb7
After... we see up to 96 and the new message
https://github.com/user-attachments/assets/572153dd-52e2-49f3-b3da-bae740c8757a
此修复将很快随 Discourse 更新推出。
4 个赞