Substituir avatar e nome de usuário em um post específico

O problema: eu quero que os usuários possam postar como personagens de RPG. Gostaria que eles pudessem inserir um modelo em uma postagem — algo como o exemplo abaixo:

[wrap="characterpost"]
[characterav]https://image.link.example.png[/characterav]
[charactername][[Character Name]][/charactername]
[/wrap]

E depois usar um script para que a imagem substitua o avatar original e o link do tópico do charactername apareça antes do nome de usuário. Por exemplo, se a postagem normalmente diz “Username”, eu quero substituir isso por “Character Name played by Username”.
(“Character Name” incluiria um link para um tópico com a ficha do personagem, usando, com sorte, apenas o componente de tema wikilinks para facilitar o uso.)

Eu colei uma estrutura de postagem em um Codepen e consegui escrever um JavaScript que faria exatamente isso. No entanto, quando se tratou de adicioná-lo a um decorador de postagem e fazê-lo funcionar ao vivo com a API, esbarrei em um muro.

Aqui está o que eu tenho em common>header agora:

<script type="text/discourse-plugin" version="0.8">
api.decorateCookedElement(
  element => {
    
    // find the characterpost tag within the post
    const characterPost = element.querySelector('[data-wrap="characterpost"]');
	
    // find the parent of the characterpost tag, which contains avatar and username
    const characterPostParent = characterPost.closest('article');
	
    // turn it red to see if it's working
    characterPostParent.style.backgroundColor = "red";
    
  },
  {
    id: 'render-character-post', onlyStream: true, afterAdopt: true
  }
);
</script>

Isso tem estado gerando um erro. É possível acessar o wrapper “article” para postagens usando decorateCookedElement para que eu possa acessar o nome de usuário e o avatar? Se não, como posso proceder?

Olá, unfairest,

Encontrei esta postagem e pensei em dar uma chance!

Aqui está a primeira versão; espero que o código não seja muito ruim. :slightly_smiling_face:
Não testei extensivamente todos os casos extremos. É um ponto de partida.

  • Formato de wrap esperado: [wrap=character avatar=\"<URL>\"]<Nome>[/wrap]
  • Certifique-se de que há uma linha vazia abaixo de [wrap]
  • Você pode personalizar o nome do personagem com CSS (veja as classes character e character-extra)

Me avise se tiver algum problema :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;
    }
}

Aqui está uma pequena demonstração de como ficou:

Isso é muito legal, obrigado por compartilhar! Eu estava procurando algo semelhante também. Você sabe se isso afeta a forma como as postagens aparecem nos resultados de pesquisa também?

Boa pergunta! O nome da tag e seu conteúdo fazem parte do conteúdo da postagem; isso pode afetar o resultado da pesquisa.
Tentarei algo mais limpo, como usar um modal e salvar em campos personalizados em vez disso.

EDIT: Acho que entendi errado :smile: se você estiver falando sobre alterações que aparecem no resultado da pesquisa, a resposta é não (nem o HTML aparecerá).

Isso é muito legal. Seu vídeo rola rápido. O URL está no site do fórum?

Ei, Dan. O que você quer dizer?

A URL do avatar é apenas uma imagem carregada ou aponta para um site externo?

Este é um URL externo. Deve ser possível trabalhar com um URL local.

Legal, obrigado

Pensar que pode ser uma ideia propor, talvez na wiki da comunidade, um guia de template de fórum. Frequentemente, as pessoas procuram ideias sobre diferentes tipos de configurações de fórum.
Neste caso, pode ser um pouco específico. Mas um template de RPG de mesa que inclua este recurso que você criou.