UI Components

The XDK ship with a number of UI Components to simplify application development. This section explores the Core Component library in more details.

Name Description
The Conversation View Presents a single Conversation, with a Message List of messages for that Conversation, as well as a Compose Bar for sending messages in that Conversation. Required component of any application
The Conversation List A list for viewing Conversations, used by your application to let users see a Conversation List and select one to open.
The Conversation Item An item of the Conversation List; generated by the Conversation List
The Message List A list for viewing Messages, scrolling through them and marking them as read
The Message Item An Item representing a message within the Message List. Generated by the Message List. Used by the Conversation View
The Identity List A list for viewing Identities. Used by applications to add or remove users from a Conversation
The Identity Item An Item representing an Identity/user within the Identity List. Generated by the Identity List.
The Compose Bar A panel for putting a text editor and buttons to allow users to enter text, and perform other message-sending-related activities such as selecting files for upload.
The Avatar Component Picture representing a user. Generated by Identity Item, Conversation Item and Message Item.
The Presence Component Conveys a user’s status/availability. Generated by Avatar.
The Typing Indicator A Component for indicating that a user is typing into a Conversation View

Of the above widgets, the ones most commonly used directly by developers are described below, and the rest are described in the API Reference.

The Conversation View

The Conversation View is the primary UI Component for adding conversations to an application. It serves the following goals:

  1. Displays the messages that have been sent and received
  2. Provides a Compose Bar for the user to send their own messages
  3. Manages sending read receipts as the user scrolls through messages
  4. Manages sending and displaying typing indicators.

In other words, the Conversation View manages the core tasks of having messaging within your application.

iOS Conversation View
Android Conversation View

The Conversation View is part of the default npm build, and can be used without additional import commands.

The Layer Conversation View includes the following subcomponents:

How to work with this panel depends upon whether you are creating a Query and passing it to the panel or letting the panel create its own query.

The Panel Creates its own Query

If you let the panel create its own query, then typically the only property/event you need for a basic application is the conversation-id attribute or conversationId property. This property determines:

  • What Messages the query will request, which in turn controls what messages are rendered in the Messages List
  • What Conversation a Message will be sent to when the user types in a Message into the Composer
  • What Conversation typing indicators will be sent/received for.

Lets take a simple scenario where your application has determined a Conversation ID that should be opened so the user can read and write messages

Note

Conversation IDs will match the Layer.Core.Conversation IDs: layer:///conversations/UUID.

Using a templating engine, this might look like:

<layer-conversation-view conversation-id={mySelectedConversationId}></layer-conversation-view>

Or using Javascript, this might look like:

var conversationView = document.createElement('layer-conversation-view');
function selectConversation(mySelectedConversationId) {
   conversationView.conversationId = mySelectedConversationId;
}

Panel is Provided a Query

If you are managing the Panel’s Query rather than letting it create its own query, then you are responsible for

  • updating the query’s predicate to get new Messages
  • updating the conversationId property or conversation-id attribute so that the Composer and Typing Indicators can still work.

Using a templating engine, this might look like:

<script>
var selectedConversationId;
var myQuery = client.createQuery({
  model: Layer.Core.Query.Message
});
function selectConversation(newSelectedConversationId) {
  myQuery.update({
    predicate: 'conversation.id = "' + mySelectedConversationId + '"'
  });
  selectedConversationId = newSelectedConversationId;
}
</script>
<layer-conversation-view query-id={myQuery.id} conversation-id={selectedConversationId}>
</layer-conversation-view>

Or using Javascript, this might look like:

var conversationView = document.createElement('layer-conversation-view');
var myQuery = client.createQuery({
  model: Layer.Core.Query.Message
});
conversationView.query = myQuery;
function selectConversation(mySelectedConversationId) {
  conversationView.conversationId = mySelectedConversationId;
  myQuery.update({
    predicate: 'conversation.id = "' + mySelectedConversationId + '"'
  });
}

Key Properties

There are various properties worth being aware of; more are documented in the API Reference, but of these, only conversation or conversationId is required to use this widget.

