Layer Messaging Experiences

Layer’s XDK provides a framework for building and rendering rich and interactive Messaging Experiences. Each message that is sent/received by an application built using Layer consists of a collection of Message Parts that have MIME Types that define what type of message it is. Each type of message can have its own custom rendering, interactions and operations. Layer’s XDK provides the tools for providing these Message Types and rendering them; it also comes with a small library of these built in.

The tools for building these Messaging Experiences are:

  • Message Viewer: A UI Component that accepts a Message as input and from it, instantiates a Message Type Model and a Message Type View
  • MessageTypeModel: Represents a specific type of Message such as an Image Message, exposing properties, methods and operations for interacting with that type of message
  • Message Type View: A rendering for a specific type of Message Type Model
  • Message View Container: A container that lays out the Message Type View with respect to other relevant information or widgets related to the Message

Examples of these include:

  • Text Message Type Model + Text Message Type View combine to create the Text Message
  • Receipt Message Type Model + Receipt Message Type View combine to create the Receipt Message

The Message Viewer

Every Message that is rendered within the Message List is handled via a Message Viewer component.

The Layer Message Viewer Component performs the following actions:

  1. Identify what Message Type the Message represents
  2. Based on the Message Type, Instantiate an appropriate MessageTypeModel for the Message.
  3. Instantiate Message Type View that knows how to render the core data for that Model. (e.g. a UI that renders a Map for a Location Message, or a UI that renders an Image for an Image Message)
  4. Instantiate the Message View Container that the Message Type View is designed to work with. This Message View Container knows how to render common properties such as titles, subtitles, etc, and where they go with respect to the Message Type View.

What is a Message Type Model?

Up to this point in the documentation, Messages and Message Parts were treated as abstract concepts that took a custom MIME Type such as text/plain, some body such as hello world, and what you do with these is entirely unspecified. As part of providing a system of Messaging Experiences, the definitions and expectations of what goes in the MessageParts has become a lot more specific; these MessageParts are expected to match a specific format and set of rules. The following Message represents a Carousel of Maps; this example is presented here so that you can see a hint of what Messages and Message Parts underlying this will look like. You will not need to really work with Messages and Message Parts directly until you start creating Custom Messaging Experiences.

[
  {
    "mime_type": "application/vnd.layer.carousel+json; role=root; node-id=6307b011-e0e9-4bb5-8b97-99309e49cbfc",
    "body": JSON.stringify({})
  },
  {
    "mime_type": "application/vnd.layer.location+json; role=carousel-item; parent-node-id=6307b011-e0e9-4bb5-8b97-99309e49cbfc",
    "body": JSON.stringify({
        "title": "Layer HQ 2017",
        "street": "655 4th st",
        "city": "San Francisco",
        "administrative_area": "CA",
        "postal_code": "91447"
    })
  },
  {
    "mime_type": "application/vnd.layer.location+json; role=carousel-item; parent-node-id=6307b011-e0e9-4bb5-8b97-99309e49cbfc",
    "body": JSON.stringify({
        "title": "Near Layer HQ 2017",
        "street": "650 4th st",
        "city": "San Francisco",
        "administrative_area": "CA",
        "postal_code": "91447"
    })
  },
  {
    "mime_type": "application/vnd.layer.location+json; role=carousel-item; parent-node-id=6307b011-e0e9-4bb5-8b97-99309e49cbfc",
    "body": JSON.stringify({
        "title": "Less Near Layer HQ 2017",
        "street": "645 4th st",
        "city": "San Francisco",
        "administrative_area": "CA",
        "postal_code": "91447"
    })
  }
]

So… thats a lot of raw data, all of it burried in JSON strings, and all of it spread across a bunch of Message Parts and MIME Types. Rather than have that as what developers interact with to create unique Messaging Experiences, we instead provide a MessageTypeModel that abstracts all of the above data into a simple set of methods, properties and operations.

A MessageTypeModel provides the following services:

  1. Understands and abstracts all of the complexities of the Message and its Message Parts into simple APIs that can be used by developers
  2. Provide APIs for generating Message Parts and Messages from abstract properties.
  3. Manage Message Responses
  4. Manage adding, removing and editing of Message Parts (operations that may be performed locally or by a remote user forcing updates to our local copy)
  5. Maps values of the model to a Title, Description and Footer (for Message Type Views that use Message View Containers)
  6. Provide named actions to be triggered if a user clicks/selects a Message

A MessageTypeModel is used in two common contexts:

  1. The Message List is iterating over each Message and generating a List Row containing a Message List Item. Each Message Item contains a <layer-message-viewer /> which instantiates a Model from the Message and a View that renders that Model.
  2. An application can create and send a properly structured message by instantiating a Model to help create and send the message.

Developers using prebuilt Layer Messages should only care about how to use a Model to create Messages of various types. To learn more about how to create new Models, see Customizing Messaging Experiences.

MessageTypeModel Actions

All Models will have the following properties:

