Skip to content

feat(react-native-uikit-docs): Pin & Save, Pin Conversation and Thread Subscription - #536

Merged
ketanyekale merged 37 commits into
mainfrom
docs/react-native-pin-save-thread-subscription
Sep 28, 2026
Merged

ketanyekale merged 37 commits into
mainfrom
docs/react-native-pin-save-thread-subscription

Conversation

@suraj-chauhan-cometchat

Copy link
Copy Markdown
Contributor

Continues #516. Same 30 commits, same head SHA — only the branch name changed, from feat/rn-pin-save-thread-subscription-uikit-docs to docs/react-native-pin-save-thread-subscription, to satisfy .github/branch-naming-convention.md (the validate-branch-name check was blocking the merge).
GitHub closed #516 on the rename rather than retargeting it, so the review history from @raj-dubey1 and @jitvarpatil lives there — every point raised in both reviews is already addressed in these commits.

Brings the React Native UI Kit docs to parity with the Android and React kits for the four features shipped in @cometchat/chat-uikit-react-native@5.5.0.

Before this, React Native had the two panel component pages (pinned-messages, saved-messages) but no core-features entries, no guides, and no mention of the thread-subscription bell anywhere.

Core features

ui-kit/react-native/core-features.mdx gains three sections, placed in the same order Android uses:

Mentions → Pin & Save Messages → Pin Conversations → Rich Text Formatting
Threaded Conversations → Thread Subscription → Group Chat

Thread Subscription sits directly after Threaded Conversations, as requested.

New guides

File Modelled on
guide-pin-and-save-messages.mdx ui-kit/react/guide-pin-and-save-messages.mdx
guide-threaded-messages.mdx (thread-subscription section) ui-kit/android/guide-thread-subscription.mdx

Both are added to docs.json in Android's relative order. Note RN keeps thread subscription inside guide-threaded-messages.mdx rather than as its own page, so there is no guide-thread-subscription.mdx on this branch.

Thread header component

threaded-messages-header.mdx gains a Thread Subscription section, mirroring the React thread-header page.

Two deliberate divergences from the reference platforms

These are real platform differences, documented rather than copied:

1. The bell is on CometChatMessageHeader, not CometChatThreadHeader.
React puts its toggle on the thread header. The landed React Native design places it in the thread screen's top bar, driven by parentMessage + threadSubscriptionVisibility. CometChatThreadHeader's TrailingView is the escape hatch for a custom control there and explicitly does not host the bell. The section says so, so nobody goes looking for a prop that isn't there.

2. Thread Subscription is ON by default on React Native, and has no dashboard flag — so it is documented as opt-out via ThreadSubscriptionConfig.setEnabled(false). Android documents the same feature as opt-in because its default differs. The React Native text matches what v5.5.0 actually ships and the published release notes.

Pin is documented as available to every member, with the server enforcing permission — matching React. The React Native role gate was removed in ENG-38197, so canPin() now only asserts that somebody is logged in.

Verification

  • 0 dangling navigation refs across the whole docs.json — build is safe
  • 100 internal links across the changed files, all resolve
  • docs.json diff is 3 insertions / 1 deletion — no reformat churn
  • Every prop, method and default documented here was read from the shipped 5.5.0 source, not from the design doc

🤖 Generated with Claude Code

…d subscription

The React Native UI Kit shipped Pin Message, Save Message, Pin Conversation and
Thread Subscription in v5.5.0, but the docs only covered the two panel components.
This brings React Native to parity with the Android and React kits.

Core features (ui-kit/react-native/core-features.mdx)

  Three sections, placed in the same order Android uses:

    Mentions -> Pin & Save Messages -> Pin Conversations -> Rich Text Formatting
    Threaded Conversations -> Thread Subscription -> Group Chat

  Thread Subscription sits directly after Threaded Conversations, matching Android.

New guides

  guide-pin-and-save-messages.mdx  modelled on the React guide
  guide-thread-subscription.mdx    modelled on the Android guide

  Both are wired into docs.json in Android's relative order — threaded-messages,
  then thread-subscription, then pin-and-save.

Thread header component (threaded-messages-header.mdx)

  Adds a Thread Subscription section, mirroring the React thread-header page.

Two platform differences are documented rather than copied over:

  * The bell is on CometChatMessageHeader, NOT CometChatThreadHeader. React puts
    its toggle on the thread header; the landed React Native design places it in
    the thread screen's top bar, driven by `parentMessage` +
    `threadSubscriptionVisibility`. CometChatThreadHeader's TrailingView is the
    escape hatch for a custom control there and does not host the bell.

  * Thread Subscription is ON by default on React Native and has no dashboard
    flag, so it is documented as opt-OUT via ThreadSubscriptionConfig.setEnabled(false).
    Android documents the same feature as opt-in because its default differs.
    The text matches what v5.5.0 actually ships and the published release notes.

Pin is documented as available to every member with the server enforcing
permission, matching React — the React Native role gate was removed in ENG-38197,
so canPin() now only asserts that somebody is logged in.

Verified: 0 dangling navigation refs, and 100 internal links across the changed
files all resolve.
The section had drifted from the Android original: different table headers
(Components/Functionality vs Component/Role), a shortened link label, and two
extra paragraphs about the dashboard flag and server-owned ordering.

Now byte-identical to ui-kit/android/core-features.mdx, with only the platform
path in the link differing.
…ve and Thread Subscription

Pin Conversations was aligned in 984ef98; these two had drifted the same way —
"Components/Functionality" table headers instead of "Component/Role", shortened
link labels, and extra "Hide it with ..." clauses and paragraphs Android does not
carry.