Name Type Description
conversationId String Set the Conversation that this panel interacts with.
conversation Conversation Set the Conversation that this panel interacts with.
queryId String Sets the query whose messages will be rendered
query MessagesQuery Sets the query whose messages will be rendered
composeText String Sets the text showing int the Compose Bar/get text from the Compose Bar
disable Boolean Disable this panel to prevent it from sending read receipts; use this when the panel is hidden but may not know its hidden
queryFilter Function Provide a Function that takes a single argument Message and returns true for Messages that should be added to the Query results, and false for Messages that should be filtered out
layer-conversation-panel-change Event This event is triggered any time the Conversation View loads a new Conversation (its conversation property has been changed)

Customization Conversation View with replaceableContent

The ConversationViewreplaceableContent property enables developers to take control over what nodes are inserted into what parts of the UI. Internally, a replaceableContent property finds the <layer-replaceable-content name="composerButtonPanelLeft"/> DOM node with a specified name, and copies your custom DOM nodes into that Replaceable Content node. The Conversation View supports replacing a number of different nodes:

Replaceable nodes for the Conversations View’s Compose Bar and Message List:

Name Description
composerButtonPanelLeft Insert a node containing buttons that go to the left of the Compose Bar
composerButtonPanelRight Insert a node containing buttons that go to the right of the Compose Bar
emptyNode DOM nodes to show the user when there are no Messages in the Conversation
endOfResultsNode DOM nodes to show the user when further paging will not load any more messages
loadIndicator DOM nodes to show the user that messages are loading

Replaceable nodes for the Conversation View’s messages sent:

Messages sent are any messages that are rendered as having been sent by the current user.

Name Description
messageSentHeader For each Message in your Message List that you sent, you can insert custom content above that Message. Replaces default header which may show sender name.
messageSentFooter For each Message in your Message List that you sent, you can insert custom content below that Message. Replaces default footer which may show message timestamp and status.
messageSentRightSide Replace the nodes to the right of each Message in your Message List you sent with your own nodes. Replaces default nodes such as the Avatar or Menu.
messageSentLeftSide Replace the nodes to the left of each Message in your Message List you sent with your own nodes. Typically left empty.

Replaceable nodes for the Conversation View’s messages received:

Messages received are any messages that are rendered as having been received by the current user.

Name Description
messageReceivedFooter For each Message in your Message List that you receive, you can insert custom content below that Message. Replaces default footer which may show message timestamp and status.
messageReceivedHeader For each Message in your Message List that you receive, you can insert custom content above that Message. Replaces default header which may show sender name.
messageReceivedLeftSide Replace the nodes to the left of each Message in your Message List you receive with your own nodes. Replaces default nodes such as the Avatar or Menu.
messageReceivedRightSide Replace the nodes to the right of each Message in your Message List you receive with your own nodes. Replaces default nodes such as the Avatar or Menu.

Replaceable nodes for the Conversation View’s Status Messages:

Status Messages are any messages the system recognizes as being status messages that render centered and without avatar/sender information.

Name Description
messageStatusLeftSide Replace the nodes to the left of each Status Message in your Message List with your own nodes.
messageStatusRightSide Replace the nodes to the right of each Status Message in your Message List with your own nodes.
messageStatusHeader For each Status Message in your Message List, you can insert custom content above that Message. Replaces default header which may show sender name.
messageStatusFooter For each Status Message in your Message List, you can insert custom content below that Message. Replaces default footer which may show message timestamp and status.

Setting the Replaceable Nodes

Each attribute of the ConversationViewreplaceableContent should be either:

  • A template string representing the HTML that is to be inserted into the <layer-replaceable-content name="messageSentFooter" />
  • A function that takes as input the widget that contains this Replaceable Content within its template, and returns DOM nodes to be inserted into the <layer-replaceable-content name="messageSentHeader" />