Name Required Type Description
action No Object Contains a description of what happens when the user clicks/selects a Message rendered from this Model
action.event No String A string naming the event to trigger. Some events are handled by the <layer-message-viewer /> itself; any viewer can handle an open-url, open-map and open-file action events. Custom event names are trigged as events on the DOM structure which can be intercepted and processed via document.addEventListener('my-action-event-name', fn)
action.data No Object Note that data commonly comes from the Model directly; a Link Message has a url, a File Message has a source; but suppose you wanted to override that behavior… or to have an open-url action on a Text Message? You can put a url into model.action.data and this url will take precedence over any other url for the open-url action.

Typically one might create a Model as follows:

const model = new LinkModel({
  url: "https://layer.com"
});

and then send the message as follows:

model.send({ conversation });

This will cause a properly structured message to be sent, and all participants will see this Link Message; clicking on it will perform the default action.event for the LinkModel of open-url. It will find the url from the LinkModel.url property and open it.

You can, however, provide a custom URL for the action:

const model = new LinkModel({
  url: "https://layer.com",
  action: {
    data: {
      url: "https://layer.com?tracking=opened-from-link-message"
    }
  }
});

Execution of any action looks first for properties within action.data and then gets any properties that are not provided there from the model itself. An open-url action on the above model would ignore the LinkModel.url and instead use LinkModel.action.data.url; it would only use LinkModel.url if there was no LinkModel.action.data.url.

You can as well provide a custom event:

const model = new LinkModel({
  url: "https://layer.com",
  action: {
    event: 'load-url-in-sidebar'
  }
});
document.body.addEventListener('load-url-in-sidebar', function(evt) {
  // The message the user clicked on:
  const selectedModel = evt.detail.model;

  // If the user clicked on some inner message type viewer within a larger
  // Messaging Experience (a Carousel Item of a Carousel)
  // then this property lets us access the root model for
  // the message (i.e. the carousel instead of the carousel item)
  const rootModel = evt.detail.rootModel;

  // Any custom data provided via "model.action.data"
  const data = evt.detail.data;

  // Use some custom function to handle showing this url.  Note that the
  // handler gives priority to any
  // custom url provided in `action.data`.
  openUrlInSidebar(data.url || selectedModel.url);
});

Providing your own event names and your own event handlers is an easy way to customize behaviors and add interactions to your application without having to building your own Custom Message Types.

MessageTypeModel Action Events

All Action Events come with the following properties:

Name Type Description
data Object Custom Data provided to the operation. This Custom Data comes from either the model’s action.data when the Message is tapped by the user, or from an Action Button’s data property when an Action Button is tapped by the uuser.
model MessageTypeModel The model representing the Message/Sub-Message that was selected (or whose Action Buttons were selected by the user)
rootModel MessageTypeModel If the model property was a Sub-Model (i.e. a Carousel Item within a Carousel Message), then this provides the top level MessageTypeModel that the model exists within. rootModel might be a Carousel Model, while model is a simple Link Model.
messageViewer Message Viewer The View associated with the model

Events are triggered in two ways:

  1. DOM level events are triggered, allowing node.addEventListener to be used to intercept and handle these events
  2. Handlers for actions are registered via Layer.UI.registerMessageActionHandler(action, handler).
  • Your event handler from step #1 may call event.preventDefault() which will prevent any handler in step #2 from being called.

Recommended practices are as follows:

Override the existing open-url action
Layer.UI.registerMessageActionHandler('open-url', function({ data, model, rootModel, messageViewer }) {
   window.open(data.url || model.url);
});

The above code replaces any existing open-url action handler with this new action handler definition

Conditionally override the existing open-url action
document.body.addEventListener('open-url', function(evt) {
  const { data, model, rootModel, messageViewer } = evt.detail;
  if (data.openInSidebar) {
    myOpenInSidebarFunc(data.url || model.url);
    evt.preventDefault();
  }
});

The above code only calls evt.preventDefault() if the above event listener is going to handle the open-url action on its own. If its not going to handle it, then evt.preventDefault() is not called, and the registered open-url actin handler will handle this event.

Registering a New Action Handler

If defining a custom action for your application’s messages, you should register an Action Handler to process any message that has that action. This example assumes a custom Message Type Model has been defined that contains a bill property and a processBill method.

Layer.UI.MessageActions.register('kill-bill', function({ data, model, rootModel, messageViewer }) {
   const bill = data.bill || model.bill;
   model.processBill(bill);
});

Note that the above could also have been written as an addEventListener method. When should one use an Event Listener vs an Action Handler?

  1. If you are designing a Message Type Model with its own default Action, you should define an Action Handler for that action. The Message Type Model may get used in multiple UIs/projects, and having the Action Handler built into your custom model definition insures that everyone who uses the custom Message Type will also have the Action Handler.
  2. If you are simply passing in a custom action.event into an existing Model Definition new LinkModel({action: {event: 'kill-bill'}}) then typically using addEventListener is simpler, though if there is common code shared by all projects that understand kill-bill then registerMessageActionHandler could still be used to provide a global definition for the action.

