チャンネルまたはDMに、最初のページで読み込まれるメッセージ数以上の未読メッセージがあり、履歴をスクロールしている最中に誰かが投稿すると、読み込まれたページの末尾から新しいライブメッセージまでの間のすべてのメッセージがスキップされます。リストは古いメッセージからいきなりライブメッセージ(多くの場合、数時間後)にジャンプし、何かが欠落していることを示す表示はありません。チャンネルを再読み込みすると、ギャップは1ページずつ埋まっていくため、大きなギャップがある場合は複数回の再読み込みが必要になります。
これはブラウザやデバイスの種類に関係なく発生します(Firefox Desktop、Android、Chrome Android PWAでテスト済み)。
再現手順:
- ユーザーAとユーザーBがDMまたはチャンネルを共有している。ユーザーAは最新のメッセージまで読んだ後、離脱する。
- ユーザーBが60件以上のメッセージを投稿する。
- ユーザーAがDMを開く。最後に読んだメッセージ付近が読み込まれ、その下に新しいメッセージが約25件ある。
- 読み込まれたページの末尾までスクロールする前に、ユーザーBがもう1件のメッセージを投稿する。
- ユーザー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は、リスト内の最後の非ステージ済みメッセージを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を確認するため、さらなるページングはまだ動作します。
加えて、これとは別に、より古いバグもあります。#sendNewMessageは、canLoadMoreFutureがtrueの場合、最新のページを読み込まずにリストの末尾にメッセージをステージします。これが確認されるとFUTUREカーソルになるため、閲覧中の履歴内の古いメッセージに返信する場合も同様のスキップが発生します。
私たちのコミュニティは主にチャット重視ではありませんが、ユーザーは後から過去を遡って読むことが頻繁にあります。そのため、自分がいなかった継続中の会話でメッセージが欠落するのは問題です。特にチャットをホームページとして設定する新機能の登場を踏まえると、主要なコミュニケーション手段がチャットであるコミュニティでは、これがさらに大きな問題になると考えられます。