Both now follow the Android sections verbatim. Three differences remain, each a
real platform fact rather than drift:

  * Pin & Save — "Pinned messages entry point", not "menu entry point": on React
    Native it is a header button (showPinnedMessagesButton), not a menu item.
  * Thread Subscription — "Enabled by default" rather than Android's "Opt-in".
    React Native ships ThreadSubscriptionConfig with enabled = true.
  * Thread Subscription — the bell is on CometChatMessageHeader, not
    CometChatThreadHeader, per the landed React Native design.
Removes all 12 mentions across four pages. Thread subscription is now documented
purely through its two per-surface props:

  hideThreadSubscriptionOption   CometChatMessageList
  threadSubscriptionVisibility   CometChatMessageHeader

message-list.mdx           dropped the opt-out paragraph, the TypeScript/JavaScript
                           tabs that only demonstrated setEnabled, and the mention
                           in the hideThreadSubscriptionOption prop description
guide-thread-subscription  "Enable the Feature" rewritten around the two props
threaded-messages-header   dropped the global row from the controls table
core-features              points at the two props instead

The feature is still described as enabled by default, which is what 5.5.0 ships.
… Android

The five bullets specify per-section references, and Pin & Save in core features
is a React one — it had been switched to Android's format along with the other
two sections.

Now follows ui-kit/react/core-features.mdx: React's heading ("Pin and Save
Messages"), intro, Components/Functionality table, and — the part Android's
version does not carry — the app-settings limits table and its closing paragraph
on cap toasts.

Five deliberate differences:

  * the Storybook <Info> + <iframe> block is dropped; React Native has no
    Storybook to embed
  * the #pin-and-save-options deep link is dropped; that anchor does not exist on
    the React Native message-list page
  * "message options menu" -> "message options": React Native uses an action sheet
  * "panel" -> "screen": React Native navigates rather than opening a panel
  * the Conversations row is dropped; Pin Conversations is its own section here,
    per the Android reference, so listing it twice would duplicate

The heading change moves the anchor, so the inbound link in the guide is updated
to #pin-and-save-messages — which is also React's own anchor.
…has it

CometChatThreadHeader was carrying the thread-subscription section, but on React
Native it renders no bell — the control belongs to CometChatMessageHeader, which
said nothing about it. Every other platform documents the bell on the component
that owns it: React and Android on their thread header, iOS on both because iOS
supports both.

message-header.mdx gains two sections, with worked examples:

  Pinned Messages      showPinnedMessagesButton (OFF by default) and
                       onPinnedMessagesPress. The guide tells integrators to use
                       these while the component's own page never listed them.
  Thread Subscription  parentMessage as the thread-mode switch that renders the
                       bell, threadSubscriptionVisibility, and
                       onThreadSubscriptionChange — the last of which was
                       undocumented anywhere, though React documents its own.

Both toggles are added to the Visibility Props table too.

threaded-messages-header.mdx keeps a Thread Subscription section, but it is now a
pointer rather than a duplicate: it states plainly that this component renders no
bell, shows the two components side by side as a thread screen actually composes
them, and links to the message header for the props. Its TrailingView note stays —
that slot is the escape hatch for a custom control and is easily mistaken for the
bell's home.

All five props shipped in 5.5.0 are now documented on the component that has them.
…ubscription section

Leads with where the bell is rather than where it is not.
Thread subscription was a top-level entry in both places. It belongs inside threaded
messages, which is how the other kits structure it.

Core features

  `## Thread Subscription` becomes `### Thread Subscription` inside
  `## Threaded Conversations`, matching iOS — its core-features page nests the same
  section at h3 under the same parent.

Guides

  The standalone guide-thread-subscription.mdx is removed and its content folded into
  guide-threaded-messages.mdx as a `## Thread Subscription` section, matching React —
  its threaded-messages guide carries the feature with the same three subsections:

    Automatic subscription
    Reacting to changes
    Hiding the controls

  The React Native text differs where the platform does: the bell is on
  CometChatMessageHeader rather than the thread header, and CometChatMessageList takes
  the whole `parentMessage` rather than an id, since a reply arriving over the socket
  carries no subscription flag of its own and is stamped from the parent.

  onThreadSubscriptionChange is documented here as well as on the component page —
  React documents its own equivalent in both places.

The nav entry is dropped and all three inbound links now point at
guide-threaded-messages#thread-subscription.

Verified: 0 dangling navigation refs, and 413 internal links across the React Native
pages all resolve.
The React component pages carry a live Storybook iframe so a reader can see the
panel before wiring it up. There is no React Native Storybook to embed, so these
pages had no visual at all — and neither do Android's or iOS's. Static screenshots
are the closest equivalent.

  pinned-messages.mdx              the Pinned Messages screen
  saved-messages.mdx               the Saved Messages screen
  guide-pin-and-save-messages.mdx  both, beside the step that builds each screen

Placed right after "Where It Fits", which is where React puts its preview.

Each carries descriptive alt text rather than a label. React's iframe has none, and
on these pages the screenshot is the only visual explanation of what the feature
looks like — so it needs to work for a reader who cannot see it.

Verified: all four references resolve, and 175 image references across the React
Native pages were checked. The one miss,
compact-message-composer-overview.png, is untouched by this branch and is already
broken on main.
The pin & save pages documented kit-internal plumbing alongside the
public surface. Nothing here is API a customer writes against:

- `## Enabling the feature` (PinSaveConfig / getPinSaveFeatures /
  refreshPinSaveFeatures) — the kit resolves these itself at login and
  re-reads them on every reconnection (CometChatUIKit onLoggedIn +
  ConnectionListener.onConnected), so there is nothing to call. The
  page's opening <Warning> already states the Dashboard requirement.
- `## Reading pin state yourself` / `## Reading save state yourself`
  (isPinned / isSaved / isSystemPin / SYSTEM_PINNER) and the matching
  "helpers" blocks in the AI Integration Quick Reference accordions.
- Two implementation asides: how the panel routes local vs SDK save
  events, and that the RN SDK has no isPinned() of its own.

Also corrects a stale instruction on conversations.mdx: Pin Conversation
no longer needs `PinConversationConfig.enable(true)` from the
integrator — PinSaveFeatureGates applies it from the server flag. The
<Warning> framed it as a required setup step, so anyone following it was
writing dead code.

React, Android and iOS document none of these APIs; this brings React
Native into line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ndard

Both pages were missing sections every other React Native component page
has, and ordered the ones they did have differently. They now follow the
house order used by conversations / message-list / users / groups:

  Where It Fits -> Minimal Render -> Actions and Events ->
  Custom View Slots -> Common Patterns -> Styling -> Props -> Next Steps

Added:
- `## Styling` with a worked example and a Style Properties table for
  every key of PinnedMessagesStyle and SavedMessagesStyle, including
  `closeButtonIcon`, the saved row's `previewIconStyle`, and the
  empty/error state styles. None of this was documented; the pages just
  listed `style` as one row with an opaque DeepPartial type.
- `## Custom View Slots` for ItemView, noting that empty/error/loading
  are restyled rather than replaced on these two components.
- `## Opening from the Message Header` (pinned), stating that
  `showPinnedMessagesButton` adds an item to the header's overflow menu
  rather than rendering a standalone button, and that it is a silent
  no-op without `onPinnedMessagesPress`.
- Per-prop `### name` entries with Type/Default tables, replacing the
  single flat table — matches every other RN component page.
- The SavedMessageSource field table, so `onItemPress`'s second argument
  is usable. The type is not exported from the package, so the example
  no longer annotates with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1. (must) Saved Messages guide read `source?.user` / `source?.group`,
   which SavedMessageSource does not have — it is
   { receiverType, receiverId, label, name, avatar? }. The snippet failed
   typecheck and at runtime navigated with both undefined. Now resolves
   the User/Group from receiverId + receiverType before navigating, and
   the prose lists the real fields.

2. (should) Threads guide wired `onThreadRepliesClick`; the RN prop is
   `onThreadRepliesPress` (CometChatMessageList.tsx:346), so navigation
   to the thread screen never fired. Renamed in all 3 places.

3. (should) Both guide screens passed `messageId` back to Messages with a
   "jump to this message" comment, but the Complete Example never used
   it. Wired `goToMessageId` on CometChatMessageList, which is the prop
   that actually performs the jump.

Nits 4-6 deliberately left for a follow-up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
React has a Custom Text Formatter guide that builds a color button in the
composer toolbar. React Native had no equivalent, and its toolbar slot
was undocumented even though it shipped in 5.5.0.

- New guide `guide-text-color`: a swatch color button in
  `ToolbarTrailingButtonsView`, driven by the `composer` handle's
  `applyInlineStyle("color", …)`. Based on the masterapp sample covered
  by e2e/color-richtext.e2e.js. Unlike React, no custom formatter is
  needed: color is part of the built-in rich-text wire format
  (`<color=#rrggbb>`), and CometChatRichTextFormatter already renders it.
  Documents what does not survive send: backgroundColor, color inside a
  code block.
- compact-message-composer: `ToolbarTrailingButtonsView` slot row,
  section with example, and the `ComposerInputHandle` method table.
- Fix the Toolbar Visibility Modes table, which was inverted. The
  toolbar renders when `enableRichTextEditor && !hideRichTextFormattingOptions`
  (CometChatCompactMessageComposer.tsx), so `hideRichTextFormattingOptions`
  true hides it. The new guide depends on this being right.
