AI Integration Quick Reference
AI Integration Quick Reference
That split drives everything below: a pin is a shared act and is permission-gated, while a save is private and is not.
Before starting, complete the Integration Guide.
Enabling the Feature
Pin Message and Save Message are gated by app settings that CometChat provisions server-side. The UI Kit resolves them once per session through the Chat SDK:
While a setting is off, the SDK reports the feature disabled and none of that feature’s surfaces render.
Development override
Because the flags are provisioned server-side, “flag off” is indistinguishable from “not built” during development.COMETCHAT_GLOBAL_CONFIG can force either surface on or off:
features.ux.conversations.pinned.enabled. It additionally requires the installed Chat SDK to expose pinConversation / unpinConversation, and is hidden with [hidePinConversation]="true".
Pinning and Saving a Message
Both actions live in the message context menu, gathered under an Organise ▸ flyout so two related actions cost one row in an already long menu. The flyout is omitted entirely when neither action applies.
The menu shows Pin or Unpin — never a toggling third state — because the presence of
pinnedAt is the boolean. The same holds for savedAt.
Eligibility
A message can carry a pin or a save unless it is deleted, still in flight, held by moderation, or anaction category message (“X joined the group” is not something anyone pins). Thread replies are eligible: the backend accepts them and returns the parent for context, so the option belongs inside the thread view too.
Permissions
Pinning is conversation-wide, and the server is the sole authority on who may do it. There is no client-side role gate: Pin is offered to every member, and a member without the permission is refused withERR_ACTION_NOT_ALLOWED — the optimistic flip then reverts with an explanatory toast.
Saving has no gate at all: it is private to the acting user and changes nothing anyone else can see.
Do not rely on the UI to withhold Pin or Unpin from a participant — it does not.
getScope() is a membership property and is frequently undefined on a Group derived from a conversation, so a client-side allow-list would hide Pin from admins whose scope simply has not loaded yet. The one action withheld on the client is Unpin on a system pin, which the server refuses for every member.Confirmation
Pinning and saving run immediately. Unpinning and unsaving ask first — unpinning acts for everyone in the chat, and an unsave can drop the only pointer the user had to a message buried far up the history.Limits
Each feature carries a cap, configured per app and read from app settings:
Every accessor above resolves asynchronously —
await it or use .then().
When a user reaches a cap, the UI Kit shows a toast naming the configured number (“You can only pin 5 messages. Unpin one to pin another.”). You do not need to handle the rejection yourself. Where an app configures no cap, the toast falls back to generic copy rather than guessing a figure.
The caps are read once per session and cached, so the same number is available before a user reaches it — use
PinSaveService.getConfiguredLimit() to disable a control ahead of the rejection. See Acting Programmatically.features.ux.messages.pinned.system.limit and features.ux.conversations.pinned.system.limit — read through CometChat.getSystemPinnedMessagesLimit() and CometChat.getSystemPinnedConversationsLimit(). System pins belong to no member: they sort above user pins and cannot be lifted from the UI.
Indicators
Once a message is pinned or saved, the bubble marks it in its status-info footer:- Pin marker — shown to everyone, because
pinnedAtis conversation-wide - Bookmark marker — shown only to the user who saved it;
savedAtis simply not present in anyone else’s copy
Viewing Pinned and Saved Messages
Two panels list what has been marked. They are separate components — see their own pages for the full API.Pinned messages
Conversation-scoped, shared, and permission-aware. Add the entry point to the message header:Saved messages
Per-user and cross-conversation, so it belongs in your app chrome rather than in a chat header — hanging it off one conversation would misrepresent what it contains.Pinning a Conversation
CometChatConversations adds a Pin conversation / Unpin conversation entry to each row’s context menu, above Delete — the safe, reversible action comes first.
Ordering
Pinned conversations are lifted above unpinned ones. The partition is stable: recency still decides the order among pinned chats and among unpinned ones, so a pinned chat with a new message still rises to the top of its block, and one message in an unpinned chat can never push the pinned block down.Reading the state
pinnedAt is the boolean here too — an unpinned conversation carries no key at all. isPinned() covers both a personal pin and an app-wide one (pinnedBy === "app_system").
Behavior
- Pinning runs straight away; unpinning asks for confirmation, because a pin is a deliberate arrangement of the list and a misplaced click should not undo it
- The row shows a pin marker, and the state reaches screen readers through the row’s accessible label, since the marker itself is
aria-hidden - The pin cap is server-owned and read once per session through
CometChat.getPinnedConversationsLimit(); exceeding it shows “You can only pin N chats. Unpin one to pin another.” with that number
Reacting to Changes
CometChatPinSaveEvents is the channel that keeps bubbles, both panels, and any surface of your own in agreement without a refetch.
source discriminator. That distinction is what the two tiers are for: ccMessagePinned and its siblings are server truth (a confirmed write or a realtime frame), while ccMessagePinChanged and ccMessageSaveChanged are this client’s optimism (a flip applied before the server answered, and its reversal if the write failed). Most surfaces want both, so subscribe to the merged pinned$ / unpinned$ / saved$ / unsaved$ observables rather than to the raw subjects.
Acting Programmatically
PinSaveService is provided in root and owns the behavior for every surface.
run() flips the message optimistically, reconciles against the authoritative copy the server returns, and restores the pre-call attributes on failure — so a rejected pin never leaves a pin showing. One request per message is on the wire at a time, so a double-tap cannot race itself.
Localization
Override any of these through Localization.
Related
- CometChatPinnedMessages — the conversation’s pinned list
- CometChatSavedMessages — the user’s saved list
- CometChatMessageList — where the actions live
- CometChatConversations — pinning a conversation
- Events — the
CometChatPinSaveEventsreference - Global Configuration — the development overrides