Embed Discourse comments on another website via Javascript

The hundreds of times I’ve been on that CSS page, and never noticed the CSS for embeddings…

1 Like

If someone is having trouble finding how the embed page looks like here it is:

I tried to implement this today but there are some problems for me:

  • In the forum post, the URL derived (from discourseEmbedUrl?) is lowercased(), breaking case-sensitive URLs in the link following the text “This is a companion discussion topic for the original entry at …”
  • On the first page load, the embed says “Loading discussion…” and hangs. Second page load everything works.
  • Page thrashing: when the iframe is loading, it will have a default height, but if there are no comments found, the height shrinks back down to only display one line with “Start Discussion”. This makes the “loading discussion” bug worse, because it holds open large vertical height
  • It would be nice if the data scraped from embed’s parent could favor a meta tag instead of textifying the whole page, I suppose this is more generalized discourse behavior

The biggest surprise was learning that every time you visit a URL with an embed, a new topic is created. This is in OP:

The only solution I can think of is hacky, relying on short term SSR/caching to ping the embed endpoint:

so I think the logic would be:

  1. check $discourse/embed/info?embed_url=$link

  2. if the topic exists, show the embed as usual.

  3. if not, show a “start conversation” button that links to $discourse/new-topic?subject=$link.

Would that work, or is there some other linkage that needs to take place?

A new topic is created only when a page with the embed is visited for the first time. On subsequent visits it renders the existing topic associated with the URL.

If you don’t want the automatic topic creation, you can create the topics via API calls the way it bests suits you and then pass the topicID to the embed to manually control the association.

Understood, this is brutal for my case because there are tens of thousands of pages

Like this?

  1. API check to see if topic exists
  2. doesn’t exist, show button
  3. click button, API add topic
  4. successful, button container transforms into embed

…that has some issues too, like button clicks with no follow up engagement.

Is there any way to only create the topic when a user has actually commented, something like this?:

$discourse/new-topic?subject=$link.

or is the issue here that we can’t pass in topicID for deterministic coupling?

No, it’s not possible to reply to a topic that doesn’t exist in Discourse.

To address this need, we made it possible to create all embeds topics as unlisted topics that are automatically listed the first time someone replies.

I think creating the topic will require an authenticated request that’s run on the server. If you don’t want to deal with that, another approach would be to add a button to the UI that triggers code similar to this (the code that would otherwise get automatically added to the page’s head tag): discourse/public/javascripts/embed.js at 581dbca97f2b55c9bbbe40dc3b58a9df7409d77f · discourse/discourse · GitHub. It’s just creating an iframe element using this data:

<div id='discourse-comments'></div>
<meta name='discourse-username' content='DISCOURSE_USERNAME'>

<script type="text/javascript">
  DiscourseEmbed = {
    discourseUrl: 'http://127.0.0.1:4200/',
    discourseEmbedUrl: 'EMBED_URL',
    // className: 'CLASS_NAME',
  };
</script>

Edit: I had to give it a try. This is just a proof of concept: discourse-embed-iframe-test/app/routes/triggering-embed-code.tsx at main · scossar/discourse-embed-iframe-test · GitHub. I don’t think it’s a great solution for your problem.

The logic is entirely on the client, (the loader function’s just for convenience). Unfortunately there isn’t a great way for the client to know if the topic exists on Discourse. So it’s not possible to customize the UI based on whether or not the topic already exists. There are ways that could be solved, but would probably require writing something to the app’s database.

Discourse is using Window: postMessage() to pass data from the iframe to the parent document. For example, when a reply link is clicked in the embedded comments: discourse/app/assets/javascripts/discourse/scripts/embed-application.js at 581dbca97f2b55c9bbbe40dc3b58a9df7409d77f · discourse/discourse · GitHub. I wonder if a message could be sent to the parent document to indicate that a topic had been created. That would let sites do thing like set custom loading spinners, or display different UI depending on whether or not the topic was ready to receive comments.

1 Like

I don’t think that need be the case if it were possible to assign a primary link to a new post:

$discourse/new-topic-link?link=$link

Which would presumably forward to the thread if it already exists and use the existing logic in the embed admin. Maybe with templates?

I guess another way to solve this is to search for any posts that refer to the link in question and surface any matches as part of a site-wide conversation, rendering directly in the page via SSR. This might be equivalent given that the embeds are view only anyways. The most obvious issue there would be choosing which thread to send the user to when they want to participate.

edit: if I can figure out how to search posts by link content

I’m wondering whether this feature may be used to attack a Discourse forum. Specifically, is it possible for someone falsify visiting pages under multiple URLs of a host and creates many many topics?