var conversationView = document.createElement('layer-conversation-view');
conversationView.replaceableContent = {
  emptyNode: 'Nobody has sent you any messages. Perhaps we should talk this.',
  messageSentRightSide: (widget) => {
    const menuButton = document.createElement('layer-menu-button');
    menuButton.getMenuOptions = function(messageItemWidget) {
      return [{
        text: 'delete',
        method: function() {
          messageItemWidget.item.delete(Layer.Constants.DELETION_MODE.ALL);
        },
      }]
    };
    return menuButton;
  },
  messageReceivedLeftSide: Layer.UI.UIUtils.ReplaceableSnippets.avatarNode + Layer.UI.UIUtils.ReplaceableSnippets.menuNode + '<input type="checkbox" />'
};

To help you with common Replaceable Content tasks, UIUtils.ReplaceableSnippets provides a library of snippets (strings) to help you customize your replaceable content areas:

Name Use With Description
avatarNode Message List Item Adds an avatar next to each message in the message list (CSS may be used to hide all but the first or last in each series of messages)
menuNode Message List Item Adds a menu button next to each message in the message list; use ConversationViewgetMenuOptions to customize the menu items
dateNode Message List Item Adds a widget for rendering the Message’s Sent At date
statusNode Message List Item Adds a widget for rendering the Message’s Read/Delivered/Sent/Pending status
senderNode Message List Item Adds a widget for rendering the Message sender’s name

Example using snippets as-is:

conversationView.replaceableContent = {
  messageReceivedLeftSide: Layer.UI.UIUtils.ReplaceableSnippets.avatarNode
};

Example concatenating snippet strings:

conversationView.replaceableContent = {
  messageReceivedLeftSide: Layer.UI.UIUtils.ReplaceableSnippets.avatarNode +
    Layer.UI.UIUtils.ReplaceableSnippets.menuNode
};

Example using snippet strings in functions:

conversationView.replaceableContent = {
  messageReceivedLeftSide: function(widget) {
   var div = document.createElement('div');
   div.innerHTML = Layer.UI.UIUtils.ReplaceableSnippets.avatarNode;
   return div;
  }
};

The Conversation List

The Conversation List serves the following goals:

  1. Display a list of Conversations to the user
  2. Allow the user to select a Conversation to open in the Conversation View
  3. Find Conversations with unread messages
  4. See recent state of each Conversation
Empty Conversation List
Conversation List with Large Sized Items

Note

The Conversation List is not part of the default npm build and must be imported

import "@layerhq/web-xdk";
import "@layerhq/web-xdk/ui/components/layer-conversation-list";

The following properties can be used to gain more control over the Conversation List, but generaly, the only thing most applications require is the selection event.

Name Type Description
selectedId String Get/Set the selected Conversation or Channel by ID
layer-conversation-selected Event Event is triggered whenever the user changes the selected Conversation or Channel.
onConversationSelected Function Called whenever the user changes the selected Conversation or Channel
queryId String Sets the query whose results will be rendered
query ConversationsQuery Sets the query whose results will be rendered
queryFilter Function Provide a Function that takes a single argument Conversation and returns true for Conversations that should be added to the Query results, and false for Conversations that should be filtered out
pageSize Number Number of items to load per page of Query results
getMenuOptions Function For Conversation Lists that use a Menu Button, this function lets you customize what menu items will show when the Menu Button is selected.
size Number tiny, small, medium or large and set different layouts and sizing of the Conversation Items within the Conversation List

Events can typically be accessed as Events or Properties to get the same end result:

  • Events are subscribed to using domNode.addEventListener(eventName, handler).
  • Properties that are functions are used as domNode.propertyName = handler

Examples

Given the following pseudo-code template:

<layer-conversation-list selected-id={selectedConversation.id}>
  </layer-conversation-list>
<layer-conversation-view conversation-id={selectedConversation.id}>
  </layer-conversation-view>
<script>
var selectedConversation = initialConversation;
document.body.addEventListener('layer-conversation-selected', function(evt) {
  selectedConversation = evt.detail.item;
});
</script>

Alternatively in pure Javascript:

var conversationView = document.createElement('layer-conversation-view');
var conversationsList = document.createElement('layer-conversation-list');
var selectedConversation = initialConversation;

conversationsList.selectedId = selectedConversation.id;
conversationView.conversationId = selectedConversation.id;

conversationList.addEventListener('layer-conversation-selected', function(evt) {
  selectedConversation = evt.detail.item;
  conversationView.conversationId = selectedConversation.id;
});

For more information on the widget and its properties, see the API Reference.

Note that at this time, this widget can have a Query that returns Conversations or Channels. There is not yet a way to have it display both at the same time.

Customization ConversationList with replaceableContent

The replaceableContent property enables developers to take control over what nodes are inserted into what parts of the UI. Internally, a replaceableContent property finds the <layer-replaceable-content name="loadIndicator"/> DOM node with a specified name, and copies your custom DOM nodes into that Replaceable Content node. The Conversation List supports replacing a number of different nodes:

Name Description
emptyNode DOM nodes to show the user when there are no Conversations
endOfResultsNode DOM nodes to show the user when further paging will not load any more Conversations
loadIndicator DOM nodes to show the user that Conversations are loading
conversationRowLeftSide Replace the nodes to the left of each Conversation in your Conversation List with your own nodes. Replaces default Avatar node.
conversationRowRightSide Replace the nodes to the right of each Conversation in your Conversation List with your own nodes. Replaces default Menu node.

Each attribute of replaceableContent should be either:

  • A template string representing the HTML that is to be inserted into the <layer-replaceable-content name="loadIndicator" />
  • A function that takes as input the widget that contains this Replaceable Content within its template, and returns DOM nodes to be inserted into the <layer-replaceable-content name="conversationRowRightSide" />
var conversationList = document.createElement('layer-conversation-list');
conversationList.replaceableContent = {
  emptyNode: 'Your not talking to anybody? How about you create a Conversation?',
  conversationRowRightSide: (widget) => {
    const conversation = widget.item;
    const div = document.createElement('div');
    const menuButton = document.createElement('layer-menu-button');
    menuButton.getMenuOptions = function(conversationItemWidget) {
      return [{
        text: 'delete',
        method: function() {
          conversationItemWidget.item.delete(Layer.Constants.DELETION_MODE.ALL);
        },
      }]
    };
    div.appendChild(menuButton);
    return div;
  }
};

The Identity List

The Identity List serves the following goals:

  1. Display a list of Identities/users
  2. Allow the user to select Identities to have a Conversation with
  3. View status of users

Note that an Identity List is typically restricted to listing users who:

  • People that your user has had Conversations with in the past
  • People that you have explicitly issued a client.followIdentity(userIdentity) operation on from the client
  • People that your server has explicitly issued a follows request on. These requests are sent to Layer’s Server API.
Identity List with size = tiny
Identity List with size = medium

Note

The Identity List is not part of the default npm build and must be imported

import "@layerhq/web-xdk";
import "@layerhq/web-xdk/ui/components/layer-identity-list";

The following properties can be used to gain more control over the Identity List, but generaly, the only thing most applications require is identifying which users are selected.

Name Type Description
selectedIdentities property Use this property to get and set which users are selected
layer-identity-selected Event Event is triggered whenever a user is selected
layer-identity-deselected Event Event is triggered whenever a user is deselected.
onIdentitySelected Function Called whenever a user is selected
onIdentityDeselected Function Called whenever a user is deselected.
queryId String Sets the query whose results will be rendered
query IdentitiesQuery Sets the query whose results will be rendered
queryFilter Function Provide a Function that takes a single argument Identity and returns true for Identities that should be added to the Query results, and false for Identities that should be filtered out
pageSize Number Number of items to load per page of Query results
size Number tiny, small, medium or large and set different layouts and sizing of the Identity Items within the Identity List

Events can typically be accessed as Events or Properties to get the same end result:

  • Events are subscribed to using domNode.addEventListener(eventName, handler).
  • Properties that are functions are used as domNode.propertyName = handler

Examples

The following can be used to edit a Conversation’s participants:

<layer-identity-list></layer-identity-list>
<button>OK</button>
<script>

// Checkboxes for all of the Identities in the the Conversation Participant list will be selected by this assignment operation
var identitiesList = document.querySelector('layer-identity-list');
identitiesList.selectedIdentities = currentConversation.participants;

