Commit f2baef7e authored by Kourser's avatar Kourser
Browse files

Correctif : un joueur ne doit pas être notifié de son propre message

Deux auto-notifications, toutes deux introduites avec les notifications sonores.

La plus visible : le gestionnaire `chat:new` de Player.tsx sonnait SANS AUCUNE
condition. Poster un message se signalait donc à soi-même, à chaque fois.

La seconde, dans Chat.tsx : un message destiné à un canal non sélectionné comptait
comme non lu et sonnait — y compris quand c'était l'auteur qui venait de l'écrire
dans un autre canal que celui qu'il regardait.

Le serveur diffuse à toute la salle : c'est donc au client de se reconnaître. Le
chat le pouvait déjà (`authorParticipantId`), pas le mail. `MailNewEvent` transporte
désormais `fromParticipantId`, renseigné à la composition joueur — ce qui corrige au
passage un second `fromParticipantId: null` codé en dur dans l'entrée synthétique de
la boîte de réception, exactement le défaut déjà corrigé pour `fromCharacterId`.

`Chat` reçoit l'identité du joueur par une prop optionnelle, comme `Directory`.
Absente côté console, où les messages d'animation n'ont pas d'auteur participant :
la garde court-circuite alors sans effet.

L'ordre compte dans Chat : la garde est placée APRÈS le rafraîchissement de la liste
des canaux. Créer un fil et y écrire le premier message doit toujours faire
apparaître l'onglet, même si ça ne doit ni compter ni sonner.

Non compilé ni exécuté (pas de toolchain Node dans l'environnement utilisé).

Co-Authored-By: Claude (RCA)
parent 51235ba1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -263,6 +263,7 @@ export class MailboxService {
          messageId: created.id,
          from: authorLabel,
          fromCharacterId: me.characterId,
          fromParticipantId: me.id,
          subject: dto.subject,
          body: dto.body,
          origin: 'PARTICIPANT',
+8 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ export function Chat({
  onCreateChannel,
  onCreateDm,
  loadContacts,
  selfParticipantId,
  joinExerciseId,
  initialChannelId,
}: {
@@ -28,6 +29,8 @@ export function Chat({
  onCreateChannel?: (name: string) => Promise<unknown>;
  onCreateDm?: (participantId: string) => Promise<{ id: string }>;
  loadContacts?: () => Promise<MailContact[]>;
  /** Identité du joueur : ses propres messages ne le notifient pas. */
  selfParticipantId?: string;
  joinExerciseId?: string;
  initialChannelId?: string;
}): JSX.Element {
@@ -47,6 +50,8 @@ export function Chat({
  const knownRef = useRef<Set<string>>(new Set());
  knownRef.current = new Set(channels.map((c) => c.id));
  const bottomRef = useRef<HTMLDivElement>(null);
  const selfRef = useRef<string | undefined>(selfParticipantId);
  selfRef.current = selfParticipantId;

  const refreshChannels = useCallback(async () => {
    const chans = await loadChannels();
@@ -71,6 +76,9 @@ export function Chat({
          /* la prochaine arrivée retentera */
        });
      }
      // Écrire dans un canal qu'on ne regarde pas ne doit ni se compter comme
      // non lu, ni se signaler : c'est notre propre message.
      if (selfRef.current && m.authorParticipantId === selfRef.current) return;
      setUnread((prev) => ({ ...prev, [m.channelId]: (prev[m.channelId] ?? 0) + 1 }));
      ping();
    });
+11 −3
Original line number Diff line number Diff line
@@ -103,6 +103,11 @@ export function Player({

        const socket = connectSocket();
        socketRef.current = socket;
        /*
         * Un joueur ne doit jamais etre notifie de son propre message : le serveur
         * diffuse a toute la salle, c'est donc au client de se reconnaitre.
         */
        const meId = session.participant.id;
        socket.on(RT.mailNew, (m: MailNewEvent) => {
          const rid = m.recipientId;
          if (!rid) return; // message pour l'animation : ignore cote joueur
@@ -114,7 +119,7 @@ export function Player({
                    id: rid,
                    messageId: m.messageId ?? rid,
                    from: m.from,
                    fromParticipantId: null,
                    fromParticipantId: m.fromParticipantId ?? null,
                    // Reprend l'expediteur diffuse : le mettre a null faisait
                    // repondre a « Animation » tout message recu en direct,
                    // y compris entre deux joueurs.
@@ -132,9 +137,11 @@ export function Player({
                  ...prev,
                ],
          );
          ping();
          if (m.fromParticipantId !== meId) ping();
        });
        socket.on(RT.chatNew, () => {
        socket.on(RT.chatNew, (m: { authorParticipantId: string | null }) => {
          // Ses propres messages ne comptent ni pour la pastille ni pour le signal.
          if (m.authorParticipantId === meId) return;
          // Compte pour la pastille de navigation ; le detail des canaux est gere
          // dans Chat, qui a son propre compteur par canal.
          if (viewRef.current !== 'chat') setChatUnread((n) => n + 1);
@@ -508,6 +515,7 @@ export function Player({
            post={writeBlocked ? undefined : api.playPostMessage}
            onCreateDm={writeBlocked ? undefined : api.playCreateDm}
            loadContacts={api.playContacts}
            selfParticipantId={session.participant.id}
            initialChannelId={chatInitial}
          />
        </div>
+2 −0
Original line number Diff line number Diff line
@@ -317,6 +317,8 @@ export interface MailNewEvent {
   * direct, y compris entre deux joueurs.
   */
  fromCharacterId?: string | null;
  /** Joueur auteur, s'il y en a un. Permet de ne pas se notifier soi-même. */
  fromParticipantId?: string | null;
  subject: string;
  body?: string;
  origin?: 'PARTICIPANT' | 'INJECT';