Skip to content

[DX-475] Updated docs section for handling self published message#3215

Open
sacOO7 wants to merge 2 commits intomainfrom
fix/chat-self-publish-message
Open

[DX-475] Updated docs section for handling self published message#3215
sacOO7 wants to merge 2 commits intomainfrom
fix/chat-self-publish-message

Conversation

@sacOO7
Copy link
Contributor

@sacOO7 sacOO7 commented Feb 19, 2026

Summary by CodeRabbit

  • Documentation
    • Added a new section on handling self-published messages in chat, describing two approaches—wait for the subscriber and optimistic UI with deduplication—and including sample patterns and snippets for JavaScript, React, Swift, Kotlin, and Android.
    • Minor Kotlin code sample correction: adjusted a message list type signature.

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

Walkthrough

Adds a "Handle self-published messages" section to the chat messages docs describing two approaches to avoid duplicate UI for sent+received messages (wait for subscriber vs. optimistic UI with deduplication). Inserts multi-language examples and corrects Kotlin example type signatures; duplicates content across language blocks.

Changes

Cohort / File(s) Summary
Chat Messages Documentation
src/pages/docs/chat/rooms/messages.mdx
Adds new "Handle self-published messages" section after the Send example, presenting two approaches: (1) render only from subscription callbacks, (2) optimistic UI plus subscriber-side deduplication (serial/version-based) with code samples in JavaScript, React, Swift, Kotlin, and Android. Also fixes two Kotlin type signatures from List<Messages> to List<Message>.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I hopped through docs both day and night,
Found twin paths to make UI right.
Wait for subscribe or optimistic cheer—
Serial checks keep duplicates clear.
Hooray for tidy message flight! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a new documentation section about handling self-published messages to prevent UI duplicates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/chat-self-publish-message

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sacOO7 sacOO7 changed the title [DX-475] Updated docs section for handling self published message to messages.kt [DX-475] Updated docs section for handling self published message Feb 19, 2026
@sacOO7
Copy link
Contributor Author

sacOO7 commented Feb 19, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 19, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sacOO7 sacOO7 requested a review from Copilot February 19, 2026 08:37
@sacOO7 sacOO7 marked this pull request as ready for review February 19, 2026 08:42
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds documentation for handling self-published messages in chat applications to prevent message duplication. When a message is sent using send(), the server echoes it back to all subscribers including the sender. This can cause messages to appear twice in the UI if not handled properly.

Changes:

  • Added a new "Handle self-published messages" section with guidance on two approaches: only rendering from subscription (simple) or using serial-based deduplication for optimistic UI updates
  • Provided code examples in JavaScript, React, Swift, Kotlin, and Android demonstrating the serial-based deduplication approach
  • Included explanatory documentation about when to use each approach

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/pages/docs/chat/rooms/messages.mdx (1)

238-240: Optional: add a code example for Approach 1.

Approach 1 is described only in text. A minimal code snippet (subscribe-only rendering, no optimistic add) alongside Approach 2 would make the docs consistent and let developers copy-paste either pattern without having to infer what Approach 1 looks like in code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/docs/chat/rooms/messages.mdx` around lines 238 - 240, Add a minimal
code example to the "Approach 1" section showing the subscribe-only rendering
pattern: show a snippet that subscribes to message events (e.g., onMessage or
messagesSubscription handler), appends incoming messages to local state (e.g.,
setMessages or messagesRef), and calls send() without mutating the UI
optimistically; reference the same send() function and the subscription listener
used elsewhere in the doc so readers can copy-paste a simple pattern where
send() only sends to the server and the subscription callback is solely
responsible for adding messages to the local list.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/docs/chat/rooms/messages.mdx`:
- Around line 233-241: Add a single clarifying sentence inside the existing
<Aside data-type='important'> block (near the explanation of send() and
subscribe()) stating that setting echoMessages=false does not disable the Chat
SDK's echo behavior—echoes are emitted by the Chat service itself and must be
deduplicated client-side (e.g., via serial or rendering only from
subscriptions). Reference the existing terms send(), subscribe(), serial, and
echoMessages=false so readers can immediately connect the caveat to the
surrounding guidance.
- Around line 297-314: The code mutates the shared var messages from two
concurrent contexts (the unstructured Task processing messagesSubscription and
the outer async scope) causing a data race; confine all mutations to the
MainActor by ensuring any append/contains on messages runs on the MainActor—wrap
the loop body inside the Task handling messagesSubscription so each iteration
does await MainActor.run { if !messages.contains(where: { $0.serial ==
message.serial }) { messages.append(message) } } and do the same for the
optimistic append (await MainActor.run { messages.append(sentMessage) }), or
alternatively annotate the surrounding UI/async scope (the function or enclosing
type that owns messages and invokes room.messages.send/write) with `@MainActor` so
both contexts perform mutations under actor isolation (reference: messages,
messagesSubscription, Task { ... }, and room.messages.send).
- Around line 262-263: The optimistic push after await room.messages.send(...)
is unconditional and can create duplicates; mirror the subscription's serial
check before adding the sent message: after calling room.messages.send(...) and
receiving message, verify the message.serial (or the same unique identifier used
by the subscription callback) is not already present in the messages collection
(e.g., check messages array for an existing entry with that serial) and only
call messages.push(message) when it's missing; apply this symmetric serial check
in the send-side code path that currently does messages.push(message).