MessageTypeModel Custom Data

All MessageTypeModel will have the following properties:

Name Required Type Description
customData No Object Arbitrary data that won’t affect rendering or actions of the message but does affect understanding of the message

A simple example of where customData might be used is to add a Product ID, or SKU Number to a Model that does not have those concepts built into it. While this Product ID would not affect how its rendered, it may be used for use cases such as:

  • Your server may have a webhook that detects messages of a specific type, and needs a Product ID (or other custom data) to properly log the event or kick off a process on behalf of the users
  • When the user selects the message triggering some custom action within the browser, this allows for the action to have more information about the message. This is generally a better way to associate custom information about a message than action.data as it allows your messages to standardize on a set of expected properties; those that are built into the model, and those that you add to the model.

Text Messages

TextMessageView is included as part of the standard npm build and require no additional import commands

A Text Message has a few uses:

  • A standard conversational message containing messages users type to one another
  • A framed and titled Message for quoted text. For example, a quote from a product manual or a product page might be rendered, and attribution given as to where it came from so that it does not look like something that the sender just typed in.
Text Message with Details
Text Message without Details

The above illustrates the difference between a Text Message that is rendered as a conversational message, and a Text Message that renders quoted text with a title and footer.

A Text Message can be instantiated using:

const TextModel = Layer.Core.Client.getMessageTypeModelClass('TextModel');

// 1. Create the Model
const model = new TextModel({
  text: "Starting at the SIM card slot, stick the plastic opening tool in between the back panel and the phone. Slide the opener tool gently around the phone until you hear clicks and visibly see the panel separate from the body of the phone. Continue this process on each edge of the phone, until the only thing holding the back panel to the body of the phone is the fingerprint scanner.",
  title: "Step 2 of Nexus 5x Battery Replacement",
  author: "ifixit"
});

// 2. Generate a Message from the Model (asynchronous)
model.send({
  conversation,
  notification: !customShouldNotifyParticipants ? undefined : {
    title: "New Message from " + message.sender.displayName,
    text: model.title,
    sound: "soundfile.aiff"
  };
});

The Text Model supports the following properties:

Name Required Type Description
text Yes String This is the core data rendered by the Core UI for this Message
title No String Title for the Text Message
subtitle No String Rendered below the title
author No String Rendered below the subtitle

Note

If text is provided, but no title, subtitle or author then this Text Message will render as a regular chat bubble rather than look like a quoted message with metadata shown below it.

LinkMessageView is included as part of the standard npm build and require no additional import commands

A Link Message has a few uses:

  • A simple way to send a link to users
  • A framed summary of a web page, article or other online content consisting of a picture, title, description and the url itself.

Note

URLs sent within a Text Message will be hyperlinked; this means that links can be sent as part of a Text Message. The Link Message provides a richer experience, and clicking anywhere on the message will open the link, and Link Messages will not truncate the rendering of the URL the way that a Text Message will do.

Example of a Link Message without any Details
Example of a Link Message showing an Article with Details

A Link Message can be instantiated using:

const LinkModel = Layer.Core.Client.getMessageTypeModelClass('LinkModel');

// 1. Create the Model
const model = new LinkModel({
  url: "https://layer.com/company/about-us/#team-container",
  title: "Meet the Layer Team",
  description: "Our world class and world spanning team of messaging experts is ready to help you build the best possible customer experience",
  imageUrl: "https://layer.com/wp-content/uploads/2017/06/ron-palmeri.png"
});

// 2. Generate a Message from the Model (asynchronous)
model.send({ conversation });

// Or...
model.send({
  conversation,
  notification: !customShouldNotifyParticipants ? undefined : {
    title: "New Link from " + message.sender.displayName,
    text: model.title,
    sound: "soundfile.aiff"
  };
});

The Link Model supports the following properties:

Name Required Type Description
url Yes String The URL to open; used by the open-url action, and by the URL Enrichment Integration (optional)
title No String Title for the Link Message
description No String Rendered below the title
author No String Rendered below the description
imageUrl No String URL to an image to render over the title

Note

If url is provided, but no title, description, author or imageUrl then this Link Message will render as a conversational message and show the url value directly.

An optional URL Enrichment Integration Service will be made available to customers that looks for any Link Message that contains only the URL, and updates the Message to have a title, description, author and imageUrl based on data it reads off of the web page loaded from that URL.

File Messages

FileMessageView is not part of the default npm build; include it using:

import '@layerhq/web-xdk';
import '@layerhq/web-xdk/messages/file/layer-file-message-view';

A File Message has a few uses:

  • Allow a user to drag and drop one or more files from their desktop and send them as Messages that recipients can open
  • Allow an application to send a reference to a file in its library, with accompanying descriptions

A File Message can be instantiated using:

const FileModel = Layer.Core.Client.getMessageTypeModelClass('FileModel');

// Send the raw file
const model = new FileModel({
  source: FileObj, // Javascript File object
  title: "expense-report.pdf",
  author: layerClient.user.displayName
});
model.send({ conversation });

