Rich Content

If a Message Part contains a large amount of content (> 2KB), it will be sent as rich content instead. This has no effect on how you send a Message.

Sending Rich Content

var fileInputNode = document.getElementById('myFileInput');

// Create one part from the file
var part = new layer.Core.MessagePart(fileInputNode.files[0]);
var message = conversation.createMessage({
    parts: [part]
});

// Optional: Add a text/plain part
message.addPart(new layer.Core.MessagePart({
  mimeType: 'text/plain',
  body: 'Hope you like this image'
}));

// Send the message
message.send();

The above code accesses an HTML File input, gets its data from the Javascript File object (subclass of Blob), and sticks the File into the body of the Message Part. The mimeType is automatically set using the type property of the File object. You can change the MIME type after creating it:

var part = new layer.Core.MessagePart(file);
part.mimeType = 'custom/type';

Receiving Rich Content

There are some differences in how such a Message would be received:

The MessagePart with Rich Content will have a body equal to null, but you will instead have access to the content via the url property, as well as convenience methods that will download the content and write it to the body.

if (part.mimeType === 'image/png') {
    if (part.url) {
       return '<img src="' + part.url + '"/>';
    }
}

The url property

The url property expires after a few hours after which it can no longer load your content. This means that:

  • If your UI loads the MessagePart but the user does not interact with it for a few hours, they may be unable to access it.
  • If your UI loads and renders the MessagePart, it should be fine as long as a re-render does not try to re-download the content later on.
  • If your user opens the url in a new tab and tries to share that URL with others, that URL will expire; this makes sharing problematic.

There are two types of values that can be stored in the url property:

  • Expiring URL: The URL points to a resource in the cloud, but the URL will expire after a few hours.
  • Local URL: The data from the resource in the cloud has been downloaded to the browser, represented by a local URL (see URL.createObjectURL()). An example of the URL generated: blob:http%3A//mydomain.com/bc01aeb1-ff6e-4c34-90a3-a7f50b7ecb79. This URL is not sharable but also does not expire.

Under normal conditions, the url property will return a value until it expires, and then will return an empty string.

The fetchContent method

The fetchContent method on each MessagePart does the following:

  • Loads the content from the server
  • Stores the results in the body property
  • Creates a local URL for accessing that data in url (this URL does not expire)

In this case, it’s up to you to decide whether to use body or url. Note that body will be a Blob, making url more direct for rendering images.

Note

If the part’s MIME type is text/plain, the body will be a String rather than a Blob.

If you’re reading a large application/json MIME type, this may be text, but not text/plain, so you’ll have to process it:

var reader = new FileReader();
reader.addEventListener('loadend', function() {
  var partData = JSON.parse(reader.result);
  myRenderJSONPart(partData);
});
reader.readAsText(part.body);

The fetchStream method

The fetchStream method refreshes the url property so that it won’t expire for another few hours.

You should use fetchStream if, for example:

  • You have a <a href={part.url}></a> link on your page. You may not want to download all of the content for every one of your links just to prepare for the possibility that the user will click on the link. Instead, offer a link and when the user opens that link in a new tab (or new application) the data will be downloaded by the new tab.

  • You have a video or other streaming content. You don’t want to wait for all of the content to download via fetchContent before rendering. You want to render what you have regardless of how much has downloaded: <video src={part.url}></video> will render the video without waiting for the whole video to download.

When you first receive a MessagePart, it will already have a valid url property. Only call fetchStream when the url has expired (when it returns null), which should be a few hours after you first receive the MessagePart.

// If the URL has expired, load it again
if (!part.url)
  part.fetchStream();
return '<img src="' + part.url + '"/>';

The hasContent property

The hasContent property allows you to test if there should be a URL, which is useful if you’re using an expiring URL that may become null:

// A small image was sent using the body property and a base64 encoding:
if (part.body && part.mimeType === 'base64') {
  return '<img src="data:image/png;base64,' + part.body + '"/>';
}

// Or it was sent using Rich Content; this code may render an empty image if
// the url has expired; but if it has expired, a new url will be loaded by fetchStream().
else if (part.hasContent) {
  if (!part.url)
    part.fetchStream();
  return '<img src="' + part.url + '"/>';
}

The content-loaded event

The fetchContent method will trigger a content-loaded event when its data has been loaded:

if (part.mimeType === 'text/plain') {
    // If we don't have a body, call fetchContent and update our rendering
    // when the content is loaded.
    if (!part.body) {
      part.fetchContent().on('content-loaded', function() {
          document.getElementById('fred').innerHTML = part.body;
      });
      return '<div id='fred' />';
    }

    // Either this isn't rich content...
    // or fetchContent() has already populated the body property:
    else {
        return '<div>' + part.body + '</div>';
    }
}

The url-loaded event

The fetchStream method will trigger a url-loaded event when the url has been refreshed:

if (part.mimeType === 'image/jpeg') {
    // If we don't have a url, call fetchStream and update our rendering
    // when the url is refreshed.
    if (!part.url) {
      part.fetchStream().on('url-loaded', function() {
          document.getElementById('fred').src = part.url;
      });
      return '<img id='fred' />';
    }

    // The URL has not yet expired:
    else {
        return '<img src="' + part.url + '" />';
    }
}

The message:change event

Both fetchContent and fetchStream update the properties of a MessagePart, so they will trigger a message:change event.

function renderMessage(message) {
  message.parts.forEach(function(part) {
    renderPart(part);
  });
}

function renderPart(part) {
  if (!part.body) part.fetchContent();
  return '<div>' + part.body + '</div>';
}

message.on('messages:change', function() {
  renderMessage(message);
});

The above call to fetchContent will eventually lead to messages:change being triggered, causing renderPart to be called again, this time with a body property that can be rendered.

Announcements Querying