// When the OK button is clicked, replace all participants in the conversation with a new set of participants.
var button = document.querySelector('button');
button.addEventListener('click', function() {
  currentConversation.replaceParticipants(identitiesList.selectedIdentities);
});
</script>

The following can create a new Conversation:

var identities = document.createElement('layer-identity-list');
var button = document.createElement('button');
button.onclick = function() {

  // Whatever Identities the user has selected prior to this function running will now
  // become the participants of the new conversation
  client.createConversation({
    participants: identities.selectedIdentities,
    distinct: false
  });
}

Note

Do not manipulate the selectedIdentities array via push, pop, splice, etc…, you must assign the property a new array for it to receive the updated selection: identityList.selectedIdentities = identityList.selectedIdentities.concat(additionalIdentities)

Customization IdentityList with replaceableContent

The replaceableContent enables developers to take control over what nodes are inserted in what parts of the UI. The Identity List supports replacing the following nodes:

Name Description
emptyNode DOM nodes to show the user when there are no Identities
endOfResultsNode DOM nodes to show the user when further paging will not load any more Identities
loadIndicator DOM nodes to show the user that Identities are loading
identityRowRightSide Add nodes to the right of each Identity Item in the List

Each attribute of replaceableContent should be either:

  • A template string representing the HTML that is to be inserted into the <layer-replaceable-content name="loadIndicator" />
  • A function that takes as input the widget that contains this Replaceable Content within its template, and returns DOM nodes to be inserted into the <layer-replaceable-content name="identityRowRightSide" />
var identityList = document.createElement('layer-identity-list');
identityList.replaceableContent = {
  emptyNode: 'You are not following anyone.  You must therefore be a leader?',
  identityRowRightSide: (widget) => {
    const identity = widget.item;
    const div = document.createElement('div');
    const menuButton = document.createElement('layer-menu-button');
    menuButton.getMenuOptions = function(identityItemWidget) {
      return [{
        text: 'Unfollow',
        method: function() {
          layerClient.unfollowIdentity(identityItemWidget.item.id);
        },
      }]
    };
    div.appendChild(menuButton);
    return div;
  }
};

The Avatar Component

The Avatar is a simple representation of a user where a compact but visual representation is needed. The Avatar is used within many of the XDK UI Components, but can also be used directly. For example, if creating a profile page for a user, you may use this Avatar Component as part of the profile. Having an Avatar Component that looks exactly the way the Avatar looks within the XDK UI Components will help create a consistent look and feel across your application.

The only information the Avatar provides beyond a photo, is the user’s online status via an embedded Presence Component.

The Avatar is part of the default npm build, and can be used without additional import commands.

The Avatar Component provides the following properties:

Name Type Description
item Identity Identity object for the user whose image is to be rendered
showPresence Boolean Set to false if the Avatar should not show presence information for this user
size Number small, medium, large or larger configures different standard Avatar sizes

Examples:

The following pseudocode/template shows how an avatar might be used:

<body>
  User Profile for {identity.displayName}
  <hr/>
  <layer-avatar item={identity}></layer-avatar>
  This user is an egg head.  And also hard boiled.  Somewhat shell shocked. In constant fear of being fried.
</body>

Or using Javascript:

var avatar = document.querySelector('layer-avatar');
avatar.item = client.user;
avatar.size = 'larger';

The Presence Component

The Presence Component shows the status of a specific user in a very compact format and renders simply a colored circle where color conveys status.

See Presence for details on importing this component into your project.

The Presence Widget is a simple widget that shows the status of a specific user. It provides the following properties:

Name Type Description
item Identity Identity object for the user whose status is to be rendered
layer-presence-click Event Event is triggered whenever the user clicks on the Presence widget
onPresenceClick Function Function is called whenever the user clicks on the Presence widget

The Presence Widget is built into the <layer-avatar /> widget, where it will show the status of each user as they are rendered.

<body>
  <layer-presence></layer-presence>
  <script>
  var presenceWidget = document.querySelector('layer-presence');
  presenceWidget.item = client.user;
  presenceWidget.onPresenceClick = function() {
    client.user.setStatus(Layer.Core.Client.Identity.STATUS.BUSY);
  }
  </script>
</body>
UI Concepts Misc Components