---

Nitpick comments:
In `@src/pages/docs/chat/rooms/messages.mdx`:
- Around line 238-240: Add a minimal code example to the "Approach 1" section
showing the subscribe-only rendering pattern: show a snippet that subscribes to
message events (e.g., onMessage or messagesSubscription handler), appends
incoming messages to local state (e.g., setMessages or messagesRef), and calls
send() without mutating the UI optimistically; reference the same send()
function and the subscription listener used elsewhere in the doc so readers can
copy-paste a simple pattern where send() only sends to the server and the
subscription callback is solely responsible for adding messages to the local
list.

@sacOO7 sacOO7 force-pushed the fix/chat-self-publish-message branch from 4e30b7c to e1362a1 Compare February 19, 2026 14:29
@sacOO7
Copy link
Contributor Author

sacOO7 commented Feb 19, 2026

@coderabbitai review

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@sacOO7 sacOO7 force-pushed the fix/chat-self-publish-message branch 3 times, most recently from 04bd9fc to 642602b Compare February 26, 2026 08:31
@sacOO7
Copy link
Contributor Author

sacOO7 commented Feb 26, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/pages/docs/chat/rooms/messages.mdx (2)

243-257: ⚠️ Potential issue | 🟠 Major

Optimistic UI examples are missing send-side deduplication.

At Line 243 onward, the optimistic approach only shows subscriber-side duplicate checks. If you also append after send(), you need the same serial/version guard on the send path to avoid duplicates in race conditions.

Suggested pattern (apply across all language examples)
-const message = await room.messages.send({ text: 'hello' });
-myMessageList.push(message);
+const message = await room.messages.send({ text: 'hello' });
+const exists = myMessageList.find(
+  (m) => m.serial === message.serial && m.version.serial === message.version.serial
+);
+if (!exists) {
+  myMessageList.push(message);
+}
In Ably Chat SDK docs, can `messages.send()` resolve before or after the same message is received by `messages.subscribe()` for the sender client?

