Is this the correct way to create a theme component?

I’ve experience building custom solutions with other platforms/frameworks, and want to understand if this is the correct way to create a theme component in Discourse.

It seems to be working, but doesn’t necessarily mean it’s the correct way.

In short, this should hide reaction depending on the category a topic is within. Is this the right way to do it?

<script type="text/discourse-plugin" version="0.1">
$(document).ready(function() {
    try {
      const isTopicPage = /^\/t\//.test(window.location.pathname);
      
      if (!isTopicPage) return;

      const allowedCategories = ['ask-a-question'];

      const topic = Discourse.__container__.lookup("controller:topic");
      const categorySlug = topic && topic.get("model.category.slug");
      const isAllowedCategory = categorySlug && allowedCategories.includes(categorySlug);

      const toggleReactionEmoji = () => {
        const emoji = document.querySelector("[data-reaction='frog']");
        
        if (emoji) {
          emoji.style.display = isAllowedCategory ? '' : 'none';
          console.log(`Emoji with data-reaction='frog' ${isAllowedCategory ? 'shown' : 'hidden'}.`);
        }
      };

      toggleReactionEmoji();

      const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
          mutation.addedNodes.forEach(node => {
            if (node.nodeType === 1) {
              const emoji = node.querySelector("[data-reaction='frog']");
              if (emoji) {
                emoji.style.display = isAllowedCategory ? '' : 'none';
                console.log(`Emoji with data-reaction='frog' found in mutation and ${isAllowedCategory ? 'shown' : 'hidden'}.`);
              }
            }
          });
        });
      });

      observer.observe(document.body, { childList: true, subtree: true });

      api.cleanupStream(() => observer.disconnect());

    } catch (error) {
      console.error("An error occurred in the emoji toggle script:", error);
    }
  });
</script>
1 Like

Although this way of doing things is technically possible, it’s not the ideal approach.

Instead of using script tags and jQuery $(document).ready it would be better to work with Ember’s rendering system correctly.

To start, it would be good to create a theme component repository with a proper folder structure for your theme-component. Have a look at the discourse_theme CLI as this will scaffold out that structure for you, and make it easy for you to develop the component. (Alternatively, there’s also the theme skeleton if you only need the structure and not any of the other goodies of the theme CLI).

From here, I would use tools Discourse has in place for extensibility such as apiInitializers, the pluginAPI, plugin outlets, etc. to achieve what you’re trying to do.

The best way to learn about these things is to peruse the Developer Guides here on Meta (specifically the theme/theme-component) sections. Additionally, I would go through the Theme component category and find their GitHub repositories. Looking through their code and how they achieve things will help you as well.

I hope this helps!

7 Likes

If this is your goal, you can do it with simple CSS:

body.category-ask-a-question .discourse-reactions-picker.frog {
  display: none;
}
5 Likes