Comments
In this article, you’ll learn all about the optional comments feature for Mattrbld real-time previews. It allows you and your collaborators to add comments to content in the preview to facilitate review processes before content gets published.
Enabling the Feature
To enable this feature, your preview page needs to signal that it supports it by passing commments
in the supports
-array of the initial handshake:
// in a postMessage handler
if (data && data.handshake) {
source.postMessage({
handshake: data.handshake,
supports: ['comments'],
});
}
// you'll also need to cache the source to easily send more messages, so cachedSource is defined outside the handlerFunction
cachedSource = source;
With this handshake, Mattrbld will show a “Toggle Comments” button in the preview toolbar, which can be used to show existing comments and add new ones. For this to work, you’ll have to implement a few more functions in your preview that will handle the additional data Mattrbld will send.
This data will have the following format:
{
feature: 'comments',
type: <Type of Event>,
payload: <Event Payload>,
}
Implementation Details
Your preview page is responsible for adding and deleting comment markers, as well as drawing them onto the page. Mattrbld handles the commenting and storage logic itself.
Toggling Comment Mode
Whenever the “Toggle Comments” button is clicked in the Mattrbld UI, Mattrbld will send a modechange
event to the preview page:
{
feature: 'comments',
type: 'modechange',
payload: {
active: true, // or false when deactivating
canComment: true, // or false when not allowed
},
}
The payload contains two properties:
active
which signals whether the comment mode was activated or deactivatedcanComment
which contains information on whether the current user is allowed to comment the content item being edited
You should use this event to register or remove event listeners on your preview page that message Mattrbld where the page was clicked:
// in a postMessage handler
if (data && data.feature === 'comments') {
switch (data.type) {
case: 'modechange':
const { active, canComment } = data.payload;
if (active) {
window.addEventListener(
'click',
// a function that handles page clicks
handlePageClick,
false
);
// you can use canComment here to show
// that the comment mode is enabled for
// your preview page, e.g. by changing the
// cursor
} else {
window.removeEventListener(
'click',
handlePageClick,
false
);
// a function that removes all comment
// markers on the page
removeCommentMarkers();
// use canComment to determine whether you
// need to undo anything else here
}
break;
default:
break;
}
}
Initial Data
On top of the modechange
event, Mattrbld also sends the comment data to the preview page whenever the comment mode was enabled:
{
feature: 'comments',
type: 'initialData',
payload: {
// an array of all the top-level comments for
// the content item
comments: <Array of Comment>,
},
}
The payload consists of an array of top-level comments, i.e. comments that aren’t replies to other comments. A comment-object has the following properties:
{
id: '89x27mzfls', // random id
author: <Name>, // name of the comment author
parent: null, // optional parent id if is reply
content: <CommentContent>, // HTML string
position: { x: <Int>, y: <Int> }, // or null
status: null, // status like "resolved"
created: <Date>,
updated: null, // <Date> if edited
}
This event should be used to add comment markers on the page wherever they were added. The position
property contains the position on the preview page where the marker was added by the comment author. Beware that this may be out of bounds depending on the size of the viewport, so you should make sure to handle such values appropriately.
// in the switch-case within the postMessage
// handler
case 'initialData':
const { comments } = data.payload;
comments.forEach((comment) => {
// a function that places comment markers on
// the preview page
createCommentMarker(comment)
});
break;
Handling Page Clicks
Whenever the user clicks on the preview page while comment mode is active, Mattrbld needs to know where that click happened and whether it was on the page itself or on a comment marker.
The way you implement this varies depending on how you have set up your preview page, but here’s a simple example you can use for reference:
function handlePageClick(e) {
const {
target, clientX, clientY, pageX, pageY,
} = e;
const isCommentMarker =
target.classList.contains('comment-marker');
const data = {
feature: 'comments',
payload: {
clientX,
clientY,
},
};
if (isCommentMarker) {
data.type = 'commentClick';
data.payload.id = target.dataset.id;
} else {
data.type = 'pageClick';
data.payload.pageX = pageX;
data.payload.pageY = pageY;
}
cachedSource.postMessage(
data, MATTRBLD_INSTANCE
);
}
This is an example of the bidirectional communication between Mattrbld and the preview page. Just like the preview page, Mattrbld listens to messages sent by the preview page and handles those messages accordingly.
As you can see in the example above, the messages the preview page sends are structured just the same as the messages Mattrbld sends to the preview page, containing the feature
, type
and payload
properties.
When a comment marker is clicked, Mattrbld needs to be notified about where that click happened on the client (i.e. the iFrame the preview is shown within or the browser window / tab that’s displaying it) and what the ID of the clicked marker is. The event type for this kind of event is commentClick
.
When any other place on the page is clicked, Mattrbld needs to be notified where that click happened on the client and the page itself, so a new comment can be added at that position. The event type for this kind of event is pageClick
.
Adding Comment Markers
When Mattrbld receives a pageClick
event and there is no comment-thread already opened, and the current user is allowed to add comments, it will show a UI for adding a new comment. Once the user saves their comment, Mattrbld will send the addCommentMarker
event to the preview:
{
feature: 'comments',
type: 'addCommentMarker',
payload: {
comment: <Comment>, // the newly added comment
},
}
The payload contains a copy of the newly added comment in the same format as all other comment-objects, outlined above.
This event should be used to add a single new comment marker to the page at the position specified in the comment
, just like the existing comment markers were added to the page when the initialData
event was handled:
// in the switch-case within the postMessage
// handler
case 'addCommentMarker':
const { comment } = data.payload;
// a function that places comment markers on
// the preview page
createCommentMarker(comment);
break;
Deleting Comment Markers
Similarly, when a comment is removed from within Mattrbld, it will send a deleteCommentMarker
event to the preview page:
{
feature: 'comments',
type: 'deleteCommentMarker',
payload: {
comment: <Comment>, // the deleted comment
},
}
Use this event to implement the removal of comment markers on the preview page:
// in the switch-case within the postMessage
// handler
case 'deleteCommentMarker':
const { comment } = data.payload;
// a function that removes comment markers from
// the preview page
deleteCommentMarker(comment);
break;
If the deletion is undone by the user in Mattrbld, it will send out another addCommentMarker
event to recreate the marker on the preview page, so you don’t have to implement any logic to handle those cases.
Moving Comment Markers
If you would like to make comment markers movable once they were added to the preview page, you can send the following event to Mattrbld in order to update their position after you have moved the marker on the page using your own logic:
{
feature: 'comments',
type: 'commentMoved',
payload: {
comment: <CommentID>,
position: { x: <Number>, y: <Number> },
},
}
The payload
consists of two properties:
comment
which is the ID of the comment that was movedposition
which is the new position of the comment, specified in page coordinates (i.e.event.pageX
). You always have to supply bothtop
andleft
as numbers lower than 999,999 or the movement will be rejected
Mattrbld will then save the new position for the comment accordingly if the current user is the author of the comment, since users can only modify their own comments.
If the comment cannot be moved due to missing permissions, or an error, Mattrbld will respond with a moveFailed
event, alongside showing a warning to the user:
{
feature: 'comments',
type: 'moveFailed',
payload: {
comment: <Comment> or null,
}
}
The payload will contain one property:
comment
which is the comment which could not be moved, including its original position, unless it wasn’t found in Mattrbld
You should use this event in order to reset the position of the comment marker on the preview page:
// in the switch-case within the postMessage
// handler
case 'moveFailed':
const { comment } = data.payload;
// could also be a single function that resets
// the position of the comment marker
deleteCommentMarker(comment);
if (comment && comment.position) {
addCommentMarker(comment);
}
break;
Users which do not have the canComment
permission will not be able to move comments. You should prevent them from attempting to move a comment in the first place by referencing the canComment
property sent in the modechange
event.
Updating Comment Markers
Sometimes, like when the status of a comment changes, Mattrbld will send an updateCommentMarker
event to the preview page:
{
feature: 'comments',
type: 'updateCommentMarker',
payload: {
comment: <Comment>, // the updated comment
},
}
You may use this event to update the appearance of the marker, for example when the status of a comment changes:
// in the switch-case within the postMessage
// handler
case 'updateCommentMarker':
const { comment } = data.payload;
// a function that updates comment markers on
// the preview page
updateCommentMarker(comment);
break;
And that’s it! With handling these events, you and your users can now leave comments on real-time previews and enhance your feedback workflows without having to leave Mattrbld.
Disabling Comments for Certain Collections
Similarly to how you can disable real-time previews for entire Collections, you can also disable comments for them from the Collection Settings. There, you can also assign which roles are allowed to comment at all.
Storage
Comments are stored in the .mattrbld
directory of your project, in a folder called comments
that mirrors your project’s directory structure. To minimise the potential for synchronisation conflicts, all comments by one user are stored in a separate file from comments by another user. This is also the reason why users can only modify their own comments.
When a user deletes their top-level comment, only their own replies (if there are any) will be removed immediately. Replies by other users will remain as “orphans” and cleaned up the next time that user views comments on that particular content item.
If the comments-file for a single user is empty, Mattrbld will delete it. It will be re-created once that user leaves another comment.
This concludes the article on real-time preview comments and how to implement them. The next one will teach you strategies to deal with some common issues that can come up while implementing the preview protocol.