Skip to content
This repository has been archived by the owner on Jun 17, 2024. It is now read-only.

Adding previews to links #53

Open
pezholio opened this issue Dec 17, 2018 · 6 comments
Open

Adding previews to links #53

pezholio opened this issue Dec 17, 2018 · 6 comments

Comments

@pezholio
Copy link

pezholio commented Dec 17, 2018

I'm trying to adapt the starter kit to show previews of links (a la Facebook messenger, WhatsApp etc). I think the overall concept is sound, but as there's a delay getting the metadata from webpages, the next message will often show before the preview. Is there any way to stop the bot from processing while the metadata is fetched?

I've added an Express endpoint to my bot to get previews:

var og = require('open-graph');

module.exports = function(webserver, controller) {
  
  webserver.post('/link_preview', function(req, res) {
    og(req.body.url, function(err, meta){
      res.setHeader('Content-Type', 'application/json');
      res.send(JSON.stringify(meta));
    })
  });

}

Then I have a function that calls the endpoint in client.js:

getLinkPreview: function(url, done) {
  var xhr = new XMLHttpRequest();
  var postData = 'url=' + url
  xhr.open('POST', '/link_preview', true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

  xhr.onload = function () {
    var response = xhr.responseText;
    var preview = null;
    try {
      preview = JSON.parse(response);
    } catch (err) {
      done(xhr.response);
      return;
    }
    done(null, preview);
  };
  xhr.onerror = function () {
    done(xhr.response)
  };
  xhr.send(postData);
},

And finally render the actual output when the message event is called:

that.on('message', function(message) {
if (message.files) {
var url;

for (var i = 0; i < message.files.length; i++) {
  url = message.files[i].url
  that.getLinkPreview(url, function(err, response) {
    if (err) { throw err; }
    var message = document.createElement('div')
    message.setAttribute('class', 'message message')

    var meta = document.createElement('div');
    meta.setAttribute('class', 'meta');

    var link = document.createElement('a')
    link.setAttribute('href', url)
    link.innerHTML = url;
    message.appendChild(link)

    var title = document.createElement('h3')
    title.innerHTML = response.title;

    meta.appendChild(title)

    if (response.image) {
      var image = document.createElement('img')
      image.setAttribute('src', response.image.url);
      image.setAttribute('width', '120px');
      meta.appendChild(image)
    }

    if (response.description) {
      var description = document.createElement('p')
      description.innerHTML = response.description;
      meta.appendChild(description)
    }

    message.appendChild(meta)

    that.message_list.appendChild(message)
  });
}

I imagine I'm doing something wrong at the last point, but not sure where to start. Any help is appreciated!

@Naktibalda
Copy link
Contributor

Your code appends link preview to the bottom of message list.
I think that it would be better to insert it right next to the message containing link.

I had to look it up, but something like this should do the job: https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement

@pezholio
Copy link
Author

Ah OK, that seems like a good call. Any idea how I'd get a reference to that message's element? I've tried a couple of things, but to no avail. I think there also might be an issue if there's a number of links. Is there any way I can put an artificial delay in place while the preview is being fetched?

@Naktibalda
Copy link
Contributor

  1. Create message element and append it to message list.
  2. Make preview request
  3. On receiving response, modify message div element that you can still access from outer scope.

@pezholio
Copy link
Author

Ah, never mind. I've solved this by using promises and getting the preview first, then rendering the response using the template:

addLinkPreview: function(message) {
  return new Promise(function(resolve, reject) {
    if (message.files !== undefined && message.files.length > 0) {
      var url;

      for (var i = 0; i < message.files.length; i++) {
        url = message.files[i].url
        that.getLinkPreview(url, function(err, response) {
          if (err) { throw err; }
          
          message.link_preview = {
            url: url,
            data: response
          }

          resolve();
        });
      }
    } else {
      resolve();
    }
  })
}
that.on('message', function(message) {

  that.addLinkPreview(message).then(function() {
    that.renderMessage(message);          
  });

});
{{#if message.link_preview}}
    <div class="meta">
    <a href="{{{ message.link_preview.url }}}">{{{ message.link_preview.url }}}</a>
    <h3>{{{ message.link_preview.data.title }}}</h3>
    {{#if message.link_preview.data.image}}
        <img src="{{{ message.link_preview.data.image.url }}}" width="120px" />
    {{/if}}
    {{#if message.link_preview.data.description}}
        <p>message.link_preview.data.description</p>
    {{/if}}
    </div>
{{/if}}

@benbrown
Copy link
Contributor

Pretty cool! Is there something here that could be contributed back to the project?

@pezholio
Copy link
Author

Quite possibly, I (mis)used the files field for this, but I'll see what I can tidy up and push up 👍

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants