diff --git a/docs.json b/docs.json index a525c7fae..741db403e 100644 --- a/docs.json +++ b/docs.json @@ -1856,7 +1856,6 @@ "pages": [ "ui-kit/android/guide-overview", "ui-kit/android/guide-threaded-messages", - "ui-kit/android/guide-thread-subscription", "ui-kit/android/guide-pin-and-save-messages", "ui-kit/android/guide-block-unblock-user", "ui-kit/android/guide-new-chat", @@ -6939,6 +6938,10 @@ } }, "redirects": [ + { + "source": "/ui-kit/android/guide-thread-subscription", + "destination": "/ui-kit/android/guide-threaded-messages#thread-subscription" + }, { "source": "/sdk/flutter/group-kick-member", "destination": "/sdk/flutter/group-kick-ban-members" diff --git a/ui-kit/android/core-features.mdx b/ui-kit/android/core-features.mdx index 3bf9d7ec0..1291e3e74 100644 --- a/ui-kit/android/core-features.mdx +++ b/ui-kit/android/core-features.mdx @@ -169,6 +169,7 @@ Rich Text Formatting allows users to style their messages with bold, italic, str | --- | --- | | [CometChatMessageComposer](/ui-kit/android/message-composer) | Provides a built-in rich text editor with formatting toolbar and text selection menu items for bold, italic, strikethrough, code, links, lists, blockquotes, and code blocks. | | [CometChatMessageList](/ui-kit/android/message-list) | Renders formatted messages with the appropriate styling automatically applied, ensuring that rich text formatting is displayed exactly as intended by the sender. | + ## Threaded Conversations Respond directly to a specific message, keeping conversations organized. @@ -183,16 +184,16 @@ Respond directly to a specific message, keeping conversations organized. | [CometChatMessageComposer](/ui-kit/android/message-composer) | Allows composing messages within a thread. | | [CometChatMessageList](/ui-kit/android/message-list) | Displays threaded messages in context. | -## Thread Subscription +### Thread Subscription -Let users subscribe to or unsubscribe from a thread to control whether its replies notify them. Opt-in feature — enable it with `UIKitSettings.setEnableThreadSubscription(true)`. +Let users subscribe to or unsubscribe from a thread to control whether its replies notify them. Enabled by default — remove a surface with `setThreadSubscriptionOptionVisibility(View.GONE)` or `setThreadSubscriptionVisibility(View.GONE)`. | Component | Role | | --- | --- | | [CometChatMessageList](/ui-kit/android/message-list) | Provides the Subscribe to thread / Unsubscribe from thread option in the message action sheet. | | [CometChatThreadHeader](/ui-kit/android/threaded-messages-header) | Shows the subscription bell on the thread view. | -See the [Thread Subscription guide](/ui-kit/android/guide-thread-subscription) for setup and behavior. +See [Threaded Messages → Thread Subscription](/ui-kit/android/guide-threaded-messages#thread-subscription) for setup and behavior. ## Quoted Replies diff --git a/ui-kit/android/events.mdx b/ui-kit/android/events.mdx index a4a18ea62..a08ad343d 100644 --- a/ui-kit/android/events.mdx +++ b/ui-kit/android/events.mdx @@ -190,7 +190,7 @@ lifecycleScope.launch { } ``` -See the [Thread Subscription guide](/ui-kit/android/guide-thread-subscription) for the feature end to end. +See [Threaded Messages → Thread Subscription](/ui-kit/android/guide-threaded-messages#thread-subscription) for the feature end to end. ### Call Events diff --git a/ui-kit/android/guide-thread-subscription.mdx b/ui-kit/android/guide-thread-subscription.mdx deleted file mode 100644 index 62f47b73f..000000000 --- a/ui-kit/android/guide-thread-subscription.mdx +++ /dev/null @@ -1,157 +0,0 @@ ---- -title: "Thread Subscription" -sidebarTitle: "Thread Subscription" -description: "Let users subscribe to or unsubscribe from message threads so notifications only reach the people who care." ---- - -## Overview - -Thread subscription gives users Slack-style control over thread noise: they can **subscribe** to a thread to be notified about its replies, or **unsubscribe** from one to mute it. Users are automatically subscribed when they start a thread, reply in one, or are @-mentioned in one — subscribing explicitly is how they opt in to a conversation they haven't participated in yet. - -The UI Kit ships two surfaces for the same toggle, kept in sync automatically: - -1. A **Subscribe to thread / Unsubscribe from thread** option in the message action sheet. -2. A **subscription bell** on the thread view. - -## Prerequisites - -- Threaded messages working in your app — see [Threaded Messages](/ui-kit/android/guide-threaded-messages). -- CometChat UI Kit for Android with Chat SDK v5 or later. - -## Enable the Feature - -Thread subscription is **off by default** and is enabled per app via `UIKitSettings` at init time. When the gate is off, neither surface renders and no subscription request is ever made. - - - -```kotlin lines -val uiKitSettings = UIKitSettings.UIKitSettingsBuilder() - .setAppId(APP_ID) - .setRegion(REGION) - .setAuthKey(AUTH_KEY) - .setEnableThreadSubscription(true) // opt in — default is false - .subscribePresenceForAllUsers() - .build() - -CometChatUIKit.init(this, uiKitSettings, object : CometChat.CallbackListener() { - override fun onSuccess(successString: String?) { } - override fun onError(e: CometChatException?) { } -}) -``` - - - -Anywhere you build your own UI around the feature, check the gate with: - -```kotlin lines -if (CometChatUIKit.isThreadSubscriptionEnabled()) { - // render your subscription control / entry point -} -``` - -## Surface 1: The Message Action Sheet Option - -With the gate on, [CometChatMessageList](/ui-kit/android/message-list) automatically adds a **Subscribe to thread** / **Unsubscribe from thread** option to the long-press action sheet. The label reflects the current state, and the option appears on regular messages of every type (agent messages and moderation-blocked messages are excluded) — on a thread reply it targets the thread's root message, so subscribing from anywhere in the thread works. - -To hide the option while keeping the rest of the feature: - - - -```kotlin lines -messageList.setThreadSubscriptionOptionVisibility(View.GONE) -``` - - - -## Surface 2: The Thread Header Bell - -[CometChatThreadHeader](/ui-kit/android/threaded-messages-header) renders a subscription bell as a trailing control on the reply-count bar. It flips optimistically on tap and reverts with a toast if the request fails. - - - -```kotlin lines -// Hide the bell (e.g. because you host your own — see below) -threadHeader.setThreadSubscriptionVisibility(View.GONE) - -// Observe state changes (isSubscribed = the new state) -threadHeader.setOnThreadSubscriptionChange { isSubscribed -> - Log.d(TAG, "Thread subscribed: $isSubscribed") -} -``` - -The visibility can also be set in XML with the `app:cometchatThreadSubscriptionVisibility` attribute. - - - -```kotlin lines -CometChatThreadHeader( - parentMessage = parentMessage, - hideThreadSubscription = false, // hide the built-in bell when true - isSubscribed = null, // null = seed from parentMessage.isThreadSubscribed() - onSubscriptionToggle = { isSubscribed -> - Log.d(TAG, "Thread subscribed: $isSubscribed") - }, - threadSubscriptionView = null // or your own composable replacing the bell -) -``` - - - -### Hosting the Bell in Your Own Top Bar - -Many apps (matching the CometChat sample apps and Figma) place the subscription bell in the thread screen's **top title bar** rather than the reply-count row. In Compose, the bell is available as a standalone public composable — hide the header's built-in one and host `ThreadSubscriptionBell` wherever you like: - - - -```kotlin lines -TopAppBar( - title = { Text(stringResource(R.string.thread)) }, - actions = { - if (CometChatUIKit.isThreadSubscriptionEnabled()) { - ThreadSubscriptionBell(parentMessage = parentMessage) - } - } -) - -CometChatThreadHeader( - parentMessage = parentMessage, - hideThreadSubscription = true // the bell lives in the top bar instead -) -``` - - -```kotlin lines -// Hide the kit header's bell and drive your own ImageView in the activity's title bar: -threadHeader.setThreadSubscriptionVisibility(View.GONE) - -// On tap: flip your icon optimistically, then call the SDK -CometChat.subscribeToThread(parentMessage.id, object : CometChat.CallbackListener() { - override fun onSuccess(response: String?) { } - override fun onError(e: CometChatException?) { - // revert the icon and show a toast - } -}) -``` - - - -## Behavior - -- **Optimistic with revert** — both surfaces flip instantly on tap, keep one request in flight per thread, and revert with a toast if the server rejects the change. An offline tap fails visibly and reverts; nothing is queued. -- **Auto-subscribe on reply** — sending a reply in a thread subscribes the user, and every surface flips to the subscribed state automatically. -- **Unsubscribing is not sticky** — replying again, or being @-mentioned, re-subscribes the user. -- **Unknown state renders as unsubscribed** — a message whose subscription state hasn't been learned yet (for example, one that just arrived in real time) shows the enabled subscribe control, never a spinner. - -## Cross-Surface Sync - -Both surfaces observe the UI Kit event bus, so toggling in one place updates the other without a refetch. If you build your own subscription control, emit and collect `CometChatThreadEvent` through `CometChatEvents.threadEvents` — see [Events](/ui-kit/android/events). - -## Notifications - -Whether a subscribed thread actually produces a push notification is governed by the user's notification preferences: the replies preference supports notifying only for **threads the user is subscribed to** (`SUBSCRIBE_TO_SUBSCRIBED_THREADS`). See [Thread Subscription (SDK)](/sdk/android/v5/thread-subscription#notification-preferences). - -## Next Steps & Further Reading - -- [Thread Subscription (SDK)](/sdk/android/v5/thread-subscription) — the underlying APIs, including fetching the threads a user participates in to build a thread inbox. -- [Threaded Messages Header](/ui-kit/android/threaded-messages-header) — the full component reference. -- [Message List](/ui-kit/android/message-list) — action-sheet options. diff --git a/ui-kit/android/guide-threaded-messages.mdx b/ui-kit/android/guide-threaded-messages.mdx index 900ed3bd7..b2e60268e 100644 --- a/ui-kit/android/guide-threaded-messages.mdx +++ b/ui-kit/android/guide-threaded-messages.mdx @@ -274,6 +274,114 @@ if (user.isBlockedByMe) { | Blocked User | Composer hidden; unblock layout shown. | | Not in Group | Show option to join group first. | +## Thread Subscription + +Thread subscription gives users Slack-style control over thread noise: they can **subscribe** to a thread to be notified about its replies, or **unsubscribe** from one to mute it. Users are automatically subscribed when they start a thread, reply in one, or are @-mentioned in one — subscribing explicitly is how they opt in to a conversation they haven't participated in yet. + +The UI Kit ships two surfaces for the same toggle, wired out of the box and kept in sync automatically: + +1. A **Subscribe to thread / Unsubscribe from thread** option in the message action sheet. +2. A **subscription bell** on the thread view. + +### The Message Action Sheet Option + +[CometChatMessageList](/ui-kit/android/message-list) adds a **Subscribe to thread** / **Unsubscribe from thread** option to the long-press action sheet. The label reflects the current state, and the option appears on regular messages of every type (agent messages and moderation-blocked messages are excluded) — on a thread reply it targets the thread's root message, so subscribing from anywhere in the thread works. + +To hide the option while keeping the rest of the feature: + + + +```kotlin lines +messageList.setThreadSubscriptionOptionVisibility(View.GONE) +``` + + + +### The Thread Header Bell + +[CometChatThreadHeader](/ui-kit/android/threaded-messages-header) renders a subscription bell as a trailing control on the reply-count bar. It flips optimistically on tap and reverts with a toast if the request fails. + + + +```kotlin lines +// Hide the bell (e.g. because you host your own — see below) +threadHeader.setThreadSubscriptionVisibility(View.GONE) + +// Observe state changes (isSubscribed = the new state) +threadHeader.setOnThreadSubscriptionChange { isSubscribed -> + Log.d(TAG, "Thread subscribed: $isSubscribed") +} +``` + +The visibility can also be set in XML with the `app:cometchatThreadSubscriptionVisibility` attribute. + + + +```kotlin lines +CometChatThreadHeader( + parentMessage = parentMessage, + hideThreadSubscription = false, // hide the built-in bell when true + isSubscribed = null, // null = seed from parentMessage.isThreadSubscribed() + onSubscriptionToggle = { isSubscribed -> + Log.d(TAG, "Thread subscribed: $isSubscribed") + }, + threadSubscriptionView = null // or your own composable replacing the bell +) +``` + + + +#### Hosting the Bell in Your Own Top Bar + +Many apps (matching the CometChat sample apps and Figma) place the subscription bell in the thread screen's **top title bar** rather than the reply-count row. In Compose, the bell is available as a standalone public composable — hide the header's built-in one and host `ThreadSubscriptionBell` wherever you like: + + + +```kotlin lines +TopAppBar( + title = { Text(stringResource(R.string.thread)) }, + actions = { + ThreadSubscriptionBell(parentMessage = parentMessage) + } +) + +CometChatThreadHeader( + parentMessage = parentMessage, + hideThreadSubscription = true // the bell lives in the top bar instead +) +``` + + +```kotlin lines +// Hide the kit header's bell and drive your own ImageView in the activity's title bar: +threadHeader.setThreadSubscriptionVisibility(View.GONE) + +// On tap: flip your icon optimistically, then call the SDK +CometChat.subscribeToThread(parentMessage.id, object : CometChat.CallbackListener() { + override fun onSuccess(response: String?) { } + override fun onError(e: CometChatException?) { + // revert the icon and show a toast + } +}) +``` + + + +### Subscription Behavior + +- **Optimistic with revert** — both surfaces flip instantly on tap, keep one request in flight per thread, and revert with a toast if the server rejects the change. An offline tap fails visibly and reverts; nothing is queued. +- **Auto-subscribe on reply** — sending a reply in a thread subscribes the user, and every surface flips to the subscribed state automatically. +- **Unsubscribing is not sticky** — replying again, or being @-mentioned, re-subscribes the user. +- **Unknown state renders as unsubscribed** — a message whose subscription state hasn't been learned yet (for example, one that just arrived in real time) shows the enabled subscribe control, never a spinner. + +### Cross-Surface Sync + +Both surfaces observe the UI Kit event bus, so toggling in one place updates the other without a refetch. If you build your own subscription control, emit and collect `CometChatThreadEvent` through `CometChatEvents.threadEvents` — see [Events](/ui-kit/android/events). + +### Notifications + +Whether a subscribed thread actually produces a push notification is governed by the user's notification preferences: the replies preference supports notifying only for **threads the user is subscribed to** (`SUBSCRIBE_TO_SUBSCRIBED_THREADS`). See [Thread Subscription (SDK)](/sdk/android/v5/thread-subscription#notification-preferences). + ## Summary / Feature Matrix | Feature | Component / Method | @@ -283,12 +391,13 @@ if (user.isBlockedByMe) { | Show parent message | `header.setParentMessage(parentMessage)` | | Compose reply | `composer.setParentMessageId(parentMessage.getId())` | | Handle blocked users | `isBlockedByMe()`, hide composer + show unblock UI | +| Subscribe / unsubscribe | Action-sheet option + `CometChatThreadHeader` bell | ## Next Steps & Further Reading - - Let users subscribe to or unsubscribe from a thread to control whether its replies notify them. + + The underlying APIs, including fetching the threads a user participates in to build a thread inbox. Explore this feature in the CometChat SampleApp: diff --git a/ui-kit/android/message-list.mdx b/ui-kit/android/message-list.mdx index 723f3ad1f..b567a3f08 100644 --- a/ui-kit/android/message-list.mdx +++ b/ui-kit/android/message-list.mdx @@ -850,7 +850,7 @@ Available visibility methods (Kotlin XML): | `setTranslateMessageOptionVisibility()` | `VISIBLE` | Translate message | | `setShareMessageOptionVisibility()` | `VISIBLE` | Share message | | `setMarkAsUnreadOptionVisibility()` | `GONE` | Mark as unread | -| `setThreadSubscriptionOptionVisibility()` | `VISIBLE`* | Subscribe / Unsubscribe thread option (*renders only when the thread-subscription feature gate is on) | +| `setThreadSubscriptionOptionVisibility()` | `VISIBLE` | Subscribe / Unsubscribe thread option | ### Feature Options (Pin, Save, Thread Subscription) @@ -858,9 +858,9 @@ Three groups of options appear automatically when their feature is enabled for t - **Pin message / Unpin message** — shown on text and media messages when `CometChatUIKit.isPinMessageEnabled()`. The option is shown to **every** participant: permission is enforced by the server, and a user who lacks it gets a "you don't have permission" toast (`ERR_PERMISSION_DENIED`) rather than a hidden option. Pinning applies immediately with a toast; unpinning asks for confirmation first. Pinned messages get a pin indicator in the bubble footer. - **Save message / Unsave message** — shown on text and media messages when `CometChatUIKit.isSaveMessageEnabled()`, for every user. Saving applies immediately with a toast; unsaving asks for confirmation first. Saved messages get a bookmark indicator in the bubble footer. -- **Subscribe to thread / Unsubscribe from thread** — shown on regular messages (not agent or moderation-blocked ones) when thread subscription is enabled via `UIKitSettings.setEnableThreadSubscription(true)`. On a thread reply the action targets the thread's root message. Hide it with `setThreadSubscriptionOptionVisibility(View.GONE)`. +- **Subscribe to thread / Unsubscribe from thread** — shown on regular messages (not agent or moderation-blocked ones). On a thread reply the action targets the thread's root message. Hide it with `setThreadSubscriptionOptionVisibility(View.GONE)`. -Pin and Save are withheld on messages where the action is meaningless or would fail — deleted messages, messages that have not finished sending, and messages held or rejected by moderation. The labels toggle with the message's current state, and if a pin/save limit is exceeded the limit toast is generated from the server response automatically. See the [Pin & Save Messages](/ui-kit/android/guide-pin-and-save-messages) and [Thread Subscription](/ui-kit/android/guide-thread-subscription) guides. +Pin and Save are withheld on messages where the action is meaningless or would fail — deleted messages, messages that have not finished sending, and messages held or rejected by moderation. The labels toggle with the message's current state, and if a pin/save limit is exceeded the limit toast is generated from the server response automatically. See the [Pin & Save Messages](/ui-kit/android/guide-pin-and-save-messages) guide and [Threaded Messages → Thread Subscription](/ui-kit/android/guide-threaded-messages#thread-subscription). ### Replacing All Options (`setOptions`) diff --git a/ui-kit/android/methods.mdx b/ui-kit/android/methods.mdx index 963704528..f8da9c17f 100644 --- a/ui-kit/android/methods.mdx +++ b/ui-kit/android/methods.mdx @@ -74,7 +74,6 @@ The `UIKitSettings` is an important parameter of the `init()` function. It serve | **setAIFeatures** | `List` | Sets the AI Features that need to be added in UI Kit | | **setExtensions** | `List` | Sets the list of extension that need to be added in UI Kit | | **dateTimeFormatterCallback** | `DateTimeFormatterCallback` | Interface containing callback methods to format different types of timestamps. | -| **setEnableThreadSubscription** | `Boolean` | Opt in to the thread subscription feature. Default `false` — no subscription controls render without it. See [Thread Subscription](/ui-kit/android/guide-thread-subscription) | **Usage:** @@ -427,7 +426,6 @@ Synchronous, UI-safe checks for whether a feature is available. Use them to gate | `CometChatUIKit.isPinMessageEnabled()` | Whether the Pin Message feature is enabled for the app. | | `CometChatUIKit.isSaveMessageEnabled()` | Whether the Save Message feature is enabled for the app. | | `CometChatUIKit.isPinConversationEnabled()` | Whether the Pin Conversation feature is enabled for the app. | -| `CometChatUIKit.isThreadSubscriptionEnabled()` | Whether thread subscription was opted into via `UIKitSettings.setEnableThreadSubscription(true)`. | ```kotlin if (CometChatUIKit.isPinMessageEnabled()) { diff --git a/ui-kit/android/threaded-messages-header.mdx b/ui-kit/android/threaded-messages-header.mdx index cc9db9a38..affee6427 100644 --- a/ui-kit/android/threaded-messages-header.mdx +++ b/ui-kit/android/threaded-messages-header.mdx @@ -97,7 +97,7 @@ Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()`, a user lo #### Subscription Bell (`onThreadSubscriptionChange` / `onSubscriptionToggle`) -When [thread subscription](/ui-kit/android/guide-thread-subscription) is enabled (`UIKitSettings.setEnableThreadSubscription(true)`), the header renders a subscription bell as a trailing control on the reply-count bar. It flips optimistically on tap, reverts with a toast on failure, and stays in sync with the message list's Subscribe/Unsubscribe option automatically. +The header renders a [thread subscription](/ui-kit/android/guide-threaded-messages#thread-subscription) bell as a trailing control on the reply-count bar. It flips optimistically on tap, reverts with a toast on failure, and stays in sync with the message list's Subscribe/Unsubscribe option automatically. @@ -155,7 +155,7 @@ The component listens to SDK events internally via its ViewModel. No manual setu | `setAvatarVisibility(View.GONE)` | `hideAvatar = true` | Toggle avatar visibility | | `setReceiptsVisibility(View.GONE)` | `hideReceipts = true` | Toggle read receipts | | `setReplyCountVisibility(View.GONE)` | `hideReplyCount = true` | Toggle reply count text | -| `setThreadSubscriptionVisibility(View.GONE)` | `hideThreadSubscription = true` | Toggle the subscription bell (renders only when thread subscription is enabled) | +| `setThreadSubscriptionVisibility(View.GONE)` | `hideThreadSubscription = true` | Toggle the subscription bell | | `setOnThreadSubscriptionChange { }` | `onSubscriptionToggle = { }` | Subscription-state change callback | | — | `threadSubscriptionView = { }` | Replace the bell with a custom composable | diff --git a/ui-kit/react/core-features.mdx b/ui-kit/react/core-features.mdx index e9d56d92a..afec81f80 100644 --- a/ui-kit/react/core-features.mdx +++ b/ui-kit/react/core-features.mdx @@ -152,6 +152,17 @@ The Threaded Conversations feature enables users to respond directly to a specif | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | [Threaded Message Preview](/ui-kit/react/guide-threaded-messages) | [Threaded Message Preview](/ui-kit/react/guide-threaded-messages) component displays the parent message along with the number of replies. | +### Thread Subscription + +Let users subscribe to or unsubscribe from a thread to control whether its replies notify them. Enabled by default — remove a surface with `hideThreadSubscriptionToggle` or `hideThreadSubscriptionOption`. + +| Component | Role | +| --- | --- | +| [Message List](/ui-kit/react/components/message-list#hidethreadsubscriptionoption) | Provides the Subscribe to thread / Unsubscribe from thread option in the message context menu. | +| [Thread Header](/ui-kit/react/components/thread-header#thread-subscription) | Shows the subscription bell on the thread view. | + +See [Threaded Messages → Thread Subscription](/ui-kit/react/guide-threaded-messages#thread-subscription) for setup and behavior. + ## Quoted Replies Quoted Replies is a robust feature provided by CometChat that enables users to quickly reply to specific messages by selecting the "Reply" option from a message's action menu. This enhances context, keeps conversations organized, and improves overall chat experience in both 1-1 and group chats.