Messaging Features
Messaging Features
Every message in NexusRMS is stored as a CommunicationMessage record. Messages support rich text, @mentions, emoji reactions, read receipts, editing history, and multi-channel delivery tracking.
CommunicationMessage Model
The core fields on every message record:
| Field | Description |
|---|---|
| thread_id | Parent communication thread UUID |
| sender_id | Internal user UUID (null for external senders) |
| sender_name | Display name of the sender |
| sender_email | Email address of the sender |
| message_body | Message content in Markdown format |
| message_html | Rendered HTML version of the message |
| is_internal | Boolean — true for staff-only internal notes |
| is_external_reply | Boolean — true if this message arrived via email reply |
| sent_via | Delivery channel: web, email, api, or mobile |
@Mentions
Type @username in any message to mention a colleague. The system handles mentions as follows:
- The extractMentions() method parses the message body using the regex pattern /@(\w+)/ to find all @username references.
- Matched usernames are looked up in the User model and their UUIDs are stored in the mentions JSON array on the message.
- Call getMentionedUsers() to retrieve the full User collection for all mentioned users.
- Notifications are automatically sent to each mentioned user.
Use the mentionsUser(userId) query scope to find all messages that mention a specific user.
Emoji Reactions
The CommunicationReaction model supports 12 emoji reactions: thumbsup, heart, joy, tada, open_mouth, cry, fire, check, x, star, hundred, and rocket. Reaction features include:
- groupByEmoji() — groups all reactions on a message by emoji, returning each emoji with its count and the list of users who reacted.
- userHasReacted(messageId, userId, emoji) — checks whether a specific user has already reacted with a given emoji.
- Toggle behaviour — clicking a reaction you have already added removes it; clicking a new one adds it.
- Auto-populated fields — the emoji_name and reacted_by_name are automatically set during creation.
Read Receipts
The CommunicationReadReceipt model provides detailed read tracking:
- first_read_at — timestamp of the initial read event.
- last_read_at — timestamp of the most recent read event.
- read_count — total number of times the message was viewed.
- read_via — channel used: web, mobile, or email.
- user_agent and ip_address — captured for audit purposes.
Key methods:
- markAsReadBy(userId) — records a read event on the message's read_by JSON array.
- isReadBy(userId) — checks whether a specific user has read the message.
- getReadPercentage(messageId, totalParticipants) — returns the percentage of participants who have read the message.
- allRead(messageId, totalParticipants) — returns true when every participant has read the message.
Message Editing
Messages can be edited after sending. The editing process:
- The edit(newMessageBody, editedBy) method is called with the new Markdown content.
- The original message is preserved in original_message_body.
- The edited_at timestamp and edited_by user UUID are recorded.
- The message_html field is re-rendered from the updated Markdown.
- The isEdited() method returns true for any previously edited message.
Message Preview
The getPreview(length) method strips HTML tags and style blocks from message_html, then truncates to the specified character count (default 100). This is used in thread list cards to show the last message preview.
Auto-Tracking on Message Creation
When a new CommunicationMessage is created, the model's boot event listener automatically:
- Calls thread->updateActivity() to refresh last_activity_at and message_count.
- Increments the unread_count for all thread participants except the sender.
- For external senders (no sender_id), all internal participants receive the unread increment.
Typing Indicators and Query Scopes
Typing indicators can be toggled via the typing_indicators_enabled setting. When enabled, other participants see a real-time indicator while someone composes a message. Available query scopes: internal() for staff-only messages, external() for client-visible messages, edited() for messages with edit history, and mentionsUser(userId) for messages mentioning a specific user.
Was this article helpful?