O chat pula um bloco de mensagens quando alguém posta enquanto você está lendo o histórico não lido

Quando um canal ou DM possui mais mensagens não lidas do que as carregadas na primeira página, e alguém publica uma mensagem enquanto você está rolando esse histórico, todas as mensagens entre o final da página carregada e a nova mensagem ao vivo são ignoradas. A lista pula das mensagens mais antigas diretamente para a nova mensagem ao vivo (frequentemente horas depois), e nada indica que algo está faltando. Recarregar o canal preenche a lacuna de uma página por vez, então uma lacuna grande requer várias recargas.

Isso ocorre independentemente do navegador ou do tipo de dispositivo (testado no Firefox Desktop e Android e no PWA do Chrome para Android).

Etapas para reproduzir:

  1. O usuário A e o usuário B compartilham uma DM ou canal. O usuário A lê até a última mensagem, depois sai.
  2. O usuário B publica mais de 60 mensagens.
  3. O usuário A abre a DM. Ela carrega cerca da última mensagem lida por ele (25 mensagens mais novas abaixo).
  4. Antes de rolar até o final dessa página carregada, o usuário B publica mais uma mensagem.
  5. O usuário A rola para baixo.

Esperado: a próxima página com 25 mensagens carrega abaixo, e assim por diante, até chegar à mensagem ao vivo.

Real: a mensagem ao vivo aparece diretamente após a página carregada. Ao atingir o final, são solicitadas mensagens após a mensagem ao vivo, a resposta é vazia, can_load_more_future torna-se false, e as mais de 35 mensagens entre as duas nunca são mostradas. Como a mensagem ao vivo agora está visível, last_read_message_id avança para além da lacuna, fazendo com que o indicador de não lidas também desapareça.

Aqui está minha melhor tentativa de entender por que isso está acontecendo:

Desde um commit por volta de dezembro de 2025 (“FEATURE: respect user presence when chat is on screen”, #36652), ChatChannel#onNewMessage sempre roteia a mensagem através de ChatPaneState#handleIncomingMessage, que chama addMessage quando há rolagem automática e via maintainScrollPosition caso contrário.

ChatMessagesLoader#computeNextTargetMessage usa a última mensagem não encenada (non-staged) na lista como cursor para uma página FUTURE. Quando uma mensagem ao vivo fica no final de uma lista parcialmente carregada, esse cursor pula toda a história não carregada.

Em seguida, Chat::MessagesQuery.query_paginated_messages só define can_load_more_future para páginas FUTURE e can_load_more_past para páginas PAST. O outro é nil. ChatMessagesLoader#loadMore atribui ambos a partir de result.meta incondicionalmente, então uma única página PAST (rolar um pouco para cima depois de abrir na última leitura) define canLoadMoreFuture como null, enquanto o histórico abaixo ainda está não carregado. A paginação adicional ainda funciona porque loadMore verifica === false.

Além disso, há um bug separado e mais antigo onde #sendNewMessage encena a mensagem no final da lista sem carregar a página mais recente quando canLoadMoreFuture é true. Uma vez confirmada, ela se torna o cursor FUTURE, então o mesmo salto acontece se você responder a mensagens mais antigas no histórico que está lendo.

Nossa comunidade não é focada principalmente em bate-papo, mas nossos usuários fazem leitura retrospectiva com frequência, então mensagens ausentes em uma conversa contínua na qual você não estava presente são um problema. Imagino que isso será um problema maior para comunidades focadas principalmente em bate-papo, especialmente considerando a nova capacidade de definir a página inicial como bate-papo.