Discourse is a single-page application. The issue you’re encountering happens because the script you’re using is not aware of that. When you visit the homepage - or any other page - in Discourse, you get something like this.
<html>
<head>
head content including your script
</head>
<body>
<section id="main">
page content
</section>
</body>
</html>
When you navigate to another page, the only thing that gets reloaded is the content inside
<section id="main">
So, the DOM has changed, and your custom script doesn’t fire again. If you try to visit the topic page directly, you’ll see that it loads fine.
.So, now the question is how to get it to work with Discourse.
The plugin-api has a method that you can use to “decorate” posts.
discourse/plugin-api.js at main · discourse/discourse · GitHub
You can use that to fire third-party scripts when a post is rendered.
Here’s the code you would need. Add this to the common > header
tab of your theme.
<script type="text/discourse-plugin" version="0.8">
const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";
const loadScript = require("discourse/lib/load-script").default;
const { iconHTML } = require("discourse-common/lib/icon-library");
const composerPreviewIcon = iconHTML(PREVIEW_ICON, {
class: "woxo-preview-icon"
});
const previewMarkup = () => {
const markup = `<div class="woxo-preview">${composerPreviewIcon}</div>`;
return markup;
};
// create a post decorator
api.decorateCookedElement(
post => {
const woxoWidgets = post.querySelectorAll("div[data-mc-src]");
if (woxoWidgets.length) {
woxoWidgets.forEach(woxoWidget => {
if (post.classList.contains("d-editor-preview")) {
woxoWidget.innerHTML = previewMarkup();
return;
}
loadScript(WOXO_SCRIPT_SRC).then(() => {
const script = document.head.querySelector(
`script[src*="cdn2.woxo.tech"]`
);
script.dataset.usrc = "";
window.MC.Loader.init();
});
});
}
},
{ id: "render-woxo-widgets" }
);
</script>
You would then need to add a couple of domains for CSP. Add these to your
content_security_policy_script_src
site setting
https://*.woxo.tech/
https://us-central1-core-period-259421.cloudfunctions.net/availableComponentTracks
finally, you’d need to add a little bit of CSS for the static composer preview
This goes in the common > CSS
tab of your theme.
.woxo-preview {
height: 400px;
width: 100%;
background: var(--primary-low);
display: flex;
align-items: center;
justify-content: center;
.woxo-preview-icon {
font-size: var(--font-up-4);
color: var(--primary-high);
}
}
Then you can just add
<div data-mc-src="f4b43a8f-c188-4f80-8206-36d9f7529f13#instagram"></div>
to any post, and the widgets will render and be fully functional.
If you look at the JavaScript, you’ll notice that it has two options at the very top.
const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";
Change WOXO_SCRIPT_SRC
to the src that woxo gives you. It should be the same for all the embeds you create.
Change PREVIEW_ICON
to the name of the icon you want to use in the composer preview. Running this code in the composer is a bit expensive, so the composer has a static preview that looks like this.
The icon you pick will show in the middle.
Here’s a commented copy of the code if you want to follow along with what’s happening
commented code
<script type="text/discourse-plugin" version="0.8">
// options
const WOXO_SCRIPT_SRC = "https://cdn2.woxo.tech/a.js#616348fb53c1e8001686c619";
const PREVIEW_ICON = "heart";
// we use the Discourse Load script lib to ensure scripts are loaded
// properly. Don't worry, this is smart enough to not duplicate the script
// if it's already loaded
const loadScript = require("discourse/lib/load-script").default;
// we load the Discourse Icon HTML function to get the svg for the icon
// we want to use in the static composer preview
const { iconHTML } = require("discourse-common/lib/icon-library");
// setup the composer preview icon
const composerPreviewIcon = iconHTML(PREVIEW_ICON, {
class: "woxo-preview-icon"
});
// create a helper function for the composer preview markup
const previewMarkup = () => {
const markup = `<div class="woxo-preview">${composerPreviewIcon}</div>`;
return markup;
};
// create a post decorator
api.decorateCookedElement(
post => {
// does this post have woxo widgets?
const woxoWidgets = post.querySelectorAll("div[data-mc-src]");
// Yes, so let's do some work.
if (woxoWidgets.length) {
// for each woxo widget
woxoWidgets.forEach(woxoWidget => {
// if it's a composer widget, swap it out for a static preview and
// quit early
if (post.classList.contains("d-editor-preview")) {
woxoWidget.innerHTML = previewMarkup();
return;
}
// if it's not in the composer, load the woxo script.
loadScript(WOXO_SCRIPT_SRC).then(() => {
// The woxo script is very strange. It won't work unless the script
// tag has an empty data-usrc attribute. So, let's add it
const script = document.head.querySelector(
`script[src*="cdn2.woxo.tech"]`
);
script.dataset.usrc = "";
// everything is ready, let's call the init method in the woxo script
window.MC.Loader.init();
});
});
}
},
// add an id to the decorator to avoid memory leaks
{ id: "render-woxo-widgets" }
);
</script>