Olá a todos,
Estou desenvolvendo um plugin de loteria para o Discourse, onde os usuários podem participar respondendo a um tópico de loteria. Divulgação completa: não sou programador - estou construindo isso inteiramente com assistência de IA (Claude Code), então posso estar perdendo conceitos fundamentais. A funcionalidade básica funciona, mas estou tendo problemas com atualizações em tempo real usando o MessageBus. Agradeceria qualquer orientação sobre a abordagem correta.
O que estou tentando alcançar
- Criar uma postagem de loteria usando BBCode
[lottery]...[/lottery] - Exibir um cartão de loteria na postagem mostrando a contagem de participantes
- Quando os usuários responderem para participar, todos os espectadores devem ver a contagem de participantes atualizar em tempo real sem a necessidade de atualizar a página
- Semelhante a como o plugin discourse-calendar atualiza as informações do evento
Implementação atual
Backend (Ruby)
Modelo Lottery (app/models/lottery.rb):
def publish_update!
channel = "/lottery/#{self.post.topic_id}"
message = { id: self.id }
MessageBus.publish(channel, message)
end
Modelo LotteryParticipant (app/models/lottery_participant.rb):
after_commit :publish_lottery_update, on: [:create, :destroy]
private
def publish_lottery_update
self.lottery.publish_update!
end
Os logs do backend confirmam que o MessageBus.publish é chamado com sucesso:
[Lottery] 📡 Publishing MessageBus update:
Channel: /lottery/27
Message: {:id=>52}
[Lottery] ✅ MessageBus.publish completed
Frontend (JavaScript)
Decorator (discourse-lottery-decorator.gjs):
api.decorateCookedElement((cooked, helper) => {
const post = helper.getModel();
if (!post?.lottery_data) return;
// Verifica se já foi decorado (IA sugeriu usar atributo de dados)
if (cooked.dataset.lotteryDecorated === "true") return;
const lotteryNode = cooked.querySelector(".discourse-lottery");
if (!lotteryNode) return;
const wrapper = document.createElement("div");
lotteryNode.before(wrapper);
const lottery = Lottery.create(post.lottery_data);
helper.renderGlimmer(
wrapper,
`<template><DiscourseLottery @lotteryData={{lottery}} /></template>`
);
lotteryNode.remove();
cooked.dataset.lotteryDecorated = "true";
}, { id: "discourse-lottery" });
Componente (discourse-lottery/index.gjs):
export default class DiscourseLottery extends Component {
@service messageBus;
@service lotteryApi;
constructor() {
super(...arguments);
const { lotteryData } = this.args;
if (lotteryData?.topicId) {
this.lotteryPath = `/lottery/${lotteryData.topicId}`;
this.lotteryData = lotteryData;
// IA sugeriu vincular manualmente o callback
this._boundOnLotteryUpdate = this._onLotteryUpdate.bind(this);
this.messageBus.subscribe(this.lotteryPath, this._boundOnLotteryUpdate);
registerDestructor(this, () => {
this.messageBus.unsubscribe(this.lotteryPath, this._boundOnLotteryUpdate);
});
}
}
async _onLotteryUpdate(msg) {
console.log('[Lottery Component] 🔔 MessageBus message received!');
const updatedData = await this.lotteryApi.lottery(this.lotteryData.topicId);
this.lotteryData.updateFromLottery(updatedData);
}
}
Modelo (models/lottery.js):
export default class Lottery {
@tracked _stats;
set stats(stats) {
this._stats = LotteryStats.create(stats || {});
}
updateFromLottery(lottery) {
// Atualiza todas as propriedades, incluindo estatísticas
this.stats = lottery.stats || {};
// ... outras propriedades
}
}
O problema
Apenas a página do participante é atualizada em tempo real, mas as páginas de outros espectadores não são atualizadas sem recarregar.
O que funciona:
Quando um usuário responde para participar, a própria página dele mostra a contagem de participantes atualizando em tempo real
O backend publica mensagens do MessageBus com sucesso
A mensagem do MessageBus é recebida na página do participante (os logs mostram “
MessageBus message received!”)
O modelo é atualizado com a nova contagem de participantes (os logs mostram a contagem aumentando de 0 para 1)
O que não funciona:
Outros espectadores que estão visualizando o tópico da loteria não veem a contagem atualizar em tempo real
A página do criador da loteria não atualiza
A página de qualquer outro usuário que tenha o tópico da loteria aberto não atualiza- Todos eles precisam atualizar a página manualmente para ver a contagem de participantes atualizada
Além disso, vejo o componente sendo destruído e recriado várias vezes no console:
[Lottery Component] ✅ MessageBus subscription established
[Lottery Component] 🧹 Cleaning up MessageBus for topic: 27
[Lottery Component] ✅ MessageBus subscription established
[Lottery Component] 🧹 Cleaning up MessageBus for topic: 27
Isso sugere que decorateCookedElement está sendo acionado várias vezes, fazendo com que os componentes sejam destruídos e recriados, o que pode estar quebrando a assinatura do MessageBus para outros espectadores.
Perguntas
-
Usar o atributo
data-decoratedno elemento cozido é a maneira correta de evitar decoração duplicada? A IA sugeriu essa abordagem, mas não consegui encontrar documentação oficial confirmando que é o padrão recomendado. -
Por que a interface do usuário não está atualizando, mesmo que a propriedade do modelo rastreada mude? O
@tracked _statsdeve acionar a renderização quando atualizado viathis.stats = lottery.stats, mas a contagem de participantes exibida permanece a mesma. Há algo errado na forma como estou usando@tracked? -
Devo usar uma abordagem completamente diferente? Pedi à IA para seguir o padrão do discourse-calendar, mas talvez eu esteja perdendo algo fundamental sobre como o Discourse lida com atualizações em tempo real.
-
Remover o
lotteryNodeoriginal está causando o problema? A IA inicialmente tentou não removê-lo, mas isso fez com que o cartão da loteria ficasse oculto pelo CSS (.cooked > .discourse-lottery { display: none; }). Existe um padrão melhor?
Como não sou programador, posso estar fazendo perguntas básicas - qualquer indicação de documentação ou exemplos seria incrivelmente útil! Obrigado!