// Send a reference to a file
const model = new FileModel({
  sourceUrl: "https://raw.githubusercontent.com/layerhq/web-xdk/master/README.md",
  title: "WebXDK README file",
  author: "Layer"
});
model.send({ conversation });

The File Model supports the following properties:

Name Required Type Description
source No File/Blob A Javascript File or Blob object
sourceUrl No String URL to a remote file (should not be used if source is in use)
title No String Title for the File Message
author No String Rendered below the title
size No Number Represents the size of the file in number of Bytes
mimeType No String Indicates the MIME Type of the file

Note

  • source or sourceUrl must be provided for this to be a valid File Message.
  • Do not use both source and sourceUrl.

Image Messages

ImageMessageView is included as part of the standard npm build and require no additional import commands

An Image Message has a few uses:

  • Allow a user to drag and drop one or more Images from their desktop and send them as Messages that just show the image and nothing more
  • Allow an application to send images with titles, subtitles and other annotations. For example, one might send a product picture with the name and description of the product associated with it.
Basic Image Message
Image with Details

A Image Message can be instantiated using:

const ImageModel = Layer.Core.Client.getMessageTypeModelClass('ImageModel');

// Send the raw Image
const model = new ImageModel({
  source: FileObj // Javascript File or Blob object
});
model.send({ conversation });

// Send the image with extra information to render
const model = new ImageModel({
  source: FileObj, // Javascript File or Blob object
  title: "Rainbows and gummy bears",
  subtitle: "Rainbows and gummy bears fighting one another to the death",
  artist: "Michael"
});
model.send({ conversation });

// Send a reference to a remote image
const model = new ImageModel({
  sourceUrl: "https://i.ytimg.com/vi/HNsraQqDIQ8/maxresdefault.jpg",
  title: "Rainbows and gummy bears",
  subtitle: "Rainbows and gummy bears fighting one another to the death",
  artist: "Michael",
  width: 1280,
  height: 720
});
model.send({ conversation });

The Image Model supports the following properties:

Name Required Type Description
source no Image/Blob A Javascript File or Blob object representing a known Image format
sourceUrl No String URL to a remote Image (do not use this if source is in use)
title No String Title for the Image Message
subtitle No String Rendered below the title
author No String Rendered below the subtitle
width No Number Width in pixels of the source image
height No Number Height in pixels of the source image

Note

  • source or sourceUrl must be provided for this to be a valid Image Message.
  • Do not use both source and sourceUrl.
  • If using sourceUrl, you should provide a width and height value. width and height will be added automatically if providing an File/Blob representing an Image.

Location Messages

LocationMessageView is not part of the default npm build; include it using:

import '@layerhq/web-xdk';
import '@layerhq/web-xdk/messages/location/layer-location-message-view';

A Location Message has a few uses:

  • Show a map of a location; perhaps a restaurant to meet at? A house to sell? Or just your current position in the world
  • Show an address, for example, as a sub-message-view of a Receipt Message

Just the map:

Just a Map
Map with Details
Map with Address

A Location Message can be instantiated using:

Note that you will need a Google Maps API Key, and will pass that into Layer.init().

const client = Layer.init({
  appId: 'layer:///staging/UUID',
  googleMapsKey: 'my-google-maps-key',
});



const LocationModel = Layer.Core.Client.getMessageTypeModelClass('LocationModel');

// Send the address
const model = new LocationModel({
  city: 'San Francisco',
  title: 'Layer Inc',
  postalCode: '94107',
  administrativeArea: 'CA',
  street1: '655 4th st'
});
model.send({ conversation });

// Send the lat/long
const model = new LocationModel({
  title: 'Layer Inc',
  latitude: 37.7734858,
  longitude: -122.3916087
});
model.send({ conversation });

The Location Model supports the following properties:

Name Required Type Description
latitude No Number Use lat/long and/or street address
longitude No Number Use lat/long and/or street address
zoom No Number Zoom level from 1 - 18; defaults to 16; see Google Maps API
title No String Title for the Location Message
description No String Rendered below the title. If present, will be rendered instead of an Address.
street1 No String String address line 1
street2 No String String address line 2
city No String City that the location is within
administrativeArea No String Name of the state or comparable unit of region
postalCode No String Zipcode or some regionally comparable concept
country No String Country code or name

Note

  • To only show the map, and no other information, include latitude and longitude, and leave out titles, addresses and descriptions.
  • If you want to render an Address instead of the description, leave the description empty.
  • To make the Location Message show an address only, and not a map, this is managed via the Component’s properties rather than the Model’s properties. <layer-location-message-view hide-map="true"></layer-location-message-view> will hide the map, and show only the address. Typically this is only done by a Parent Message Type View that contains the Location Message View.

Status Messages

StatusMessageView is included as part of the standard npm build and require no additional import commands

A Status Message is a Message within the Message List that:

  • Does not have an avatar, nor indicate any sender
  • Is centered rather than right or left aligned as though it came from one or the other participant
  • Does not have a timestamp
  • Supports only textual information
A simple Status Message

A Status Message can be instantiated using:

const StatusModel = Layer.Core.Client.getMessageTypeModelClass('StatusModel');

const model = new StatusModel({
  text: "Processing your request, this may take a few minutes."
});

model.send({ conversation });

The Status Model supports the following properties:

Name Required Type Description
text Yes String This is the core data rendered by the Core UI for this Message

CarouselMessageView is not part of the default npm build; include it using:

import '@layerhq/web-xdk';
import '@layerhq/web-xdk/messages/carousel/layer-carousel-message-view';

The Carousel Message presents an arbitrary collection of sub-message-views in a left-right scrolling view.

While one can create a Carousel that contains a mix of Image Messages, Text Messages, Product Messages, Button Messages, etc… for best results, the Caroursel items:

  1. Should all be of the same type
  2. Should all contain similar sizes and quantities of content
Carousel of File Messages

A Carousel Message can be created using:

const CarouselModel = Layer.Core.Client.getMessageTypeModelClass('CarouselModel');
const FileModel = Layer.Core.Client.getMessageTypeModelClass('FileModel');

// Send the address
const model = new CarouselModel({
  items: [
    new FileModel({
      sourceUrl: "https://raw.githubusercontent.com/layerhq/web-xdk/master/README.md",
      title: "XDK README file",
      author: "Layer"
    }),
    new FileModel({
      sourceUrl: "https://raw.githubusercontent.com/layerhq/web-xdk/master/LICENSE.md",
      title: "XDK License file",
      author: "Apache License 2.0"
    }),
    new FileModel({
      sourceUrl: "https://raw.githubusercontent.com/layerhq/web-xdk/master/package.json",
      title: "XDK Dependencies",
      author: "Layer"
    })
  ]
});
model.send({ conversation });

Its important to understand that a Carousel Message’s action.event and action.data will override all of the Carousel Item’s default actions. In the above example, the default action on each FileModel is open-file. We can change that as follows:

// NOTE: Creating Carousel Models on mobile clients is not currently supported; this Javascript example can be used to create test messages from a web browser


const model = new CarouselModel({
  items: [
    new FileModel({
      sourceUrl: "https://raw.githubusercontent.com/layerhq/web-xdk/master/README.md",
      title: "XDK README file",
      author: "Layer"
    }),
    new FileModel({
      sourceUrl: "https://raw.githubusercontent.com/layerhq/web-xdk/master/LICENSE.md",
      title: "XDK License file",
      author: "Apache License 2.0"
    }),
    new FileModel({
      sourceUrl: "https://raw.githubusercontent.com/layerhq/web-xdk/master/package.json",
      title: "XDK Dependencies",
      author: "Layer"
    })
  ],
  action: {
    event: 'delete-file'
  }
});

When the user clicks on ANY of the Messages specified in items, it will invoke the delete-file action on the Carousel Item, and will NOT trigger an open-file action. NOTE: If any of the items specifies its own action.event, this will not be overwritten; only default values for action.event are overwritten.

Futher, you can add custom action data:

const model = new CarouselModel({
  items: [...],
  action: {
    data: {confirm_deletion: true},
    event: 'delete-file'
  }
});

If your custom delete-file event handler checks for the evt.detail.data.confirm_deletion it will know whether to directly delete the file or ask for confirmation. action.data will be mixed into any existing action.data on each Carousel Item, and will not overwrite any action.data values explicitly entered for a given sub-message within the carousel; this means that individual Carousel Items could have their own value for evt.detail.data.confirm_deletion.

The Carousel Model supports the following properties:

Name Required Type Description
items Yes MessageTypeModel[] Array of Message Type Models representing the Items the Carousel will scroll through

Choice Messages

ChoiceMessageView is not part of the default npm build; include it using:

import '@layerhq/web-xdk';
import '@layerhq/web-xdk/messages/choice/layer-choice-message-view';

The Choice Message presents the user with a set of choices to make, and when the user has made their selection, it updates the message’s state for all participants in the Conversation.

Note

This Message, unlike others, depends upon a Message Response Integration Service which you will need to install.

Choice with No Selection
Choice with Selection and allowReselect: false

A Choice Message, configured to not allow Reselection:

ChoiceModel = Layer.Core.Client.getMessageTypeModelClass('ChoiceModel');
model = new ChoiceModel({
  allowReselect: true,
  label: "What is the airspeed velocity of an unladen swallow?",
  enabledFor: "layer:///identities/a"
  choices: [
    {text:  "Zero, it can not get off the ground!", id: "zero"},
    {text:  "Are we using Imperial or Metric units?", id: "units"},
    {text:  "What do you mean? African or European swallow?", id: "continent"},
  ]
});
model.send({ conversation });

The above Choice Message will ask the user the specified question, and provide the specified choices for the user to choose from. When the recipient selects an answer, the following actions will be performed:

  1. A Response Message is sent indicating what the user selected, and providing a textual description of that selection
  2. A Response Message is rendered for all participants, representing that Response Message, and informing all users of the change in the state of the Message. It might say something like “User X has selected Are we using Imperial or Metric units?”.
  3. The Message’s local state is promptly updated for the user who made the selection, providing quick feedback to the user that the selection was accepted.
  4. The Message Response Integration Service receives the Response Message, and updates the original Choice Message with the user’s Selection
  5. All other participants receive the update to the Choice Message, and now all users see the selection.

The choices array is designed to have an id so that edits to the text values may eventually be supported, while leaving the id values unchanged. This would leave the user’s selections intact, while allowing for tweaks to the text.

The Buttons Model supports the following properties:

Name Required Type Description
label N String A question to ask the user
choices Y Object[] Array of choices for the user to pick from
choices.text Y String Text to show the user within a selection button
choices.id Y String ID that identifies the selection and distinguishes it from the other choices
allowReselect N Boolean Default is false; if false then selections may not be changed once made
allowDeselect N Boolean Default is false; if false then selections may not be selected again to deselect them. If true then allowReselect will be set to true
allowMultiselect N Boolean Default is false; if false then only a single selection is allowed at a time. If this is true, users may select multiple choices, and allowReselect and allowDeselect are set to true.
preselectedChoice N String Specify the id of the default selected choice. User selected choices will take precedence over this value. If the user deselects their choice, this value is NOT restored.
responseName N String Name that will uniquely identify this response within the Message and differentiate it from other types of responses
enabledFor Y String Identity ID of the user allowed to select these buttons
customResponseData N Object Name value pairs to add to the Message Response data. Allows you to add some context to the Response Messages as well as to the data added to the Choice Model once the Message has been updated
title N String The default title of Choose One can be replaced by providing your own title

Note

  • allowReselect is enforced client side, not server side, and should not be treated as securely enforced at this time.
  • Failure to set a responseName to a unique name could cause different message interactions to overwrite one another. This should not matter for simple messages, but for complicated messages with multiple types of state and interactions it can easily cause errors.
  • Default responseName is selection.

Button Messages

ButtonsView is included as part of the standard npm build and require no additional import commands

Most of the messages described up above are minimally interactive. Except for the Choice Message, the only action they provide is that if you click/select them, they try to open something or trigger an event. The Buttons Message allows you to take any other Message and add Actions to it.

Product Message wrapped in a Button Message. Button Message provides all buttons shown above.
const ButtonsModel = Layer.Core.Client.getMessageTypeModelClass('ButtonsModel');

// Send the address
const model = new ButtonsModel({
  buttons: [
    {"type": "action", "text": "Buy It", "event": "buy", "data": {"one_click_purchasing_enabled": true}},
    {"type": "action", "text": "Like It", "event": "like"},
    {"type": "action", "text": "Discover It", "event": "open-url", "data": {"url": "https://layer.com"}},
  ],
  contentModel: new ProductModel({
    name: 'Dead Flat Beer',
    brand: 'Pail Ail Brewery',
    description: 'It could be root beer. Drinking this is a fast track to being one with the roots.',
    price: 0.90,
    quantity: 120,
    url: "http://www.rootbeer.com/products/aw-original-root-beer",
    imageUrls: [ "http://www.rootbeer.com/images/catalog/aw-on@2x.png" ],
    customData: {
      product_id: "abcdef"
    }
  })
});
model.send({ conversation });

// Clicking a button whose event is "buy" will trigger a "buy" event
document.addEventListener('buy', function(evt) {
  const buttonModel = evt.detail.model;
  const productModel = buttonModel.contentModel;
  const productId = productModel.customData.product_id;

  // the button's data property will be copied into evt.detail.data
  const oneClickEnabled = evt.detail.data['one_click_purchasing_enabled'];

  myCustomPurchasingFunc(productId, oneClickEnabled);
});

// Clicking a button whose event is "like" will trigger a "like" event
document.addEventListener('like', function(evt) {
  const product = evt.data.model.contentModel;
  myCustomLikeFunc(productModel.customData.product_id);
});

For a button with an event of open-url, one of the following will occur:

  • Your button’s data.url will be opened if present
  • Your Button Model’s contentModel.url will be opened if present
  • No action will occur

Typically, your event will either be open-url (or other Layer provided event) or a custom action that will be triggered on the DOM for your event handler to listen for.

The Buttons Model supports the following properties:

Name Required Type Description
contentModel No MessageTypeModel A Message Type Model for a a sub-message-model that will have buttons attached to it; however, buttons can be sent without any content.
buttons Yes Object[] Array of objects describing the buttons to add
buttons.type Yes String Currently “action” and “choice” are the only supported values
buttons.text Yes String Label for the Button; used for type=action buttons only
buttons.event No String Event to trigger when the button is clicked
buttons.data No Object Custom parameters to provide with your buttons.event
buttons.choices No Object[] Choices that the user can chose from using the ChoiceModel

Button Message with Choice Buttons

The Choice Buttons within the Button Message enables an app to persist a user’s selection and share it with all participants. These selections are designed for a single user only to perform the selection, and for that selection to become a permanent part of that Message. Only a single state name can be set by a given Button Set; you can not set multiple types of state in a single Button Set.

Button Message that does not wrap another message, and which provides 3 choices
const ButtonsModel = Layer.Core.Client.getMessageTypeModelClass('ButtonsModel');

// Send a Product with two ChoiceButton sets
const model = new ButtonsModel({
  contentModel: new ProductModel({
    name: 'Dead Flat Beer'
  },
  buttons: [
    // A Choice Button Set with 3 options
    {
      "type": "choice",
      "choices": [
        {"text": "Love It", "id": "love"},
        {"text": "Like It", "id": "like"},
        {"text": "Hate It", "id": "hate"}
      ],
      "data": {
        "responseName": "likelevel",
        "allowReselect": true,
        "preselectedChoice": "like", // Starts of with "like" selected.
        "enabledFor": "layer:///identities/a"
      }
    },

    // A Choice Button set with  toggling selection enabled via allowDeselect
    {
      "type": "choice",
      "choices": [
        {"text": "Favorite", "id": "true"}
      ],
      "data": {
        "responseName": "isfavorite",
        "allowReselect": true,
        "allowDeselect": true,
        "enabledFor": "layer:///identities/a"
      }
    }
  ]
});
model.send({ conversation });

model.on('change', function() {
  console.log('likelevel is now ' + model.choices.likelevel.selectedAnswer || 'unset');
  console.log('isfavorite is now ' + model.choices.isfavorite.selectedAnswer || 'unset');
});

The Choice Buttons use the Choice Model; you can access the individual choice models using model.choices[responseName], and may therefore access the selectedAnswer of any Choice Model using model.choices[responseName].selectedAnswer; this gives you the id of the selected Choice.

The following properties can be used with the ButtonModel constructor to setup your Choice Buttons:

Name Required Type Description
choices.text Yes String Button text; emoji characters are accepted as well such as “\uD83D\uDC4D”
choices.tooltip No String Button tooltip
choices.id Yes String Value that will be recorded if this Choice is selected
data.responseName No String Unique name associated with this response; needed if more than one Choice or Response may be associated with this message
data.allowReselect No Boolean Allow users to change their selection once made
data.allowDeselect No Boolean Allow user to deselect a previous selection requires allowReselect
data.allowMultiselect N Boolean Default is false; if false then only a single selection is allowed at a time. If this is true, users may select multiple choices, and allowReselect and allowDeselect are implicitly set to true.
data.preselectedChoice No String Specify the id of the default selected choice. User selected choices will take precedence over this value.
data.enabledFor N String Identity IDs of those allowed to select these buttons (all are enabled if this is empty or omitted)
data.customResponseData N Object Name value pairs to add to the Message Response data. Allows you to add some context to the Response Messages as well as to the data added to the Choice Model once the Message has been updated

Note

  • Failure to set a data.responseName to a unique name could cause different Message Response interactions to overwrite one another.
  • Default responseName is selection.
  • A Button Message may contain both Action buttons and Choice Buttons

Product Messages

ProductMessageView is not part of the default npm build; include it using:

import '@layerhq/web-xdk';
import '@layerhq/web-xdk/messages/product/layer-product-message-view';

The Product Message represents a product for sale. Unlike many other informational Messaging Experiences, this does not depend upon the Standard Message Container which provides a standard layout of information. There is a lot of custom information about a product that needs to be shown. Typically this Message would be used to allow a user to purchase, bookmark or perhaps return a Product. Such actions are attached using the Buttons Message.

Standard Product Message
ProductModel = Layer.Core.Client.getMessageTypeModelClass('ProductModel');
model = new ProductModel({
  name: 'Dead Flat Beer',
  brand: 'Pail Ail Brewery',
  description: 'It could be root beer. Drinking this is a fast track to being one with the roots.',
  price: 0.90,
  quantity: 120,
  url: "http://www.rootbeer.com/products/aw-original-root-beer",
  imageUrls: [ "http://www.rootbeer.com/images/catalog/aw-on@2x.png" ],
  customData: {
    product_id: "abcdef"
  },
  options: [
    new ChoiceModel({
      label: 'Ailment Level',
      type: 'label',
      allowReselect: true,
      preselectedChoice: 'medium',
      enabledFor: 'layer:///identities/frodo-the-dodo',
      choices: [
        {text:  "Burpy", id: "mild"},
        {text:  "Nauseous", id: "medium"},
        {text:  "Hopitalized", id: "high"},
      ]
    })
  ]
});
model.send({ conversation });

The Product Model supports the following properties:

Name Required Type Description
name N String Name of your product
brand N String Brand of your product
description N String One or more lines of product description
price N Number Cost per unit of this product
quantity N Number Number of units of the product; defaults to 1
currency N String ISO 4217 currency codes; default is USD
url N String URL to open if user clicks for more information
imageUrls N String[] Array of URLs to a product pictures. Currently only the first is rendered.
options N ChoiceModels[] Array of Choice models representing options for this product (clothing size, CPU speed, etc…). If type='label' then this Product Message simply shows the user what options are selected, but does not allow changing them. (Only label supported at this time)

Note

Currently, only a ChoiceModel with type='label' is supported. Future versions may allow type values that allow users to select a choice and update the state of the Message.

Receipt Messages

ReceiptMessageView is not part of the default npm build; include it using:

import '@layerhq/web-xdk';
import '@layerhq/web-xdk/messages/receipt/layer-receipt-message-view';

The Receipt Message is a Message that renders:

  • a list of Products
  • a shipping address
  • a total price
  • payment info
Standard Receipt Message
ReceiptModel = Layer.Core.Client.getMessageTypeModelClass('ReceiptModel');
ProductModel = Layer.Core.Client.getMessageTypeModelClass('ProductModel');
LocationModel = Layer.Core.Client.getMessageTypeModelClass('LocationModel');
ChoiceModel = Layer.Core.Client.getMessageTypeModelClass('ChoiceModel');

model = new ReceiptModel({
  currency: 'USD',
  order: {
    number: 'FRODO-DODO-ONE'
  },
  paymentMethod: "VISA ****1234",
  summary: {
    subtitle: 'Your Purchase is Complete',
    shippingCost: 50.01,
    productCost: 1900.25,
    totalTax: 0,
    totalCost: 1950.26
  },
  shippingAddressModel: new LocationModel({
    city: 'San Francisco',
    name: 'Layer Inc',
    postalCode: '94107',
    state: 'CA',
    street1: '655 4th st',
    description: 'Description should not show'
  }),
  items: [
    new ProductModel({
      price: 525,
      quantity: 1,
      currency: "USD",
      brand: "Prison Garb Inc",
      name: "Formal Strait Jacket",
      description: "The right choice for special occasions with your crazed inlaws.  This will make you feel like you at last belong.",
      imageUrls: [ "http://l7.alamy.com/zooms/e33f19042cbe4ec1807bba7f3720ba62/executive-in-a-strait-jacket-aakafp.jpg" ],
      options: [
        new ChoiceModel({
          label: 'Color',
          type: 'label',
          preselectedChoice: 'white',
          choices: [
            {text:  "White", id: "white"},
            {text:  "Black", id: "black"},
            {text:  "Gold", id: "gold"},
          ]
        })
      ]
    }),
    new ProductModel({
      price: 500,
      quantity: 1,
      currency: "USD",
      brand: "Prison Garb Inc",
      name: "Formal Strait Jacket",
      description: "The right choice for special occasions with your crazed inlaws.  This will make you feel like you at last belong.",
      imageUrls: [ "http://l7.alamy.com/zooms/e33f19042cbe4ec1807bba7f3720ba62/executive-in-a-strait-jacket-aakafp.jpg" ],
      options: [
        new ChoiceModel({
          label: 'Color',
          type: 'label',
          preselectedChoice: 'black',
          choices: [
            {text:  "White", id: "white"},
            {text:  "Black", id: "black"},
            {text:  "Gold", id: "gold"},
          ]
        })
      ]
    }),
    new ProductModel({
      price: 300,
      quantity: 3,
      currency: "USD",
      brand: "Prison Garb Inc",
      name: "Formal Strait Jacket",
      description: "The right choice for special occasions with your crazed inlaws.  This will make you feel like you at last belong.",
      imageUrls: [ "http://l7.alamy.com/zooms/e33f19042cbe4ec1807bba7f3720ba62/executive-in-a-strait-jacket-aakafp.jpg" ],
      options: [
        new ChoiceModel({
          label: 'Color',
          type: 'label',
          preselectedChoice: 'gold',
          enabledFor: "layer:///identities/frodo-the-dodo"
          choices: [
            {text:  "White", id: "white"},
            {text:  "Black", id: "black"},
            {text:  "Gold", id: "gold"},
          ]
        })
      ]
    })
  ]
})
model.send({ conversation });

The Receipt Model supports the following properties:

Name Required Type Description
billingAddressModel N Location Model A billing address; not rendered in the basic Receipt Message View
shippingAddressModel Y Location Model The shipping address; rendered in the basic Receipt Message View
currency N String ISO 4217 currency codes; default is USD
paymentMethod Y String Arbirary string, often of the form “Visa ****-XXXX”
order N Object Object describing the Order
order.number N String Order Number / Order ID
summary Y Object Summary of the order
summary.productCost N Number The sum of all costs for all products (not currently rendered)
summary.totalTax N Number Total taxes for the purchase (not currently rendered)
summary.shippingCost N Number Shipping charges for the purchase (not currently rendered)
summary.totalCost N Number Sum of all costs; this is always rendered
summary.subtitle N String Summary of order (Not currently rendered)
items Y ProductModel[] Array of items that are to be displayed as part of the purchase
Modifying Components Concepts