- Nav entry after Pin & Save (matches React's placement) and a row in
  the guides overview.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… matching React

The base-class guide already used that title in the same sidebar group, so
it gets sidebarTitle "Text Formatter Base Class" to avoid two identical
entries. Cross-links and the guides overview updated to the new names.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lpha

Verified against the 5.5.0 native editors: applyInlineStyle with no
selection arms the color for the next typed text (RichTextEditorView.kt
applyInlineStyle, RichTextEditorView.swift applyInlineStyle), and the
reported color is masked to 6-digit hex (hexOf: #%06x), so alpha is lost.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The guide's example kept the picker's open state inside a child component.
CometChatCompactMessageComposer mounts ToolbarTrailingButtonsView as a
component, and an inline render function is a new function on every host
re-render, so that child remounted and the picker closed on its own. The
masterapp sample (Messages.tsx, covered by e2e/color-richtext.e2e.js) keeps
colorPickerOpen in the screen; the guide now does the same, with the same
swatches, code-block disable and clear button.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
React's Custom Text Formatter guide builds an app-owned color formatter: a marker the
integrator defines, a toolbar button that writes it, and a formatter that renders it
everywhere the message appears. React Native had no equivalent — only the built-in
rich-text route in guide-text-color, where the UI Kit owns the format.

This adds the twin, section for section, with the SAME marker React publishes —
{color=#e5484d}...{/color} — so one token works across platforms with a matching
formatter on each.

The React Native mechanism differs where it has to:

  React                          React Native
  format() -> <span style>       formatRawText() -> <color=...>
  priority = 20                  runs before markdown automatically
  toolbarTrailingView            ToolbarTrailingButtonsView + composer.replaceSelection()
  -                              handlePreMessageSend() for the round trip

formatRawText() is the part that makes it reliable: it receives the raw string before the
built-in Markdown formatter runs. A formatter that only overrides getFormattedText() is
handed JSX instead, so it silently does nothing on any message containing _, **, '- ', [
or a backtick.

Verified: the guide's own code block was extracted and run against the release branch —
6-digit hex, 3-digit hex and named colors all render, markdown around the marker is
unaffected, and handlePreMessageSend round-trips <color=...> back to {color=...}. Every
symbol used is exported from the package, and all four internal links resolve on this
branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The hashtag example did its matching in getFormattedText(). That method runs AFTER the
built-in Markdown formatter, which hands it JSX rather than a string — so the guide's own
first line returned early and the formatter did nothing.

Run verbatim from the live page, against the shipped kit:

    hashtag styled: YES   "hello #world"
    hashtag styled: NO    "hello #world _ok_"
    hashtag styled: NO    "hello #world **bold**"
    hashtag styled: NO    "- hello #world"
    hashtag styled: NO    "hello #world `code`"
    hashtag styled: NO    "hello #world [a](b)"

It works only on a message with no Markdown at all — which is exactly what someone tests
first, so the failure ships.

The matching now happens in formatRawText(), which receives the raw string before any UI
Kit parsing and returns markup the kit already draws. Same six inputs, after:

    styled: YES  every one, {"color":"#5dff05","fontWeight":"700"}

Changes:
- Step 2 and 3 rewritten around formatRawText; a Warning explains why getFormattedText is
  the wrong place, so nobody reintroduces it
- Example is a complete, copy-pasteable pair of files, with the formatter registered on the
  message list, the composer and the conversation list
- formatRawText added to the base-class listing and the methods table
- Links to the new color formatter guide

Structure follows the React and Android formatter guides; the mechanism is ours.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Switches the marker in the color formatter guide from {color=#hex} to {color:#hex}, as
decided.

Only the APP-owned marker changed. The UI Kit's own <color=...> markup is untouched — the
formatter still maps to it, because that is the one color syntax the React Native renderer
understands.

Verified by extracting the guide's code block from the .mdx and running it against the
release branch: 6-digit hex, 3-digit hex and named colors all render, markdown around the
marker is unaffected, and handlePreMessageSend round-trips to {color:#e5484d}world{/color}.

NOTE for whoever picks up cross-platform parity: React and Angular both publish
{color=#hex} today (ui-kit/react/guide-custom-text-formatter.mdx and
ui-kit/angular/guides/custom-text-formatter.mdx). Android publishes no color marker at all.
With this change React Native differs from React and Angular by one character, so those two
guides need the same switch for the token to be shared across platforms.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…pping alpha

The regex was /{3,6}/ while React and Angular publish /{3,8}/. A message written on the web
with an alpha color — {color=#e5484dff}text{/color} — did not match here, so the marker was
left untouched and the reader saw the raw text rather than colored text.

Now /{3,8}/, with the alpha dropped before the value reaches the UI Kit:

    #rgba     -> #rgb
    #rrggbbaa -> #rrggbb

That step is needed because the UI Kit renders #rgb and #rrggbb only, and an unrecognised
value is not silently uncolored — it leaves the raw <color=...> tag visible in the message,
which is the worse failure.

Verified by extracting the guide's code from the .mdx and running it against the release
branch — all five forms the marker allows now render:

    3  #rgb       -> #f00
    4  #rgba      -> #f00
    6  #rrggbb    -> #e5484d
    8  #rrggbbaa  -> #e5484d
    name          -> #e5484d

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tter', as React does

Three React Native pages all carried title: "Custom Text Formatter", and the sidebar name
was held by guide-text-color — the page whose own first section says "In React Native you
do not need to write a text formatter for this". The one page that IS React's counterpart
was called "Color Formatter".

Sorted out so each page is named for what it teaches:

  guide-color-formatter        Custom Text Formatter      <- matches React and Angular
  guide-text-color             Text Color                 <- built-in toolbar, no formatter
  custom-text-formatter-guide  Text Formatter Base Class  <- the base class

guide-text-color also had title: "Custom Text Formatter", which is now "Text Color" —
three pages sharing one title made them indistinguishable in search.

Also updated the link TEXT that still called guide-text-color the Custom Text Formatter
guide, in compact-message-composer and guide-overview, and added the new guide to the
guide-overview table, where it was missing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ct's parity

Four public APIs land in the React Native UI Kit that the docs never mentioned,
three of which React has documented since its own pin/save shipped. Reading only
the React Native docs, an integrator could not discover any of them.

  messagesRequestBuilder   CometChatPinnedMessages, CometChatSavedMessages
  textFormatters           the same two, plus CometChatSearch and
                           CometChatMessageInformation
  replaceSelection(text)   ComposerInputHandle — the one method the color
                           formatter guide already tells the reader to call
  onError(e)               CometChatGroupMembers now receives the exception

Pinned and Saved each gain a Filtering section and two reference entries,
matching React's pinned-messages / saved-messages pages heading for heading. The
prose is React Native's own, because the two kits differ where it matters: the
builder methods are setPinnedOnly() / setSavedOnly(), the component works on a
prototype-preserving COPY so one builder can feed both panels, the limit falls
back prop -> builder -> 30, and the saved panel clears any uid/guid because a
save is account-wide. All four are stated, since each one is a way to be
surprised.

CometChatMessageInformation had no v5 page at all — only the v4 one — while
React, Angular and Vue all carry theirs, so its new textFormatters prop had
nowhere to live. Added the page: what it shows for a group (server receipts, one
row per recipient) versus a one-on-one (the timestamps already on the message),
why you normally do not render it yourself, and the one case where you must pass
textFormatters — a standalone screen, which has no template to inherit them
from. Its style type is spelled DeepPartial<CometChatTheme["messageInformation
Styles"]> and the ListItemView receipt shape is written out, because neither
MessageInformationStyle nor Recipient is an exported name.

Verified against the shipping source on bugfixes/release-2026-09-week-4-v5, not
from the commit messages: every prop read off its own interface, the builder
invariants off buildPinnedMessagesRequest / buildSavedMessagesRequest, and
setPinnedOnly/setSavedOnly confirmed public in the SDK's CometChat.d.ts. Both
Accordion JSON blocks and docs.json re-parse, and every internal link in the
seven files resolves to a page that exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both the composer snippet in Step 3 and four of the five components in Step 4
were written with `user={user} group={group}` on the same element. They are
mutually exclusive on every one of those components — CometChatPinnedMessages
picks the group and ignores the user, and a reader following the guide's own
instruction to copy the block gets a panel scoped to the wrong conversation with
nothing on screen saying why.

This guide exists to be pasted, so it now shows one scope with the alternative in
a comment.

Verified the rest of the guide by running it: ColorFormatter.ts extracted
verbatim from this page, with only its import path redirected to the real class,
passes 24 assertions through the shipping code — the bubble, conversation-row and
search chains each render red for the plain example and for text that also
carries bold, italics, a link, a bullet and a code span; alpha hex narrows to
what HEX_COLOR_REGEX accepts; an unknown color name leaves the marker alone
instead of leaking a raw <color=> tag; and handlePreMessageSend converts the kit
token back to the marker. Also confirmed against source that the toolbar hosting
the button is on by default, that a consumer's array is added to the built-in
formatters rather than replacing them, and that a formatter with no tracking
character is keyed by id so its send hook still runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
….5.1

The blocking item is retired by the release, not by an edit. Every API the review
called "5.5.1-only, unreleased" is in the published tarball, which is now npm
`latest`: formatRawText (CometChatTextFormatter.ts:235), replaceSelection
(compact composer :566, :3652), textFormatters on Pinned, Saved, Search and
MessageInformation, messagesRequestBuilder on Pinned and Saved, and
GroupMembers `onError?: (e: CometChat.CometChatException) => void` (:190). The
review's own remedy was "hold until 5.5.1 is on npm"; that has happened, so no
version callouts were added — no RN page carries one, and inventing the
convention here would be inconsistent.

Everything else was checked against that same tarball and fixed:

* Moderation. isPinSaveEligible() explicitly allows `pending`
  (PinSaveHelper.ts:308) — the guide claimed the options were hidden until a
  verdict arrived. Rewritten to say what the kit does: offer the option, let the
  server refuse, render the refusal.
* ThreadSubscriptionConfig. Still exported, still ON by default
  (ThreadSubscriptionHelper.ts:82-102), and setEnabled(false) is the only
  app-wide opt-out — it was undocumented anywhere after the rewrite. Documented
  on message-list beside hideThreadSubscriptionOption, with the AND relationship
  between the two spelled out.
* The pinned entry point is a ⋮ overflow-menu item, not a button
  (CometChatMessageHeader.tsx:343-346 pushes `{ text: 'Pinned Messages' }` into
  the options list), and needs BOTH showPinnedMessagesButton and
  onPinnedMessagesPress — setting one does nothing. Corrected in all four places.
* Menu labels are "Subscribe to thread" / "Unsubscribe from thread"
  (translation.json:231-232), not Follow / Unfollow.
* SavedMessageSource is not exported from the package root, so the callback's
  parameter is now shown as an inline type with its real fields instead of a name
  a reader cannot import.
* The five pin/save helpers — isPinned, isSystemPin, isSaved, getPinSaveFeatures,
  refreshPinSaveFeatures — are all still exported and correct. Restored as a
  Helpers section, with a note that refreshPinSaveFeatures is not needed in
  normal use because the kit re-resolves on every reconnect.
* custom-text-formatter-guide: title was "Custom Text Formatter", the same as
  guide-color-formatter — now "Text Formatter Base Class", matching its own
  sidebar entry. The stray top-level `<CometChatConversations …/>;` expression is
  now a comment, since it was neither valid at that position nor part of the
  component above it.

Checked after the edits: every in-page anchor on the five files resolves to a
heading that exists, every internal link resolves to a page that exists, and the
Custom Text Formatter title collision is gone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The last nit from the review that cc26136 missed: both guides declared
`function ChatScreen({ user, group })` in a .tsx block, which is an implicit-any
error under the strict settings the rest of the samples assume. Annotated with
the SDK types, and guide-text-color gains the `CometChat` import the annotation
needs — custom-text-formatter-guide already had it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…othing

Self-review of #516 against the published 5.5.1 tarball. Three props were
documented as working that the component never reads — each is declared in
CometChatMessageInformationInterface, destructured at the top of the component,
and then not referenced again.

  emptyStateText   removed. The default empty view is `<></>`; there is no string
                   to override (:343-346).
  errorStateText   removed. The error view hard-codes t("WRONG_TEXT") and
                   t("WRONG_TEXT_TRY_AGAIN") (:329-330).
  onBack           kept, with a warning. Unlike the other two it appears in both
                   code samples and in the public type, so removing it silently
                   would leave a reader to rediscover it in autocomplete with no
                   explanation. The screen renders no back control at all — its
                   header is a centred <Text> (:350-354) — so the page now says
                   plainly that 5.5.1 accepts the callback and never calls it, and
                   to give the screen a navigator that provides its own back.

Two smaller corrections on the same page:

* The default title was documented as Localized "Message Information". The en
  bundle maps MESSAGE_INFORMATION to "Message Info" (translation.json:278).
* EmptyStateView was described as replacing the empty state. There is no default
  empty state to replace — without the prop the screen renders nothing in that
  condition — so it ADDS one.

Found by checking each documented prop's occurrence count in the shipped source
rather than trusting the interface: a prop that appears exactly twice is declared
and destructured and never used. onBack surfaced from that same check while
verifying the first two fixes.

The new #onback anchor was checked against IDs scraped from the rendered preview,
not a slug rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… the opt-out

Three things from jitvarpatil's review, plus a build break my previous commit
introduced and the review predates.

## The deployment was failing — message-information.mdx

"Failed to parse page content: Unexpected end of file in expression, expected a
corresponding closing brace for `{`". The cause is one table row:

  | Type | `(… receipt: { sender: CometChat.User | CometChat.Group; … })` |

Markdown splits a table row on unescaped pipes BEFORE inline code is parsed, so
the `|` inside the backticks cut the code span in half. The left fragment ends
with an open backtick and an unclosed `{`, which MDX then reads as a JSX
expression that never closes. Escaped as `\|`, the form already used on the
saved-messages page.

Worth recording how it was found: a per-line brace check said the file was
balanced, because it stripped intact inline code — and the span only breaks
AFTER markdown splits the row. The check that finds it splits each table row on
unescaped pipes first, then looks for a cell with an odd number of backticks.
That sweep now runs over every changed page; this was the only occurrence.

## Version notes (blocker 1)

`replaceSelection`, `textFormatters` on Pinned/Saved/Search/MessageInformation,
and `messagesRequestBuilder` on Pinned/Saved all landed in 5.5.1. 5.5.1 is npm
`latest`, but nothing told a reader still on 5.5.0 why the samples fail, and on
that version the colour guide's Step 4 silently renders the raw marker on four of
five surfaces — the exact failure that section warns about.

Each note names only the APIs that are actually 5.5.1-only on that page, and says
the rest of the page works on either version. The colour-formatter guide is the
exception: it is built on the new hook end to end, so the whole guide needs 5.5.1
and the note says so.

I declined this when raj-dubey1 first raised it, reasoning that 5.5.1 being
`latest` made it moot and no RN page carries a version callout. Two reviewers
asking for the same thing is the answer: a reader on a pinned 5.5.0 has no other
way to find out.

## App-wide thread-subscription opt-out (should-fix)

`ThreadSubscriptionConfig.setEnabled(false)` was documented on message-list in
cc26136, but a reader wanting the feature off app-wide looks in the threaded-
messages guide, next to the per-surface flags. Added there as "Turning the feature
off app-wide", with the actual gate expression the bell evaluates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nformation page

The page had drifted into the worst of both: two samples passing
`onBack={() => navigation.goBack()}`, and three separate notices saying the
callback is never invoked. It told a reader to use the prop and then told them
three times that it does nothing.

Customer docs describe the product, not its defect list, and "not called in
5.5.1" is prose that rots the moment the kit wires it up. Removed all of it —
the JSON entry, the Actions and Events section, the props entry, and the prop
from both samples.

In its place, one positive sentence that answers the question the warnings were
circling: the screen draws no header controls of its own, so put it on a route
whose navigator supplies the back affordance, or render it in a sheet you close
yourself. That is true on any version and needs no correction when onBack starts
working — only an addition.

The underlying defect is a kit issue, not a docs one: onBack, emptyStateText and
errorStateText are all declared in CometChatMessageInformationInterface,
destructured, and never read. Raising separately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@suraj-chauhan-cometchat

Copy link
Copy Markdown
Contributor Author

@jitvarpatil @raj-dubey1 — ready for re-review. Everything from both of your reviews on #516 is addressed here.

Why the PR number changed: the branch was feat/rn-pin-save-thread-subscription-uikit-docs, which .github/branch-naming-convention.md doesn't allow for docs (feat/ is in neither list), so validate-branch-name was blocking the merge. Renaming it closed #516 rather than retargeting it — same 30 commits, same head SHA, nothing rewritten. Your discussion stays readable on #516.

@jitvarpatil

Item Status
Blocker 1 — 5.5.1-only APIs with no version note Version notes on all six pages. Each names only the APIs that are actually 5.5.1-only on that page and says the rest works on either version; the colour-formatter guide is the exception — it's built on formatRawText end to end, so the whole guide needs 5.5.1 and says so
Blocker 2 — validate-branch-name failing This PR. Check now passes
Should fix — no documented app-wide opt-out ThreadSubscriptionConfig.setEnabled(false) added to guide-threaded-messages under "Turning the feature off app-wide", with the gate expression the bell evaluates. It was already on message-list; you're right that it belongs next to the per-surface flags

You were right on the version notes and I'd argued against them when @raj-dubey1 first raised it — reasoning that 5.5.1 being latest made it moot. Two reviewers asking is the answer: a reader pinned to 5.5.0 has no other way to find out.

@raj-dubey1

Everything from your list landed in cc261366 / aa1a7794: the pending moderation claim (isPinSaveEligible allows it), ThreadSubscriptionConfig, the pinned entry point being a ⋮ menu item needing both props, the duplicate "Custom Text Formatter" title, the Subscribe/Unsubscribe labels, SavedMessageSource not being exported from the root, the stray top-level JSX, the untyped ChatScreen props, the five restored pin/save helpers, and the PR body reference.

Found while verifying, not in either review

  • The Mintlify build was broken by one of my own commits. A table cell contained `… receipt: { sender: CometChat.User | CometChat.Group … }` — markdown splits a row on unescaped pipes before parsing inline code, so the span broke and left an unclosed { that MDX read as a never-closing JSX expression. Escaped as \|. Deployment is green again.
  • Three props on CometChatMessageInformation are declared, destructured and never read — onBack, emptyStateText, errorStateText (:47/:73, :50/:76, :52/:78 in published 5.5.1). All three are now out of the docs rather than documented with a caveat. The interface still exports them, so that remains a kit issue.

Checks: validate-branch-name pass · Mintlify Deployment success · link-rot pass.

raj-dubey1
raj-dubey1 previously approved these changes Sep 25, 2026
…guide

The page already carried React's title, sidebar title and all eight headings
verbatim. The URL was the last thing that differed:

  react         /ui-kit/react/guide-custom-text-formatter
  react-native  /ui-kit/react-native/guide-color-formatter   <- now matches

Renamed the file and repointed all three references — the docs.json nav entry,
the table row in guide-overview, and the Card in custom-text-formatter-guide.

No redirect needed: the page is added by this PR and has never been published, so
the old slug has no live URL to break. Checked against main before renaming
rather than assuming.

Verified after: docs.json parses, every react-native nav ref resolves to a file
that exists, and no internal link on any react-native page points at the old
slug.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…act and Android do

Both reference platforms use the same name for this page, and React Native was
the only one that didn't:

  react     ui-kit/react/plugins/text-formatters        "Text Formatters"
  android   ui-kit/android/customization-text-formatters "Text Formatters"
  RN        custom-text-formatter-guide                  "Text Formatter Base Class"  <- renamed

With the previous commit that makes the pair line up across all three: this page
is "Text Formatters", and the worked guide beside it is "Custom Text Formatter"
on every platform.

Retitled the page and updated all 11 occurrences of the old label across 7 files,
so link text matches the page it points at rather than naming something that no
longer exists. Also renamed the one section heading React has a direct
counterpart for — "Base Class Overview" -> "The Formatter Interface"; nothing
linked to the old anchor.

The SLUG is deliberately unchanged. It is published on main with 25 inbound
references, so renaming needs a redirect — and there is no single slug to match
anyway, since React uses plugins/text-formatters and Android uses
customization-text-formatters. The visible name is what a reader navigates by.

Full heading parity would be a content rewrite, not a rename: React's page also
documents the built-in formatters, which React Native splits into its own
mentions/URL/shortcut pages. Left alone.

Verified: no duplicate titles across the react-native pages, every in-page anchor
and internal link still resolves, docs.json parses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
969aa91 renamed guide-color-formatter.mdx to guide-custom-text-formatter.mdx but
shipped ONLY the rename. docs.json line 1214, the guide-overview table row and
the Card in custom-text-formatter-guide were all left pointing at a file that no
longer exists — a dangling nav ref, which is a build break, plus two 404 links.

My mistake, twice over. The `git add` that was meant to stage them listed the old
path as well; that path was gone after `git mv`, so git rejected the whole
pathspec and staged nothing — and `2>/dev/null` swallowed the error. Then I read
the `git status --porcelain` output as confirmation, missing that a leading space
means UNSTAGED: only the `R ` row was staged, the three ` M` rows were not. The
`git reset --hard` at the start of the next turn then discarded them.

The check that would have caught it existed and ran — it reported
`guide-overview.mdx -> /ui-kit/react-native/guide-color-formatter` — but it ran in
the same command as the commit, so I saw the failure only after pushing. It now
gates the commit instead of reporting beside it.

Verified before committing: every react-native nav ref resolves to a file that
exists, and every in-page anchor and internal link across the react-native pages
resolves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verified each finding against the shipped source before changing anything; two of
the five do not apply to these pages.

## Thread subscription default was stated without a version

iOS gates it from v5.2.0 and Android from v6.1.0, while React Native said only
"Enabled by default". For React Native the flip landed in `48ff1569` and first
shipped in **v5.5.0** (`git tag --contains` gives v5.5.0, v5.5.1). Both places
that state the default now say so, and core-features also points at
`ThreadSubscriptionConfig.setEnabled(false)` as the app-wide opt-out.

## Step 5 renamed and corrected

React's #518 deliberately renames this section to "App Settings and Limits";
React Native still had the older "Step 5: Limits". Renamed, and brought over the
app-settings table so both pages name the same six `features.ux.*` keys.

The bigger problem was the sentence under it. It said the toast carries "the
server-provided limit" and implied a number is always present. `resolveCapLimit`
(PinSaveHelper.ts:405-460) says otherwise: `errorParams.limit` is "documented,
but rarely present", the usual source is the app settings cached at login, and
the function returns NULL when neither has a value — deliberately, so callers
show copy without a figure "rather than printing a guess". The SDK agrees:
ErrorModel.ts warns "Do NOT assume a cap breach carries its limit" (ENG-37690).
Now describes both sources in order and adds a Note that the toast may name no
number at all.

## Two findings that are not ours

* Error codes — the review is right that `ERR_PERMISSION_DENIED` is the RBAC/SBAC
  denial code (ErrorModel.ts:18 documents it carrying `{action, role,
  restrictionSource, guid, scope}`), and `ERR_SYSTEM_PINNED_CONVERSATION` does
  not exist anywhere in the React Native SDK. No react-native UI Kit page cites
  any of these codes, so there is nothing to fix here. The stale
  `ERR_ACTION_NOT_ALLOWED` reference is in the SDK's own pinMessage JSDoc
  (CometChat.ts:2365) — an SDK fix, not a docs one.
* System vs user pin budgets — the SDK settles it: "system pins do not consume a
  user's allowance" (CometChat.ts:2564), so the budgets ARE independent and
  articles/error-guide.mdx is the page that is wrong. No react-native UI Kit page
  makes a claim either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
raj-dubey1
raj-dubey1 previously approved these changes Sep 28, 2026

@raj-dubey1 raj-dubey1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Every point from the #516 review is addressed, and @cometchat/chat-uikit-react-native@5.5.1 is now published as latest. I checked the 5.5.1 tarball: formatRawText, replaceSelection, textFormatters and messagesRequestBuilder on Pinned/Saved, textFormatters on Search and MessageInformation, and GroupMembers onError(e) are all present, and the pages carry clear "5.5.1+" callouts. The moderation note, ThreadSubscriptionConfig, the overflow-menu wording, titles, labels, the SavedMessageSource note and the helpers table are all fixed. Build is safe: 0 dangling nav refs, 0 broken links, images resolve.

Optional follow-ups (non-blocking):

  • The custom-text-formatter-guide and guide-custom-text-formatter slugs are easy to confuse.
  • llms-react-native-v5.mdx doesn't list the new pages and still says 5.4.0 (pre-existing).
  • The PR body still says "shipped in 5.5.0" and "read from 5.5.0 source".

🤖 Generated with Claude Code

jitvarpatil
jitvarpatil previously approved these changes Sep 28, 2026

@jitvarpatil jitvarpatil left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving

Both blockers from #516 are cleared, and the should-fix was taken further than I asked.

1. Version mismatch — fixed thoroughly. All six affected surfaces carry an explicit 5.5.1 note, each naming the actual failure mode rather than hand-waving: compact-message-composer (composer.replaceSelection()), guide-custom-text-formatter (spelling out that on 5.5.0 the toolbar button does not compile and four of the five Step 4 surfaces silently render the raw marker), pinned-messages / saved-messages (messagesRequestBuilder + textFormatters), search and message-information (textFormatters).

2. Branch name — fixed. validate-branch-name passes; all other checks green.

3. The app-wide opt-out is now documented. guide-threaded-messages gains "Turning the feature off app-wide" with ThreadSubscriptionConfig.setEnabled(false), the import, and the real gate expression; core-features points at it. Verified in the published 5.5.0 tarball — private static enabled: boolean = true — so "Enabled by default from 5.5.0" is correct, and the source comment explains why the default flipped (RN's extra global gate defaulted to false, so identical integration code showed the control on web and nothing on mobile).

Spot-checks on the parity pass (af12d1d8)

resolveCapLimit in PinSaveHelper.ts does return null when neither errorParams.limit nor the cached app settings carry a figure, and readCapLimit's own comment says the cap is "Usually absent". So the corrected wording — the toast may name no number at all — is accurate where the previous text implied a number is always present.

Structure

  • Nav: all three guides plus message-information wired in, no orphans; the rename left no stale guide-color-formatter references.
  • In-page anchors: 0 unresolved across every React Native page, checked with Mintlify's slug rule (a dot in a numbered heading becomes a hyphen — a GitHub-style slugger strips it and reports false passes).
  • The two similarly-named formatter pages mirror React's own slugs, so the parity claim holds.

Thanks for the care on the version notes in particular — they turn a silent runtime failure into something a reader can diagnose.

The two cross-platform findings in af12d1d8 that belong to other pages (the error-guide pin-budget statement and ERR_SYSTEM_PINNED_CONVERSATION) I have carried over to #517, with the published-SDK evidence.

… React's #518

React's open PR #518 rewrites its custom text formatter guide to store colored
text as `{color:#e5484d}text{/color}` — a colon, not the equals sign its merged
page still publishes. The marker is app-owned and travels on the message as
plain text, so a React Native app writing `{color=…}` would show as raw text in
a React client reading `{color:…}`. Same class of break as the Web/React Native
mismatch in Rekha's ticket.

All 8 occurrences switch: the prose in Steps 1 and 3, the <Warning>, the
round-trip diagram, and the three code sites (COLOR_REGEX, handlePreMessageSend,
replaceSelection).

The UI Kit's own wire format `<color=#rrggbb>` is deliberately untouched —
that one is fixed in richTextWireFormat.ts (COLOR_OPEN_TAG = '<color='), not
app-owned, and #518 keeps its equivalent too.

Also drops a comment claiming React and Angular publish the same pattern.
Angular is not in #518's scope and keeps `{color=`, so the claim stops being
true once this lands; replaced with a platform-neutral note.

Verified by extracting COLOR_REGEX, KIT_REGEX and toKitHex from the edited page
with a script and running them: 6 assertions green, including a negative control
proving the old `{color=…}` form now passes through untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…when the component re-asserts them

Both Filtering sections said opposite things about fifteen lines apart. The prose
and the code comment told the reader the flag is mandatory — "without it the
request is an ordinary history read", "// required" — while the Note below said
the component re-asserts it "so those are safe even if you omit them".

The Note is the correct one. buildPinnedMessagesRequest calls setPinnedOnly(true)
and setSavedOnly(false) with no condition in front of them
(PinnedMessagesHelper.ts:40-41), buildSavedMessagesRequest does the mirror
(SavedMessagesHelper.ts:245-246), and CometChatPinnedMessages.tsx:252 is the only
site the prop is used, so no builder reaches the SDK unprocessed.

Proved it rather than reading it: three throwaway jest cases against the real
helpers, all green. The load-bearing one passes a builder with
setPinnedOnly(false) set deliberately and the built request still comes out
pinnedOnly=true — the component overrides the caller, it does not merely fill a
gap.

The ambiguity is in the phrase itself. The FLAG is required — without it anywhere
in the chain the SDK does an ordinary history read — but setting it YOURSELF is
not, and "without it" reads as the second. Prose now recommends writing it for
explicit intent and drops the claim that omitting it breaks the fetch.

Same wording is on React's components/pinned-messages.mdx:206 and
components/saved-messages.mdx:165, which is where I took it from. React's kit
7.2.2 re-asserts too — its own JSDoc says setPinned(true) is "re-asserted because
without it this is an ordinary history read" — so that page has the same defect.
Left for its owner alongside #518.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ketanyekale
ketanyekale merged commit d2af3b1 into main Sep 28, 2026
5 checks passed

This branch was successfully deployed

1 active deployment
staging — 9183e9a5 Deployed Sep 28, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

5 participants