Skip to content

Comments in Template

This feature is available with the Plugin for Business plan and higher.

If you're on the Plugin for Startup or Plugin Expansion plan, consider upgrading to access this feature. For more details, visit our pricing page or contact support.

Comments attach a conversation thread to any section or block in the template. Users can reply, mention teammates, react with a thumb up or down, and resolve a thread once it is handled. Every conversation is stored through your own API endpoints.

To enable the feature, set the following option inside Topol Options:

js
enableComments: true;

Identifying users and the template

Comments work without the options below, but attribution and unread tracking depend on them. Without a current user the editor sends an empty current_user_id, and it can no longer tell which comments are unread.

js
currentUser: {
  userId: "current user user_id",
  name: "current user name",
  profilePhotoUrl:
    "link to the current user's profile picture",
};
templateId: "id";

teamUsers lists the colleagues who can be mentioned in a thread.

js
teamUsers: [
  {
    userId: "1",
    name: "Jane Doe",
    profilePhotoUrl: "...",
  },
  {
    userId: "2",
    name: "John Doe",
    profilePhotoUrl: "...",
  },
];

All three properties are required on both currentUser and every entry in teamUsers, and userId has to be a string. The current user is filtered out of teamUsers automatically, so passing the whole team is fine; that person simply cannot mention themselves.

WARNING

Before implementing the endpoints, check how to work with API endpoints.

List conversations

  • URL: /{API.CONVERSATIONS}
  • Method: GET
  • Content-Type: application-json

Params:

keyvalue
keyauthorization api key
hostnamehostname
template_idid of the template
entity_identity_id corresponds to userId in Options
current_user_iduser id of current user
sort_by"date" or "unread"
show_resolvedboolean

The editor calls this endpoint on load, whenever sorting or the resolved-display preference changes, after a comment is marked read or unread, and on every TopolPlugin.refreshComments(key) call.

Response:

json
{
  "success": true,
  "data": [
    {
      "key": "conversation_key", //section or block uid on which the conversation is based
      "type": "block", // or section
      "resolved": false,
      "delete": false,
      "involved_users": [
        {
          "user_id": "user_id",
          "profile_photo_url": "link to the user's profile picture",
          "name": "user name"
        }
      ], // list of involved users, maximum of the first 7 users will be displayed
      "comments": [
        {
          "id": "comment_id",
          "created_by": {
            "user_id": "user_id",
            "profile_photo_url": "link to the user's profile picture",
            "name": "user name"
          },
          "created_at": "ISO 8601 time",
          "content": "text content of the comment",
          "read_by": ["user_id of the user who saw the comment"],
          "reactions": [
            {
              "created_by": {
                "user_id": "user_id",
                "profile_photo_url": "link to the user's profile picture",
                "name": "user name"
              },
              "type": "thumb-up" // or thumb-down
            }
          ]
        }
      ]
    }
  ]
}

A few details are worth knowing before implementing this response:

  • resolved accepts true, false, 0 or 1, which suits a MySQL-backed store directly. Both resolved and delete may be omitted.
  • reactions may be omitted. Only thumb-up and thumb-down are rendered; any other value is accepted and then ignored.
  • Every object under created_by needs all three of user_id, name and profile_photo_url. The list is validated as a whole, so one comment with an incomplete author blanks the entire panel.
  • Unread state is derived from the last comment of each thread. read_by on earlier comments is never inspected.

Detail conversation

  • URL: /{API.CONVERSATIONS}/{conversation_key}
  • Method: GET
  • Content-Type: application-json
  • Params: key, hostname, template_id, entity_id, current_user_id

The response is the same conversation object as above, returned on its own rather than inside an array. The involved_users list is not rendered in this view.

This endpoint is called when a conversation is opened, when the user selects a different block or section, right after a comment is posted, and when TopolPlugin.refreshComments("conversation_key") is called with the key of the open conversation.

Unread messages should be marked as read.

Resolve or unresolve conversation

  • URL: /{API.CONVERSATIONS}/{conversation_key}
  • Method: PATCH

This endpoint is called when you click to resolve button.

Request:

json
{
  "template_id": "id of the template",
  "entity_id": "entity user_id",
  "current_user_id": "current user user_id",
  "hostname": "origin",
  "key": "api key",
  "resolved": true // or false depending on whether we want resolved or unresolved
}

Expected Response: STATUS 200

Delete conversation

  • URL: /{API.CONVERSATIONS}/{conversation_key}
  • Method: DELETE
  • Params: key, hostname, template_id, entity_id, current_user_id

This endpoint is called when user deletes a conversation.

Consider whether to delete the conversation or just soft delete.

Expected Response: STATUS 200

Add comment

  • URL: /{API.CONVERSATIONS}/{conversation_key}
  • Method: POST

This endpoint is called when new comment is entered.

Request:

json
{
  "template_id": "id of the template",
  "entity_id": "entity user_id",
  "current_user_id": "current user user_id",
  "hostname": "origin",
  "key": "api key",
  "type": "block", // or section, is for setting up a conversation
  "content": "text content of comment",
  "mentions": ["user_id of the user who is mentioned in the comment"]
}

type carries block or section when the comment opens a new conversation, and an empty string when replying to an existing one, so the endpoint has to tolerate all three. The values in mentions are the userId strings from teamUsers.

Expected Response: STATUS 200

Edit, react, read or unread the comment

  • URL: /{API.COMMENTS}/{comment_id}
  • Method: POST

This endpoint is called when edit specific comment, react (add thumbs up or down) to specific comment or click to mark as read/unread conversation (will call for last comment of conversation).

Request:

json
{
  "template_id": "id of the template",
  "entity_id": "entity user_id",
  "current_user_id": "current user user_id",
  "hostname": "origin",
  "key": "api key",
  // if edit the comment
  "content": "new text content of comment",
  "mentions": ["user_id of the user who is mentioned in the comment"],
  // if react to the comment
  "react": "thumb-up", // or thumb-down
  // if click on mark as read/unread on the parent conversation
  "action": "mark_as_read" // or mark_as_unread
}

Expected Response: STATUS 200

Delete user's comment

  • URL: /{API.COMMENTS}/{comment_id}
  • Method: DELETE
  • Params: key, hostname, template_id, entity_id, current_user_id

This endpoint is called when user deletes a comment.

Expected Response: STATUS 200

refreshComments

Comments do not poll, so after your application adds or deletes a comment the editor needs to be told to reload.

js
// reload the list, and the open thread if this key matches it
TopolPlugin.refreshComments("conversation_key");

// reload the conversation list only
TopolPlugin.refreshComments("");

The key is required. The conversation list reloads on every call whatever key is passed, so an empty string is the way to refresh only the list. A key matching the currently open conversation additionally reloads that thread's detail view, which makes the affected key the right value to pass after a specific thread changed.

toggleComments

Opens and closes the comments panel from your own UI.

js
TopolPlugin.toggleComments();