Remplacer l’avatar et le nom d’utilisateur pour un message spécifique

Le problème : je souhaite que les utilisateurs puissent publier en tant que personnages de jeu de rôle. J’aimerais qu’ils puissent insérer un modèle dans un message — quelque chose comme ce qui suit :

[wrap="characterpost"]
[characterav]https://image.link.example.png[/characterav]
[charactername][[Nom du personnage]][/charactername]
[/wrap]

Et ensuite utiliser un script pour que l’image remplace l’avatar d’origine et que le lien vers le sujet du nom du personnage soit placé devant le nom d’utilisateur. Par exemple, si le message affiche normalement « Nom d’utilisateur », je souhaite le remplacer par « Nom du personnage joué par Nom d’utilisateur ».
(Le « Nom du personnage » inclurait un lien vers un sujet contenant la fiche du personnage, en espérant utiliser simplement le composant de thème des liens wiki pour la facilité d’utilisation.)

J’ai collé un squelette de message dans un Codepen et j’ai réussi à écrire un peu de JavaScript qui ferait exactement cela. Cependant, quand il a fallu l’ajouter dans un décorateur de message et le faire fonctionner en direct avec l’API, je suis arrivé à une impasse.

Voici ce que j’ai actuellement dans common>header :

<script type="text/discourse-plugin" version="0.8">
api.decorateCookedElement(
  element => {
    
    // trouver la balise characterpost dans le message
    const characterPost = element.querySelector('[data-wrap="characterpost"]');
	
    // trouver le parent de la balise characterpost, qui contient l'avatar et le nom d'utilisateur
    const characterPostParent = characterPost.closest('article');
	
    // le rendre rouge pour voir si ça fonctionne
    characterPostParent.style.backgroundColor = "red";
    
  },
  {
    id: 'render-character-post', onlyStream: true, afterAdopt: true
  }
);
</script>

Cela provoque une erreur. Est-il possible d’accéder à l’élément enveloppe « article » des messages en utilisant decorateCookedElement afin que je puisse accéder au nom d’utilisateur et à l’avatar ? Si ce n’est pas le cas, comment puis-je m’y prendre ?

Salut, unfairest,

Je suis tombé sur ce post et j’ai pensé que je pourrais essayer !

Voici la première version ; j’espère que le code n’est pas trop horrible. :slightly_smiling_face:
Je n’ai pas testé de manière exhaustive tous les cas limites. C’est un point de départ.

  • Format d’enroulement attendu : [wrap=character avatar=\"<URL>\"]<Nom>[/wrap]
  • Assurez-vous qu’il y a une ligne vide sous [wrap]
  • Vous pouvez personnaliser le nom du personnage avec CSS (voir les classes character et character-extra)

Faites-moi savoir si vous rencontrez des problèmes :slight_smile:

Head
<script type="text/discourse-plugin" version="0.8.13">

let characters = new Map();

function searchTag(obj, tag) {
  if (!obj || !obj.firstObject) return;
  
  const firstObj = obj.firstObject;
  return firstObj.tagName === tag ? firstObj : searchTag(firstObj.children, tag);
}
            
api.addPostTransformCallback(post => 
{
    if (post.post_number <= 1 || post.post_type !== 1) {
        return;
    }

    const matches = post.cooked.match(/data-wrap="character"\s+data-avatar="(?<avatar>[^"]+)"\s+data-name="(?<name>[^"]+)"/i);
    
    if (!matches) {
        characters.delete(post.id);
        return;
    }
    
    // TODO: sanity check for avatar/name
    
    const {name, avatar} = matches.groups;
    
    characters.set(post.id, {name, avatar});
    
});

api.reopenWidget("post-avatar", {
    html(attrs) {
        const html = this._super(attrs);
        
        if (attrs.id && characters.has(attrs.id)) {
            const imageHtml = searchTag(html, 'IMG');
    
            if (imageHtml) {
                imageHtml.properties.attributes.src = characters.get(attrs.id).avatar;
            }
        }
        
        return html;
    }
});

api.reopenWidget("poster-name", {
    html(attrs) {
        let html = this._super(attrs);
        
        if (attrs.id && characters.has(attrs.id)) {
            const h = require("virtual-dom").h;
            
            html = [
                h('span.character', this.userLink(attrs, characters.get(attrs.id).name)),
                h('span.character-extra', 'played by'), // optional
                ...html
            ];
        }
        
        return html;
    }
});

</script>
CSS
.names {
    .character a {
        font-weight: bold;
        color: var(--tertiary-high);
    }
    
    .character-extra {
        
    }
}

.cooked, .d-editor-preview {
    p:has(> [data-wrap="character"]) {
        display: none;
    }
    
    p:has(> [data-wrap="character"]) + * {
        margin-top: 0;
    }
}

Voici une petite démo de ce à quoi cela ressemblait :

C’est très cool, merci de partager ! Je cherchais aussi quelque chose de similaire. Savez-vous si cela affecte également la façon dont les publications apparaissent dans les résultats de recherche ?

Bonne question ! Le nom de la balise et son contenu font partie du contenu du message ; cela pourrait affecter le résultat de la recherche.
Je vais essayer quelque chose de plus propre, comme utiliser une modale et enregistrer dans des champs personnalisés à la place.

EDIT : Je pense que j’ai mal compris :smile : si vous parlez des changements apparaissant dans le résultat de la recherche, la réponse est non (ni le HTML n’apparaîtra).

C’est plutôt cool. Votre vidéo défile rapidement. L’URL se trouve-t-elle sur le site du forum ?

Salut Dan. Qu’est-ce que tu veux dire ?

L’URL de l’avatar. S’agit-il simplement d’une image téléchargée ou d’un lien vers un site externe ?

Ceci est une URL externe. Il devrait être possible de travailler avec une URL locale.

Cool merci

Il pourrait être judicieux de proposer, peut-être dans le wiki communautaire, un guide de modèles de forum. Souvent, les gens viennent chercher des idées sur différents types de configurations de forum. Dans ce cas, cela pourrait être un peu spécifique. Mais un modèle de jeu de rôle sur table qui inclut cette fonctionnalité que vous avez créée.