Also applies to: 259-321

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/docs/chat/rooms/messages.mdx` around lines 243 - 257, The
optimistic UI flow currently only guards against duplicates in the
messages.subscribe() handler; you must also perform the same serial/version
deduplication immediately after messages.send() before appending to your local
list to avoid race duplicates. In the code paths that call send() (the send
handler) check the incoming message.serial and message.version.serial against
your local store (e.g., myMessageList) and skip appending if a match exists, and
ensure the shared list is a concurrent-safe structure or access is synchronized
so both send() and the messages.subscribe() handler use the same guard logic.

233-234: ⚠️ Potential issue | 🟠 Major

Add the echoMessages=false caveat explicitly in this section.

This new guidance explains self-echo behavior, but it still does not state that Chat echo behavior cannot be disabled with echoMessages=false, which is a key requirement of this PR objective.

Suggested patch
 When you send a message using `send()`, the server echoes it back to all subscribers in the room, including the sender. If your application adds the message to the UI immediately after calling `send()` and also appends it when received via `subscribe()`, the message will appear twice. There are two approaches to handle this.
+In Chat, this self-echo behavior is the default and is not disabled by setting `echoMessages=false`.
Does the Ably Chat SDK allow disabling sender echo with `echoMessages=false`, or is `echoMessages` only a Realtime SDK connection option?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/docs/chat/rooms/messages.mdx` around lines 233 - 234, Add an
explicit caveat after the paragraph about duplicate messages stating that
echoMessages=false is a Realtime SDK connection option and does not disable Chat
SDK sender echo: explain that when using Chat send() the server will echo
messages back to all subscribers (including the sender) and that
echoMessages=false cannot be used to prevent this behavior, referencing send(),
subscribe(), and echoMessages=false so readers know which APIs are involved;
update the paragraph that currently discusses the two approaches to handling
duplicates to include this caveat and a short recommended action (e.g.,
deduplicate client-side by checking message ID or sender).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/pages/docs/chat/rooms/messages.mdx`:
- Around line 239-245: Two sentences use em dashes (—) in the new prose, which
violates doc rules; update the two occurrences around the discussion of the
subscriber and optimistic UI so they don't use em dashes: replace the em‑dash
between "the message list is only written to from a single place — the
subscriber" with a comma or parenthetical ("the subscriber") and replace the
em‑dash in "Because the message list is written to from two places — once after
`send()` and again inside the subscriber" with a comma or rephrase ("once after
`send()` and again inside the subscriber"); keep the references to `subscriber`,
`send()`, `serial`, and `version` unchanged.
- Around line 248-321: The code blocks use non-SDK language tags; update each
triple-backtick fence to the SDK-prefixed variants: change `javascript` ->
`realtime_javascript` for the room.messages.subscribe example, `react` ->
`realtime_react` for the useMessages/MyComponent snippet, `swift` ->
`realtime_swift` for the async messagesSubscription loop, `kotlin` ->
`realtime_kotlin` for the room.messages.subscribe lambda, and `android` ->
`realtime_android` for the Compose LaunchedEffect snippet so the <Code>
component follows the realtime_ SDK naming convention.

---

Duplicate comments:
In `@src/pages/docs/chat/rooms/messages.mdx`:
- Around line 243-257: The optimistic UI flow currently only guards against
duplicates in the messages.subscribe() handler; you must also perform the same
serial/version deduplication immediately after messages.send() before appending
to your local list to avoid race duplicates. In the code paths that call send()
(the send handler) check the incoming message.serial and message.version.serial
against your local store (e.g., myMessageList) and skip appending if a match
exists, and ensure the shared list is a concurrent-safe structure or access is
synchronized so both send() and the messages.subscribe() handler use the same
guard logic.
- Around line 233-234: Add an explicit caveat after the paragraph about
duplicate messages stating that echoMessages=false is a Realtime SDK connection
option and does not disable Chat SDK sender echo: explain that when using Chat
send() the server will echo messages back to all subscribers (including the
sender) and that echoMessages=false cannot be used to prevent this behavior,
referencing send(), subscribe(), and echoMessages=false so readers know which
APIs are involved; update the paragraph that currently discusses the two
approaches to handling duplicates to include this caveat and a short recommended
action (e.g., deduplicate client-side by checking message ID or sender).

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4e30b7c and 642602b.

📒 Files selected for processing (1)
  • src/pages/docs/chat/rooms/messages.mdx

Comment on lines 239 to 245
This approach has the advantage that the message list is only written to from a single place — the subscriber. This means you don't need a concurrent data structure or additional synchronization to protect the list from simultaneous writes. The tradeoff is a slight delay before the sent message appears in the UI, as it must complete a round trip to the server first.

#### Deduplicate with optimistic UI <a id="deduplicate-optimistic-ui"/>

If your application adds the message to the UI immediately after calling `send()` for a more responsive experience, you need to add a safety check in the subscriber to avoid duplicates. Validate the incoming message `serial` and `version` against existing messages.

Because the message list is written to from two places — once after `send()` and again inside the subscriber — you must use a concurrent or thread-safe data structure to handle simultaneous writes safely:
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace em dashes in this new prose.

Line 239 and Line 245 use em dashes (), which conflicts with the docs writing rules.

Suggested patch
-This approach has the advantage that the message list is only written to from a single place — the subscriber.
+This approach has the advantage that the message list is only written to from a single place, the subscriber.

-Because the message list is written to from two places — once after `send()` and again inside the subscriber — you must use a concurrent or thread-safe data structure to handle simultaneous writes safely:
+Because the message list is written to from two places, once after `send()` and again inside the subscriber, you must use a concurrent or thread-safe data structure to handle simultaneous writes safely:

As per coding guidelines, "Avoid em-dashes, Latin abbreviations (use 'for example' not 'e.g.'), and slang in documentation writing."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
This approach has the advantage that the message list is only written to from a single place the subscriber. This means you don't need a concurrent data structure or additional synchronization to protect the list from simultaneous writes. The tradeoff is a slight delay before the sent message appears in the UI, as it must complete a round trip to the server first.
#### Deduplicate with optimistic UI <a id="deduplicate-optimistic-ui"/>
If your application adds the message to the UI immediately after calling `send()` for a more responsive experience, you need to add a safety check in the subscriber to avoid duplicates. Validate the incoming message `serial` and `version` against existing messages.
Because the message list is written to from two placesonce after `send()` and again inside the subscriber you must use a concurrent or thread-safe data structure to handle simultaneous writes safely:
This approach has the advantage that the message list is only written to from a single place, the subscriber. This means you don't need a concurrent data structure or additional synchronization to protect the list from simultaneous writes. The tradeoff is a slight delay before the sent message appears in the UI, as it must complete a round trip to the server first.
#### Deduplicate with optimistic UI <a id="deduplicate-optimistic-ui"/>
If your application adds the message to the UI immediately after calling `send()` for a more responsive experience, you need to add a safety check in the subscriber to avoid duplicates. Validate the incoming message `serial` and `version` against existing messages.
Because the message list is written to from two places, once after `send()` and again inside the subscriber, you must use a concurrent or thread-safe data structure to handle simultaneous writes safely:
🧰 Tools
🪛 LanguageTool

[style] ~239-~239: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...o from a single place — the subscriber. This means you don't need a concurrent data ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/docs/chat/rooms/messages.mdx` around lines 239 - 245, Two sentences
use em dashes (—) in the new prose, which violates doc rules; update the two
occurrences around the discussion of the subscriber and optimistic UI so they
don't use em dashes: replace the em‑dash between "the message list is only
written to from a single place — the subscriber" with a comma or parenthetical
("the subscriber") and replace the em‑dash in "Because the message list is
written to from two places — once after `send()` and again inside the
subscriber" with a comma or rephrase ("once after `send()` and again inside the
subscriber"); keep the references to `subscriber`, `send()`, `serial`, and
`version` unchanged.

@sacOO7 sacOO7 force-pushed the fix/chat-self-publish-message branch 3 times, most recently from b888e03 to d6272d8 Compare February 26, 2026 08:50
@sacOO7 sacOO7 force-pushed the fix/chat-self-publish-message branch from d6272d8 to 8d81ee8 Compare February 26, 2026 08:53

This approach has the advantage that the message list is only written to from a single place — the subscriber. This means you don't need a concurrent data structure or additional synchronization to protect the list from simultaneous writes. The tradeoff is that the sent message must complete a round trip to the server before appearing in the UI. While Ably's realtime delivery is always near-instantaneous, this may introduce a slight delay rarely in poor network conditions.

#### Deduplicate with optimistic UI <a id="deduplicate-optimistic-ui"/>
Copy link
Contributor Author

@sacOO7 sacOO7 Feb 26, 2026

Choose a reason for hiding this comment

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

If chat REST API starts respecting echoMessages=false as per https://ably.atlassian.net/browse/DX-456, section Deduplicate with optimistic UI will only have a config. echoMessages=false and a note to use concurrent or thread-safe data structure to handle simultaneous writes safely.

@sacOO7 sacOO7 requested a review from umair-ably February 26, 2026 09:05

#### Wait for the subscriber <a id="wait-for-subscriber"/>

The recommended approach is to not add the message to the UI immediately before/after calling `send()`. Instead, only append messages to the UI inside the `subscribe()` listener. Since the server echoes every sent message back to the sender as a subscriber event, the message will still appear in the UI when it arrives through the subscription. This eliminates the duplication problem entirely and requires no deduplication logic in the subscriber.
Copy link
Contributor

Choose a reason for hiding this comment

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

Users still have to contend with cross-region publishes, so they still need to handle ordering. They also need to apply updates/deletes, which means existence checks. At this point, the deduplication logic is essentially mostly implemented (and we recommend to use the .with() function to handle this in JS land).

I agree with handling concurrent writes to a data structure being a concern, but I don't think this means we should recommend in all cases for non-optimistic updates.

Copy link
Contributor

Choose a reason for hiding this comment

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

The cases where we recommend non-optimistic are updates/deletes and annotations.

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.

3 participants